* [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-06-30 3:08 ` Jia Hongtao
0 siblings, 0 replies; 18+ messages in thread
From: Jia Hongtao @ 2016-06-30 3:08 UTC (permalink / raw)
To: edubezval, rui.zhang, robh+dt, galak, scott.wood, shawnguo
Cc: linux-pm, devicetree, linux-kernel, linuxppc-dev,
linux-arm-kernel, hongtao.jia
This driver add thermal management support by enabling TMU (Thermal
Monitoring Unit) on QorIQ platform.
It's based on thermal of framework:
- Trip points defined in device tree.
- Cpufreq as cooling device registered in qoriq cpufreq driver.
Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
---
Changes of V2:
* Add HAS_IOMEM dependency to fix build error on UM
drivers/thermal/Kconfig | 10 ++
drivers/thermal/Makefile | 1 +
drivers/thermal/qoriq_thermal.c | 328 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 339 insertions(+)
create mode 100644 drivers/thermal/qoriq_thermal.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 2d702ca..56ef30d 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -195,6 +195,16 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
+config QORIQ_THERMAL
+ tristate "QorIQ Thermal Monitoring Unit"
+ depends on THERMAL_OF
+ depends on HAS_IOMEM
+ help
+ Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
+ It supports one critical trip point and one passive trip point. The
+ cpufreq is used as the cooling device to throttle CPUs when the
+ passive trip is crossed.
+
config SPEAR_THERMAL
tristate "SPEAr thermal sensor driver"
depends on PLAT_SPEAR || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 10b07c1..6662232 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
+obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
new file mode 100644
index 0000000..644ba52
--- /dev/null
+++ b/drivers/thermal/qoriq_thermal.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define SITES_MAX 16
+
+/*
+ * QorIQ TMU Registers
+ */
+struct qoriq_tmu_site_regs {
+ u32 tritsr; /* Immediate Temperature Site Register */
+ u32 tratsr; /* Average Temperature Site Register */
+ u8 res0[0x8];
+};
+
+struct qoriq_tmu_regs {
+ u32 tmr; /* Mode Register */
+#define TMR_DISABLE 0x0
+#define TMR_ME 0x80000000
+#define TMR_ALPF 0x0c000000
+ u32 tsr; /* Status Register */
+ u32 tmtmir; /* Temperature measurement interval Register */
+#define TMTMIR_DEFAULT 0x0000000f
+ u8 res0[0x14];
+ u32 tier; /* Interrupt Enable Register */
+#define TIER_DISABLE 0x0
+ u32 tidr; /* Interrupt Detect Register */
+ u32 tiscr; /* Interrupt Site Capture Register */
+ u32 ticscr; /* Interrupt Critical Site Capture Register */
+ u8 res1[0x10];
+ u32 tmhtcrh; /* High Temperature Capture Register */
+ u32 tmhtcrl; /* Low Temperature Capture Register */
+ u8 res2[0x8];
+ u32 tmhtitr; /* High Temperature Immediate Threshold */
+ u32 tmhtatr; /* High Temperature Average Threshold */
+ u32 tmhtactr; /* High Temperature Average Crit Threshold */
+ u8 res3[0x24];
+ u32 ttcfgr; /* Temperature Configuration Register */
+ u32 tscfgr; /* Sensor Configuration Register */
+ u8 res4[0x78];
+ struct qoriq_tmu_site_regs site[SITES_MAX];
+ u8 res5[0x9f8];
+ u32 ipbrr0; /* IP Block Revision Register 0 */
+ u32 ipbrr1; /* IP Block Revision Register 1 */
+ u8 res6[0x310];
+ u32 ttr0cr; /* Temperature Range 0 Control Register */
+ u32 ttr1cr; /* Temperature Range 1 Control Register */
+ u32 ttr2cr; /* Temperature Range 2 Control Register */
+ u32 ttr3cr; /* Temperature Range 3 Control Register */
+};
+
+/*
+ * Thermal zone data
+ */
+struct qoriq_tmu_data {
+ struct thermal_zone_device *tz;
+ struct qoriq_tmu_regs __iomem *regs;
+ int sensor_id;
+ bool little_endian;
+};
+
+static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
+{
+ if (p->little_endian)
+ iowrite32(val, addr);
+ else
+ iowrite32be(val, addr);
+}
+
+static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
+{
+ if (p->little_endian)
+ return ioread32(addr);
+ else
+ return ioread32be(addr);
+}
+
+static int tmu_get_temp(void *p, int *temp)
+{
+ u32 val;
+ struct qoriq_tmu_data *data = p;
+
+ val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
+ *temp = (val & 0xff) * 1000;
+
+ return 0;
+}
+
+static int qoriq_tmu_get_sensor_id(void)
+{
+ int ret, id;
+ struct of_phandle_args sensor_specs;
+ struct device_node *np, *sensor_np;
+
+ np = of_find_node_by_name(NULL, "thermal-zones");
+ if (!np)
+ return -ENODEV;
+
+ sensor_np = of_get_next_child(np, NULL);
+ ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
+ "#thermal-sensor-cells",
+ 0, &sensor_specs);
+ if (ret) {
+ of_node_put(np);
+ of_node_put(sensor_np);
+ return ret;
+ }
+
+ if (sensor_specs.args_count >= 1) {
+ id = sensor_specs.args[0];
+ WARN(sensor_specs.args_count > 1,
+ "%s: too many cells in sensor specifier %d\n",
+ sensor_specs.np->name, sensor_specs.args_count);
+ } else {
+ id = 0;
+ }
+
+ of_node_put(np);
+ of_node_put(sensor_np);
+
+ return id;
+}
+
+static int qoriq_tmu_calibration(struct platform_device *pdev)
+{
+ int i, val, len;
+ u32 range[4];
+ const u32 *calibration;
+ struct device_node *np = pdev->dev.of_node;
+ struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+ if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
+ dev_err(&pdev->dev, "missing calibration range.\n");
+ return -ENODEV;
+ }
+
+ /* Init temperature range registers */
+ tmu_write(data, range[0], &data->regs->ttr0cr);
+ tmu_write(data, range[1], &data->regs->ttr1cr);
+ tmu_write(data, range[2], &data->regs->ttr2cr);
+ tmu_write(data, range[3], &data->regs->ttr3cr);
+
+ calibration = of_get_property(np, "fsl,tmu-calibration", &len);
+ if (calibration == NULL || len % 8) {
+ dev_err(&pdev->dev, "invalid calibration data.\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < len; i += 8, calibration += 2) {
+ val = of_read_number(calibration, 1);
+ tmu_write(data, val, &data->regs->ttcfgr);
+ val = of_read_number(calibration + 1, 1);
+ tmu_write(data, val, &data->regs->tscfgr);
+ }
+
+ return 0;
+}
+
+static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
+{
+ /* Disable interrupt, using polling instead */
+ tmu_write(data, TIER_DISABLE, &data->regs->tier);
+
+ /* Set update_interval */
+ tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
+
+ /* Disable monitoring */
+ tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+}
+
+static struct thermal_zone_of_device_ops tmu_tz_ops = {
+ .get_temp = tmu_get_temp,
+};
+
+static int qoriq_tmu_probe(struct platform_device *pdev)
+{
+ int ret;
+ const struct thermal_trip *trip;
+ struct qoriq_tmu_data *data;
+ struct device_node *np = pdev->dev.of_node;
+ u32 site = 0;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Device OF-Node is NULL");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+
+ data->little_endian = of_property_read_bool(np, "little-endian");
+
+ data->sensor_id = qoriq_tmu_get_sensor_id();
+ if (data->sensor_id < 0) {
+ dev_err(&pdev->dev, "Failed to get sensor id\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ data->regs = of_iomap(np, 0);
+ if (!data->regs) {
+ dev_err(&pdev->dev, "Failed to get memory region\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ qoriq_tmu_init_device(data); /* TMU initialization */
+
+ ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
+ if (ret < 0)
+ goto err_tmu;
+
+ data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
+ data, &tmu_tz_ops);
+ if (IS_ERR(data->tz)) {
+ ret = PTR_ERR(data->tz);
+ dev_err(&pdev->dev,
+ "Failed to register thermal zone device %d\n", ret);
+ goto err_tmu;
+ }
+
+ trip = of_thermal_get_trip_points(data->tz);
+
+ /* Enable monitoring */
+ site |= 0x1 << (15 - data->sensor_id);
+ tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
+
+ return 0;
+
+err_tmu:
+ iounmap(data->regs);
+
+err_iomap:
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int qoriq_tmu_remove(struct platform_device *pdev)
+{
+ struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+ thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
+
+ /* Disable monitoring */
+ tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+
+ iounmap(data->regs);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qoriq_tmu_suspend(struct device *dev)
+{
+ u32 tmr;
+ struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+ /* Disable monitoring */
+ tmr = tmu_read(data, &data->regs->tmr);
+ tmr &= ~TMR_ME;
+ tmu_write(data, tmr, &data->regs->tmr);
+
+ return 0;
+}
+
+static int qoriq_tmu_resume(struct device *dev)
+{
+ u32 tmr;
+ struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+ /* Enable monitoring */
+ tmr = tmu_read(data, &data->regs->tmr);
+ tmr |= TMR_ME;
+ tmu_write(data, tmr, &data->regs->tmr);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
+ qoriq_tmu_suspend, qoriq_tmu_resume);
+
+static const struct of_device_id qoriq_tmu_match[] = {
+ { .compatible = "fsl,qoriq-tmu", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
+
+static struct platform_driver qoriq_tmu = {
+ .driver = {
+ .name = "qoriq_thermal",
+ .pm = &qoriq_tmu_pm_ops,
+ .of_match_table = qoriq_tmu_match,
+ },
+ .probe = qoriq_tmu_probe,
+ .remove = qoriq_tmu_remove,
+};
+module_platform_driver(qoriq_tmu);
+
+MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
+MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
+MODULE_LICENSE("GPL v2");
--
2.1.0.27.g96db324
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-06-30 3:08 ` Jia Hongtao
0 siblings, 0 replies; 18+ messages in thread
From: Jia Hongtao @ 2016-06-30 3:08 UTC (permalink / raw)
To: edubezval, rui.zhang, robh+dt, galak, scott.wood, shawnguo
Cc: devicetree, linux-pm, linux-kernel, hongtao.jia, linuxppc-dev,
linux-arm-kernel
This driver add thermal management support by enabling TMU (Thermal
Monitoring Unit) on QorIQ platform.
It's based on thermal of framework:
- Trip points defined in device tree.
- Cpufreq as cooling device registered in qoriq cpufreq driver.
Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
---
Changes of V2:
* Add HAS_IOMEM dependency to fix build error on UM
drivers/thermal/Kconfig | 10 ++
drivers/thermal/Makefile | 1 +
drivers/thermal/qoriq_thermal.c | 328 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 339 insertions(+)
create mode 100644 drivers/thermal/qoriq_thermal.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 2d702ca..56ef30d 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -195,6 +195,16 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
+config QORIQ_THERMAL
+ tristate "QorIQ Thermal Monitoring Unit"
+ depends on THERMAL_OF
+ depends on HAS_IOMEM
+ help
+ Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
+ It supports one critical trip point and one passive trip point. The
+ cpufreq is used as the cooling device to throttle CPUs when the
+ passive trip is crossed.
+
config SPEAR_THERMAL
tristate "SPEAr thermal sensor driver"
depends on PLAT_SPEAR || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 10b07c1..6662232 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
+obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
new file mode 100644
index 0000000..644ba52
--- /dev/null
+++ b/drivers/thermal/qoriq_thermal.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define SITES_MAX 16
+
+/*
+ * QorIQ TMU Registers
+ */
+struct qoriq_tmu_site_regs {
+ u32 tritsr; /* Immediate Temperature Site Register */
+ u32 tratsr; /* Average Temperature Site Register */
+ u8 res0[0x8];
+};
+
+struct qoriq_tmu_regs {
+ u32 tmr; /* Mode Register */
+#define TMR_DISABLE 0x0
+#define TMR_ME 0x80000000
+#define TMR_ALPF 0x0c000000
+ u32 tsr; /* Status Register */
+ u32 tmtmir; /* Temperature measurement interval Register */
+#define TMTMIR_DEFAULT 0x0000000f
+ u8 res0[0x14];
+ u32 tier; /* Interrupt Enable Register */
+#define TIER_DISABLE 0x0
+ u32 tidr; /* Interrupt Detect Register */
+ u32 tiscr; /* Interrupt Site Capture Register */
+ u32 ticscr; /* Interrupt Critical Site Capture Register */
+ u8 res1[0x10];
+ u32 tmhtcrh; /* High Temperature Capture Register */
+ u32 tmhtcrl; /* Low Temperature Capture Register */
+ u8 res2[0x8];
+ u32 tmhtitr; /* High Temperature Immediate Threshold */
+ u32 tmhtatr; /* High Temperature Average Threshold */
+ u32 tmhtactr; /* High Temperature Average Crit Threshold */
+ u8 res3[0x24];
+ u32 ttcfgr; /* Temperature Configuration Register */
+ u32 tscfgr; /* Sensor Configuration Register */
+ u8 res4[0x78];
+ struct qoriq_tmu_site_regs site[SITES_MAX];
+ u8 res5[0x9f8];
+ u32 ipbrr0; /* IP Block Revision Register 0 */
+ u32 ipbrr1; /* IP Block Revision Register 1 */
+ u8 res6[0x310];
+ u32 ttr0cr; /* Temperature Range 0 Control Register */
+ u32 ttr1cr; /* Temperature Range 1 Control Register */
+ u32 ttr2cr; /* Temperature Range 2 Control Register */
+ u32 ttr3cr; /* Temperature Range 3 Control Register */
+};
+
+/*
+ * Thermal zone data
+ */
+struct qoriq_tmu_data {
+ struct thermal_zone_device *tz;
+ struct qoriq_tmu_regs __iomem *regs;
+ int sensor_id;
+ bool little_endian;
+};
+
+static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
+{
+ if (p->little_endian)
+ iowrite32(val, addr);
+ else
+ iowrite32be(val, addr);
+}
+
+static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
+{
+ if (p->little_endian)
+ return ioread32(addr);
+ else
+ return ioread32be(addr);
+}
+
+static int tmu_get_temp(void *p, int *temp)
+{
+ u32 val;
+ struct qoriq_tmu_data *data = p;
+
+ val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
+ *temp = (val & 0xff) * 1000;
+
+ return 0;
+}
+
+static int qoriq_tmu_get_sensor_id(void)
+{
+ int ret, id;
+ struct of_phandle_args sensor_specs;
+ struct device_node *np, *sensor_np;
+
+ np = of_find_node_by_name(NULL, "thermal-zones");
+ if (!np)
+ return -ENODEV;
+
+ sensor_np = of_get_next_child(np, NULL);
+ ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
+ "#thermal-sensor-cells",
+ 0, &sensor_specs);
+ if (ret) {
+ of_node_put(np);
+ of_node_put(sensor_np);
+ return ret;
+ }
+
+ if (sensor_specs.args_count >= 1) {
+ id = sensor_specs.args[0];
+ WARN(sensor_specs.args_count > 1,
+ "%s: too many cells in sensor specifier %d\n",
+ sensor_specs.np->name, sensor_specs.args_count);
+ } else {
+ id = 0;
+ }
+
+ of_node_put(np);
+ of_node_put(sensor_np);
+
+ return id;
+}
+
+static int qoriq_tmu_calibration(struct platform_device *pdev)
+{
+ int i, val, len;
+ u32 range[4];
+ const u32 *calibration;
+ struct device_node *np = pdev->dev.of_node;
+ struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+ if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
+ dev_err(&pdev->dev, "missing calibration range.\n");
+ return -ENODEV;
+ }
+
+ /* Init temperature range registers */
+ tmu_write(data, range[0], &data->regs->ttr0cr);
+ tmu_write(data, range[1], &data->regs->ttr1cr);
+ tmu_write(data, range[2], &data->regs->ttr2cr);
+ tmu_write(data, range[3], &data->regs->ttr3cr);
+
+ calibration = of_get_property(np, "fsl,tmu-calibration", &len);
+ if (calibration == NULL || len % 8) {
+ dev_err(&pdev->dev, "invalid calibration data.\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < len; i += 8, calibration += 2) {
+ val = of_read_number(calibration, 1);
+ tmu_write(data, val, &data->regs->ttcfgr);
+ val = of_read_number(calibration + 1, 1);
+ tmu_write(data, val, &data->regs->tscfgr);
+ }
+
+ return 0;
+}
+
+static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
+{
+ /* Disable interrupt, using polling instead */
+ tmu_write(data, TIER_DISABLE, &data->regs->tier);
+
+ /* Set update_interval */
+ tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
+
+ /* Disable monitoring */
+ tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+}
+
+static struct thermal_zone_of_device_ops tmu_tz_ops = {
+ .get_temp = tmu_get_temp,
+};
+
+static int qoriq_tmu_probe(struct platform_device *pdev)
+{
+ int ret;
+ const struct thermal_trip *trip;
+ struct qoriq_tmu_data *data;
+ struct device_node *np = pdev->dev.of_node;
+ u32 site = 0;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Device OF-Node is NULL");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+
+ data->little_endian = of_property_read_bool(np, "little-endian");
+
+ data->sensor_id = qoriq_tmu_get_sensor_id();
+ if (data->sensor_id < 0) {
+ dev_err(&pdev->dev, "Failed to get sensor id\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ data->regs = of_iomap(np, 0);
+ if (!data->regs) {
+ dev_err(&pdev->dev, "Failed to get memory region\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ qoriq_tmu_init_device(data); /* TMU initialization */
+
+ ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
+ if (ret < 0)
+ goto err_tmu;
+
+ data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
+ data, &tmu_tz_ops);
+ if (IS_ERR(data->tz)) {
+ ret = PTR_ERR(data->tz);
+ dev_err(&pdev->dev,
+ "Failed to register thermal zone device %d\n", ret);
+ goto err_tmu;
+ }
+
+ trip = of_thermal_get_trip_points(data->tz);
+
+ /* Enable monitoring */
+ site |= 0x1 << (15 - data->sensor_id);
+ tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
+
+ return 0;
+
+err_tmu:
+ iounmap(data->regs);
+
+err_iomap:
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int qoriq_tmu_remove(struct platform_device *pdev)
+{
+ struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+ thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
+
+ /* Disable monitoring */
+ tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+
+ iounmap(data->regs);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qoriq_tmu_suspend(struct device *dev)
+{
+ u32 tmr;
+ struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+ /* Disable monitoring */
+ tmr = tmu_read(data, &data->regs->tmr);
+ tmr &= ~TMR_ME;
+ tmu_write(data, tmr, &data->regs->tmr);
+
+ return 0;
+}
+
+static int qoriq_tmu_resume(struct device *dev)
+{
+ u32 tmr;
+ struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+ /* Enable monitoring */
+ tmr = tmu_read(data, &data->regs->tmr);
+ tmr |= TMR_ME;
+ tmu_write(data, tmr, &data->regs->tmr);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
+ qoriq_tmu_suspend, qoriq_tmu_resume);
+
+static const struct of_device_id qoriq_tmu_match[] = {
+ { .compatible = "fsl,qoriq-tmu", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
+
+static struct platform_driver qoriq_tmu = {
+ .driver = {
+ .name = "qoriq_thermal",
+ .pm = &qoriq_tmu_pm_ops,
+ .of_match_table = qoriq_tmu_match,
+ },
+ .probe = qoriq_tmu_probe,
+ .remove = qoriq_tmu_remove,
+};
+module_platform_driver(qoriq_tmu);
+
+MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
+MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
+MODULE_LICENSE("GPL v2");
--
2.1.0.27.g96db324
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-06-30 3:08 ` Jia Hongtao
0 siblings, 0 replies; 18+ messages in thread
From: Jia Hongtao @ 2016-06-30 3:08 UTC (permalink / raw)
To: linux-arm-kernel
This driver add thermal management support by enabling TMU (Thermal
Monitoring Unit) on QorIQ platform.
It's based on thermal of framework:
- Trip points defined in device tree.
- Cpufreq as cooling device registered in qoriq cpufreq driver.
Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
---
Changes of V2:
* Add HAS_IOMEM dependency to fix build error on UM
drivers/thermal/Kconfig | 10 ++
drivers/thermal/Makefile | 1 +
drivers/thermal/qoriq_thermal.c | 328 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 339 insertions(+)
create mode 100644 drivers/thermal/qoriq_thermal.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 2d702ca..56ef30d 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -195,6 +195,16 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
+config QORIQ_THERMAL
+ tristate "QorIQ Thermal Monitoring Unit"
+ depends on THERMAL_OF
+ depends on HAS_IOMEM
+ help
+ Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
+ It supports one critical trip point and one passive trip point. The
+ cpufreq is used as the cooling device to throttle CPUs when the
+ passive trip is crossed.
+
config SPEAR_THERMAL
tristate "SPEAr thermal sensor driver"
depends on PLAT_SPEAR || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 10b07c1..6662232 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
+obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
new file mode 100644
index 0000000..644ba52
--- /dev/null
+++ b/drivers/thermal/qoriq_thermal.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define SITES_MAX 16
+
+/*
+ * QorIQ TMU Registers
+ */
+struct qoriq_tmu_site_regs {
+ u32 tritsr; /* Immediate Temperature Site Register */
+ u32 tratsr; /* Average Temperature Site Register */
+ u8 res0[0x8];
+};
+
+struct qoriq_tmu_regs {
+ u32 tmr; /* Mode Register */
+#define TMR_DISABLE 0x0
+#define TMR_ME 0x80000000
+#define TMR_ALPF 0x0c000000
+ u32 tsr; /* Status Register */
+ u32 tmtmir; /* Temperature measurement interval Register */
+#define TMTMIR_DEFAULT 0x0000000f
+ u8 res0[0x14];
+ u32 tier; /* Interrupt Enable Register */
+#define TIER_DISABLE 0x0
+ u32 tidr; /* Interrupt Detect Register */
+ u32 tiscr; /* Interrupt Site Capture Register */
+ u32 ticscr; /* Interrupt Critical Site Capture Register */
+ u8 res1[0x10];
+ u32 tmhtcrh; /* High Temperature Capture Register */
+ u32 tmhtcrl; /* Low Temperature Capture Register */
+ u8 res2[0x8];
+ u32 tmhtitr; /* High Temperature Immediate Threshold */
+ u32 tmhtatr; /* High Temperature Average Threshold */
+ u32 tmhtactr; /* High Temperature Average Crit Threshold */
+ u8 res3[0x24];
+ u32 ttcfgr; /* Temperature Configuration Register */
+ u32 tscfgr; /* Sensor Configuration Register */
+ u8 res4[0x78];
+ struct qoriq_tmu_site_regs site[SITES_MAX];
+ u8 res5[0x9f8];
+ u32 ipbrr0; /* IP Block Revision Register 0 */
+ u32 ipbrr1; /* IP Block Revision Register 1 */
+ u8 res6[0x310];
+ u32 ttr0cr; /* Temperature Range 0 Control Register */
+ u32 ttr1cr; /* Temperature Range 1 Control Register */
+ u32 ttr2cr; /* Temperature Range 2 Control Register */
+ u32 ttr3cr; /* Temperature Range 3 Control Register */
+};
+
+/*
+ * Thermal zone data
+ */
+struct qoriq_tmu_data {
+ struct thermal_zone_device *tz;
+ struct qoriq_tmu_regs __iomem *regs;
+ int sensor_id;
+ bool little_endian;
+};
+
+static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
+{
+ if (p->little_endian)
+ iowrite32(val, addr);
+ else
+ iowrite32be(val, addr);
+}
+
+static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
+{
+ if (p->little_endian)
+ return ioread32(addr);
+ else
+ return ioread32be(addr);
+}
+
+static int tmu_get_temp(void *p, int *temp)
+{
+ u32 val;
+ struct qoriq_tmu_data *data = p;
+
+ val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
+ *temp = (val & 0xff) * 1000;
+
+ return 0;
+}
+
+static int qoriq_tmu_get_sensor_id(void)
+{
+ int ret, id;
+ struct of_phandle_args sensor_specs;
+ struct device_node *np, *sensor_np;
+
+ np = of_find_node_by_name(NULL, "thermal-zones");
+ if (!np)
+ return -ENODEV;
+
+ sensor_np = of_get_next_child(np, NULL);
+ ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
+ "#thermal-sensor-cells",
+ 0, &sensor_specs);
+ if (ret) {
+ of_node_put(np);
+ of_node_put(sensor_np);
+ return ret;
+ }
+
+ if (sensor_specs.args_count >= 1) {
+ id = sensor_specs.args[0];
+ WARN(sensor_specs.args_count > 1,
+ "%s: too many cells in sensor specifier %d\n",
+ sensor_specs.np->name, sensor_specs.args_count);
+ } else {
+ id = 0;
+ }
+
+ of_node_put(np);
+ of_node_put(sensor_np);
+
+ return id;
+}
+
+static int qoriq_tmu_calibration(struct platform_device *pdev)
+{
+ int i, val, len;
+ u32 range[4];
+ const u32 *calibration;
+ struct device_node *np = pdev->dev.of_node;
+ struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+ if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
+ dev_err(&pdev->dev, "missing calibration range.\n");
+ return -ENODEV;
+ }
+
+ /* Init temperature range registers */
+ tmu_write(data, range[0], &data->regs->ttr0cr);
+ tmu_write(data, range[1], &data->regs->ttr1cr);
+ tmu_write(data, range[2], &data->regs->ttr2cr);
+ tmu_write(data, range[3], &data->regs->ttr3cr);
+
+ calibration = of_get_property(np, "fsl,tmu-calibration", &len);
+ if (calibration == NULL || len % 8) {
+ dev_err(&pdev->dev, "invalid calibration data.\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < len; i += 8, calibration += 2) {
+ val = of_read_number(calibration, 1);
+ tmu_write(data, val, &data->regs->ttcfgr);
+ val = of_read_number(calibration + 1, 1);
+ tmu_write(data, val, &data->regs->tscfgr);
+ }
+
+ return 0;
+}
+
+static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
+{
+ /* Disable interrupt, using polling instead */
+ tmu_write(data, TIER_DISABLE, &data->regs->tier);
+
+ /* Set update_interval */
+ tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
+
+ /* Disable monitoring */
+ tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+}
+
+static struct thermal_zone_of_device_ops tmu_tz_ops = {
+ .get_temp = tmu_get_temp,
+};
+
+static int qoriq_tmu_probe(struct platform_device *pdev)
+{
+ int ret;
+ const struct thermal_trip *trip;
+ struct qoriq_tmu_data *data;
+ struct device_node *np = pdev->dev.of_node;
+ u32 site = 0;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Device OF-Node is NULL");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+
+ data->little_endian = of_property_read_bool(np, "little-endian");
+
+ data->sensor_id = qoriq_tmu_get_sensor_id();
+ if (data->sensor_id < 0) {
+ dev_err(&pdev->dev, "Failed to get sensor id\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ data->regs = of_iomap(np, 0);
+ if (!data->regs) {
+ dev_err(&pdev->dev, "Failed to get memory region\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ qoriq_tmu_init_device(data); /* TMU initialization */
+
+ ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
+ if (ret < 0)
+ goto err_tmu;
+
+ data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
+ data, &tmu_tz_ops);
+ if (IS_ERR(data->tz)) {
+ ret = PTR_ERR(data->tz);
+ dev_err(&pdev->dev,
+ "Failed to register thermal zone device %d\n", ret);
+ goto err_tmu;
+ }
+
+ trip = of_thermal_get_trip_points(data->tz);
+
+ /* Enable monitoring */
+ site |= 0x1 << (15 - data->sensor_id);
+ tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
+
+ return 0;
+
+err_tmu:
+ iounmap(data->regs);
+
+err_iomap:
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int qoriq_tmu_remove(struct platform_device *pdev)
+{
+ struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+ thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
+
+ /* Disable monitoring */
+ tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+
+ iounmap(data->regs);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qoriq_tmu_suspend(struct device *dev)
+{
+ u32 tmr;
+ struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+ /* Disable monitoring */
+ tmr = tmu_read(data, &data->regs->tmr);
+ tmr &= ~TMR_ME;
+ tmu_write(data, tmr, &data->regs->tmr);
+
+ return 0;
+}
+
+static int qoriq_tmu_resume(struct device *dev)
+{
+ u32 tmr;
+ struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+ /* Enable monitoring */
+ tmr = tmu_read(data, &data->regs->tmr);
+ tmr |= TMR_ME;
+ tmu_write(data, tmr, &data->regs->tmr);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
+ qoriq_tmu_suspend, qoriq_tmu_resume);
+
+static const struct of_device_id qoriq_tmu_match[] = {
+ { .compatible = "fsl,qoriq-tmu", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
+
+static struct platform_driver qoriq_tmu = {
+ .driver = {
+ .name = "qoriq_thermal",
+ .pm = &qoriq_tmu_pm_ops,
+ .of_match_table = qoriq_tmu_match,
+ },
+ .probe = qoriq_tmu_probe,
+ .remove = qoriq_tmu_remove,
+};
+module_platform_driver(qoriq_tmu);
+
+MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
+MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
+MODULE_LICENSE("GPL v2");
--
2.1.0.27.g96db324
^ permalink raw reply related [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
2016-06-30 3:08 ` Jia Hongtao
(?)
(?)
@ 2016-07-19 6:54 ` Hongtao Jia
-1 siblings, 0 replies; 18+ messages in thread
From: Hongtao Jia @ 2016-07-19 6:54 UTC (permalink / raw)
To: edubezval, rui.zhang, Scott Wood
Cc: linux-pm, devicetree, linux-kernel, linuxppc-dev, linux-arm-kernel
Hi Eduardo,
Any comments on this patch?
Thanks.
-Hongtao.
> -----Original Message-----
> From: Jia Hongtao [mailto:hongtao.jia@nxp.com]
> Sent: Thursday, June 30, 2016 11:09 AM
> To: edubezval@gmail.com; rui.zhang@intel.com; robh+dt@kernel.org;
> galak@codeaurora.org; Scott Wood <scott.wood@nxp.com>;
> shawnguo@kernel.org
> Cc: linux-pm@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-arm-
> kernel@lists.infradead.org; Hongtao Jia <hongtao.jia@nxp.com>
> Subject: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> This driver add thermal management support by enabling TMU (Thermal
> Monitoring Unit) on QorIQ platform.
>
> It's based on thermal of framework:
> - Trip points defined in device tree.
> - Cpufreq as cooling device registered in qoriq cpufreq driver.
>
> Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
> ---
> Changes of V2:
> * Add HAS_IOMEM dependency to fix build error on UM
>
> drivers/thermal/Kconfig | 10 ++
> drivers/thermal/Makefile | 1 +
> drivers/thermal/qoriq_thermal.c | 328
> ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 339 insertions(+)
> create mode 100644 drivers/thermal/qoriq_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> 2d702ca..56ef30d 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -195,6 +195,16 @@ config IMX_THERMAL
> cpufreq is used as the cooling device to throttle CPUs when the
> passive trip is crossed.
>
> +config QORIQ_THERMAL
> + tristate "QorIQ Thermal Monitoring Unit"
> + depends on THERMAL_OF
> + depends on HAS_IOMEM
> + help
> + Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
> + It supports one critical trip point and one passive trip point. The
> + cpufreq is used as the cooling device to throttle CPUs when the
> + passive trip is crossed.
> +
> config SPEAR_THERMAL
> tristate "SPEAr thermal sensor driver"
> depends on PLAT_SPEAR || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> 10b07c1..6662232 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
> obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
> obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c new
> file mode 100644 index 0000000..644ba52
> --- /dev/null
> +++ b/drivers/thermal/qoriq_thermal.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright 2016 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> +it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> +WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> +or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> +License for
> + * more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define SITES_MAX 16
> +
> +/*
> + * QorIQ TMU Registers
> + */
> +struct qoriq_tmu_site_regs {
> + u32 tritsr; /* Immediate Temperature Site Register */
> + u32 tratsr; /* Average Temperature Site Register */
> + u8 res0[0x8];
> +};
> +
> +struct qoriq_tmu_regs {
> + u32 tmr; /* Mode Register */
> +#define TMR_DISABLE 0x0
> +#define TMR_ME 0x80000000
> +#define TMR_ALPF 0x0c000000
> + u32 tsr; /* Status Register */
> + u32 tmtmir; /* Temperature measurement interval Register */
> +#define TMTMIR_DEFAULT 0x0000000f
> + u8 res0[0x14];
> + u32 tier; /* Interrupt Enable Register */
> +#define TIER_DISABLE 0x0
> + u32 tidr; /* Interrupt Detect Register */
> + u32 tiscr; /* Interrupt Site Capture Register */
> + u32 ticscr; /* Interrupt Critical Site Capture Register */
> + u8 res1[0x10];
> + u32 tmhtcrh; /* High Temperature Capture Register */
> + u32 tmhtcrl; /* Low Temperature Capture Register */
> + u8 res2[0x8];
> + u32 tmhtitr; /* High Temperature Immediate Threshold */
> + u32 tmhtatr; /* High Temperature Average Threshold */
> + u32 tmhtactr; /* High Temperature Average Crit Threshold */
> + u8 res3[0x24];
> + u32 ttcfgr; /* Temperature Configuration Register */
> + u32 tscfgr; /* Sensor Configuration Register */
> + u8 res4[0x78];
> + struct qoriq_tmu_site_regs site[SITES_MAX];
> + u8 res5[0x9f8];
> + u32 ipbrr0; /* IP Block Revision Register 0 */
> + u32 ipbrr1; /* IP Block Revision Register 1 */
> + u8 res6[0x310];
> + u32 ttr0cr; /* Temperature Range 0 Control Register */
> + u32 ttr1cr; /* Temperature Range 1 Control Register */
> + u32 ttr2cr; /* Temperature Range 2 Control Register */
> + u32 ttr3cr; /* Temperature Range 3 Control Register */
> +};
> +
> +/*
> + * Thermal zone data
> + */
> +struct qoriq_tmu_data {
> + struct thermal_zone_device *tz;
> + struct qoriq_tmu_regs __iomem *regs;
> + int sensor_id;
> + bool little_endian;
> +};
> +
> +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem
> +*addr) {
> + if (p->little_endian)
> + iowrite32(val, addr);
> + else
> + iowrite32be(val, addr);
> +}
> +
> +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) {
> + if (p->little_endian)
> + return ioread32(addr);
> + else
> + return ioread32be(addr);
> +}
> +
> +static int tmu_get_temp(void *p, int *temp) {
> + u32 val;
> + struct qoriq_tmu_data *data = p;
> +
> + val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
> + *temp = (val & 0xff) * 1000;
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_get_sensor_id(void) {
> + int ret, id;
> + struct of_phandle_args sensor_specs;
> + struct device_node *np, *sensor_np;
> +
> + np = of_find_node_by_name(NULL, "thermal-zones");
> + if (!np)
> + return -ENODEV;
> +
> + sensor_np = of_get_next_child(np, NULL);
> + ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
> + "#thermal-sensor-cells",
> + 0, &sensor_specs);
> + if (ret) {
> + of_node_put(np);
> + of_node_put(sensor_np);
> + return ret;
> + }
> +
> + if (sensor_specs.args_count >= 1) {
> + id = sensor_specs.args[0];
> + WARN(sensor_specs.args_count > 1,
> + "%s: too many cells in sensor specifier %d\n",
> + sensor_specs.np->name,
> sensor_specs.args_count);
> + } else {
> + id = 0;
> + }
> +
> + of_node_put(np);
> + of_node_put(sensor_np);
> +
> + return id;
> +}
> +
> +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> + int i, val, len;
> + u32 range[4];
> + const u32 *calibration;
> + struct device_node *np = pdev->dev.of_node;
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
> + dev_err(&pdev->dev, "missing calibration range.\n");
> + return -ENODEV;
> + }
> +
> + /* Init temperature range registers */
> + tmu_write(data, range[0], &data->regs->ttr0cr);
> + tmu_write(data, range[1], &data->regs->ttr1cr);
> + tmu_write(data, range[2], &data->regs->ttr2cr);
> + tmu_write(data, range[3], &data->regs->ttr3cr);
> +
> + calibration = of_get_property(np, "fsl,tmu-calibration", &len);
> + if (calibration == NULL || len % 8) {
> + dev_err(&pdev->dev, "invalid calibration data.\n");
> + return -ENODEV;
> + }
> +
> + for (i = 0; i < len; i += 8, calibration += 2) {
> + val = of_read_number(calibration, 1);
> + tmu_write(data, val, &data->regs->ttcfgr);
> + val = of_read_number(calibration + 1, 1);
> + tmu_write(data, val, &data->regs->tscfgr);
> + }
> +
> + return 0;
> +}
> +
> +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> + /* Disable interrupt, using polling instead */
> + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> +
> + /* Set update_interval */
> + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr); }
> +
> +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> + .get_temp = tmu_get_temp,
> +};
> +
> +static int qoriq_tmu_probe(struct platform_device *pdev) {
> + int ret;
> + const struct thermal_trip *trip;
> + struct qoriq_tmu_data *data;
> + struct device_node *np = pdev->dev.of_node;
> + u32 site = 0;
> +
> + if (!np) {
> + dev_err(&pdev->dev, "Device OF-Node is NULL");
> + return -ENODEV;
> + }
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> + GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, data);
> +
> + data->little_endian = of_property_read_bool(np, "little-endian");
> +
> + data->sensor_id = qoriq_tmu_get_sensor_id();
> + if (data->sensor_id < 0) {
> + dev_err(&pdev->dev, "Failed to get sensor id\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + data->regs = of_iomap(np, 0);
> + if (!data->regs) {
> + dev_err(&pdev->dev, "Failed to get memory region\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + qoriq_tmu_init_device(data); /* TMU initialization */
> +
> + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> + if (ret < 0)
> + goto err_tmu;
> +
> + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
> + data, &tmu_tz_ops);
> + if (IS_ERR(data->tz)) {
> + ret = PTR_ERR(data->tz);
> + dev_err(&pdev->dev,
> + "Failed to register thermal zone device %d\n", ret);
> + goto err_tmu;
> + }
> +
> + trip = of_thermal_get_trip_points(data->tz);
> +
> + /* Enable monitoring */
> + site |= 0x1 << (15 - data->sensor_id);
> + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> +
> + return 0;
> +
> +err_tmu:
> + iounmap(data->regs);
> +
> +err_iomap:
> + platform_set_drvdata(pdev, NULL);
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_remove(struct platform_device *pdev) {
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +
> + iounmap(data->regs);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qoriq_tmu_suspend(struct device *dev) {
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Disable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr &= ~TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_resume(struct device *dev) {
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Enable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr |= TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> + qoriq_tmu_suspend, qoriq_tmu_resume);
> +
> +static const struct of_device_id qoriq_tmu_match[] = {
> + { .compatible = "fsl,qoriq-tmu", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> +
> +static struct platform_driver qoriq_tmu = {
> + .driver = {
> + .name = "qoriq_thermal",
> + .pm = &qoriq_tmu_pm_ops,
> + .of_match_table = qoriq_tmu_match,
> + },
> + .probe = qoriq_tmu_probe,
> + .remove = qoriq_tmu_remove,
> +};
> +module_platform_driver(qoriq_tmu);
> +
> +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0.27.g96db324
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-07-19 6:54 ` Hongtao Jia
0 siblings, 0 replies; 18+ messages in thread
From: Hongtao Jia @ 2016-07-19 6:54 UTC (permalink / raw)
To: edubezval, rui.zhang, Scott Wood
Cc: devicetree, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-pm
Hi Eduardo,
Any comments on this patch?
Thanks.
-Hongtao.
> -----Original Message-----
> From: Jia Hongtao [mailto:hongtao.jia@nxp.com]
> Sent: Thursday, June 30, 2016 11:09 AM
> To: edubezval@gmail.com; rui.zhang@intel.com; robh+dt@kernel.org;
> galak@codeaurora.org; Scott Wood <scott.wood@nxp.com>;
> shawnguo@kernel.org
> Cc: linux-pm@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-arm-
> kernel@lists.infradead.org; Hongtao Jia <hongtao.jia@nxp.com>
> Subject: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> This driver add thermal management support by enabling TMU (Thermal
> Monitoring Unit) on QorIQ platform.
>
> It's based on thermal of framework:
> - Trip points defined in device tree.
> - Cpufreq as cooling device registered in qoriq cpufreq driver.
>
> Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
> ---
> Changes of V2:
> * Add HAS_IOMEM dependency to fix build error on UM
>
> drivers/thermal/Kconfig | 10 ++
> drivers/thermal/Makefile | 1 +
> drivers/thermal/qoriq_thermal.c | 328
> ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 339 insertions(+)
> create mode 100644 drivers/thermal/qoriq_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> 2d702ca..56ef30d 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -195,6 +195,16 @@ config IMX_THERMAL
> cpufreq is used as the cooling device to throttle CPUs when the
> passive trip is crossed.
>
> +config QORIQ_THERMAL
> + tristate "QorIQ Thermal Monitoring Unit"
> + depends on THERMAL_OF
> + depends on HAS_IOMEM
> + help
> + Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
> + It supports one critical trip point and one passive trip point. The
> + cpufreq is used as the cooling device to throttle CPUs when the
> + passive trip is crossed.
> +
> config SPEAR_THERMAL
> tristate "SPEAr thermal sensor driver"
> depends on PLAT_SPEAR || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> 10b07c1..6662232 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
> obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
> obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c new
> file mode 100644 index 0000000..644ba52
> --- /dev/null
> +++ b/drivers/thermal/qoriq_thermal.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright 2016 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> +it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> +WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> +or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> +License for
> + * more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define SITES_MAX 16
> +
> +/*
> + * QorIQ TMU Registers
> + */
> +struct qoriq_tmu_site_regs {
> + u32 tritsr; /* Immediate Temperature Site Register */
> + u32 tratsr; /* Average Temperature Site Register */
> + u8 res0[0x8];
> +};
> +
> +struct qoriq_tmu_regs {
> + u32 tmr; /* Mode Register */
> +#define TMR_DISABLE 0x0
> +#define TMR_ME 0x80000000
> +#define TMR_ALPF 0x0c000000
> + u32 tsr; /* Status Register */
> + u32 tmtmir; /* Temperature measurement interval Register */
> +#define TMTMIR_DEFAULT 0x0000000f
> + u8 res0[0x14];
> + u32 tier; /* Interrupt Enable Register */
> +#define TIER_DISABLE 0x0
> + u32 tidr; /* Interrupt Detect Register */
> + u32 tiscr; /* Interrupt Site Capture Register */
> + u32 ticscr; /* Interrupt Critical Site Capture Register */
> + u8 res1[0x10];
> + u32 tmhtcrh; /* High Temperature Capture Register */
> + u32 tmhtcrl; /* Low Temperature Capture Register */
> + u8 res2[0x8];
> + u32 tmhtitr; /* High Temperature Immediate Threshold */
> + u32 tmhtatr; /* High Temperature Average Threshold */
> + u32 tmhtactr; /* High Temperature Average Crit Threshold */
> + u8 res3[0x24];
> + u32 ttcfgr; /* Temperature Configuration Register */
> + u32 tscfgr; /* Sensor Configuration Register */
> + u8 res4[0x78];
> + struct qoriq_tmu_site_regs site[SITES_MAX];
> + u8 res5[0x9f8];
> + u32 ipbrr0; /* IP Block Revision Register 0 */
> + u32 ipbrr1; /* IP Block Revision Register 1 */
> + u8 res6[0x310];
> + u32 ttr0cr; /* Temperature Range 0 Control Register */
> + u32 ttr1cr; /* Temperature Range 1 Control Register */
> + u32 ttr2cr; /* Temperature Range 2 Control Register */
> + u32 ttr3cr; /* Temperature Range 3 Control Register */
> +};
> +
> +/*
> + * Thermal zone data
> + */
> +struct qoriq_tmu_data {
> + struct thermal_zone_device *tz;
> + struct qoriq_tmu_regs __iomem *regs;
> + int sensor_id;
> + bool little_endian;
> +};
> +
> +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem
> +*addr) {
> + if (p->little_endian)
> + iowrite32(val, addr);
> + else
> + iowrite32be(val, addr);
> +}
> +
> +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) {
> + if (p->little_endian)
> + return ioread32(addr);
> + else
> + return ioread32be(addr);
> +}
> +
> +static int tmu_get_temp(void *p, int *temp) {
> + u32 val;
> + struct qoriq_tmu_data *data = p;
> +
> + val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
> + *temp = (val & 0xff) * 1000;
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_get_sensor_id(void) {
> + int ret, id;
> + struct of_phandle_args sensor_specs;
> + struct device_node *np, *sensor_np;
> +
> + np = of_find_node_by_name(NULL, "thermal-zones");
> + if (!np)
> + return -ENODEV;
> +
> + sensor_np = of_get_next_child(np, NULL);
> + ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
> + "#thermal-sensor-cells",
> + 0, &sensor_specs);
> + if (ret) {
> + of_node_put(np);
> + of_node_put(sensor_np);
> + return ret;
> + }
> +
> + if (sensor_specs.args_count >= 1) {
> + id = sensor_specs.args[0];
> + WARN(sensor_specs.args_count > 1,
> + "%s: too many cells in sensor specifier %d\n",
> + sensor_specs.np->name,
> sensor_specs.args_count);
> + } else {
> + id = 0;
> + }
> +
> + of_node_put(np);
> + of_node_put(sensor_np);
> +
> + return id;
> +}
> +
> +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> + int i, val, len;
> + u32 range[4];
> + const u32 *calibration;
> + struct device_node *np = pdev->dev.of_node;
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
> + dev_err(&pdev->dev, "missing calibration range.\n");
> + return -ENODEV;
> + }
> +
> + /* Init temperature range registers */
> + tmu_write(data, range[0], &data->regs->ttr0cr);
> + tmu_write(data, range[1], &data->regs->ttr1cr);
> + tmu_write(data, range[2], &data->regs->ttr2cr);
> + tmu_write(data, range[3], &data->regs->ttr3cr);
> +
> + calibration = of_get_property(np, "fsl,tmu-calibration", &len);
> + if (calibration == NULL || len % 8) {
> + dev_err(&pdev->dev, "invalid calibration data.\n");
> + return -ENODEV;
> + }
> +
> + for (i = 0; i < len; i += 8, calibration += 2) {
> + val = of_read_number(calibration, 1);
> + tmu_write(data, val, &data->regs->ttcfgr);
> + val = of_read_number(calibration + 1, 1);
> + tmu_write(data, val, &data->regs->tscfgr);
> + }
> +
> + return 0;
> +}
> +
> +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> + /* Disable interrupt, using polling instead */
> + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> +
> + /* Set update_interval */
> + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr); }
> +
> +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> + .get_temp = tmu_get_temp,
> +};
> +
> +static int qoriq_tmu_probe(struct platform_device *pdev) {
> + int ret;
> + const struct thermal_trip *trip;
> + struct qoriq_tmu_data *data;
> + struct device_node *np = pdev->dev.of_node;
> + u32 site = 0;
> +
> + if (!np) {
> + dev_err(&pdev->dev, "Device OF-Node is NULL");
> + return -ENODEV;
> + }
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> + GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, data);
> +
> + data->little_endian = of_property_read_bool(np, "little-endian");
> +
> + data->sensor_id = qoriq_tmu_get_sensor_id();
> + if (data->sensor_id < 0) {
> + dev_err(&pdev->dev, "Failed to get sensor id\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + data->regs = of_iomap(np, 0);
> + if (!data->regs) {
> + dev_err(&pdev->dev, "Failed to get memory region\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + qoriq_tmu_init_device(data); /* TMU initialization */
> +
> + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> + if (ret < 0)
> + goto err_tmu;
> +
> + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
> + data, &tmu_tz_ops);
> + if (IS_ERR(data->tz)) {
> + ret = PTR_ERR(data->tz);
> + dev_err(&pdev->dev,
> + "Failed to register thermal zone device %d\n", ret);
> + goto err_tmu;
> + }
> +
> + trip = of_thermal_get_trip_points(data->tz);
> +
> + /* Enable monitoring */
> + site |= 0x1 << (15 - data->sensor_id);
> + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> +
> + return 0;
> +
> +err_tmu:
> + iounmap(data->regs);
> +
> +err_iomap:
> + platform_set_drvdata(pdev, NULL);
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_remove(struct platform_device *pdev) {
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +
> + iounmap(data->regs);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qoriq_tmu_suspend(struct device *dev) {
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Disable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr &= ~TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_resume(struct device *dev) {
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Enable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr |= TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> + qoriq_tmu_suspend, qoriq_tmu_resume);
> +
> +static const struct of_device_id qoriq_tmu_match[] = {
> + { .compatible = "fsl,qoriq-tmu", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> +
> +static struct platform_driver qoriq_tmu = {
> + .driver = {
> + .name = "qoriq_thermal",
> + .pm = &qoriq_tmu_pm_ops,
> + .of_match_table = qoriq_tmu_match,
> + },
> + .probe = qoriq_tmu_probe,
> + .remove = qoriq_tmu_remove,
> +};
> +module_platform_driver(qoriq_tmu);
> +
> +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0.27.g96db324
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-07-19 6:54 ` Hongtao Jia
0 siblings, 0 replies; 18+ messages in thread
From: Hongtao Jia @ 2016-07-19 6:54 UTC (permalink / raw)
To: edubezval, rui.zhang, Scott Wood
Cc: linux-pm, devicetree, linux-kernel, linuxppc-dev, linux-arm-kernel
Hi Eduardo,
Any comments on this patch?
Thanks.
-Hongtao.
> -----Original Message-----
> From: Jia Hongtao [mailto:hongtao.jia@nxp.com]
> Sent: Thursday, June 30, 2016 11:09 AM
> To: edubezval@gmail.com; rui.zhang@intel.com; robh+dt@kernel.org;
> galak@codeaurora.org; Scott Wood <scott.wood@nxp.com>;
> shawnguo@kernel.org
> Cc: linux-pm@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-arm-
> kernel@lists.infradead.org; Hongtao Jia <hongtao.jia@nxp.com>
> Subject: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>=20
> This driver add thermal management support by enabling TMU (Thermal
> Monitoring Unit) on QorIQ platform.
>=20
> It's based on thermal of framework:
> - Trip points defined in device tree.
> - Cpufreq as cooling device registered in qoriq cpufreq driver.
>=20
> Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
> ---
> Changes of V2:
> * Add HAS_IOMEM dependency to fix build error on UM
>=20
> drivers/thermal/Kconfig | 10 ++
> drivers/thermal/Makefile | 1 +
> drivers/thermal/qoriq_thermal.c | 328
> ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 339 insertions(+)
> create mode 100644 drivers/thermal/qoriq_thermal.c
>=20
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> 2d702ca..56ef30d 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -195,6 +195,16 @@ config IMX_THERMAL
> cpufreq is used as the cooling device to throttle CPUs when the
> passive trip is crossed.
>=20
> +config QORIQ_THERMAL
> + tristate "QorIQ Thermal Monitoring Unit"
> + depends on THERMAL_OF
> + depends on HAS_IOMEM
> + help
> + Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
> + It supports one critical trip point and one passive trip point. The
> + cpufreq is used as the cooling device to throttle CPUs when the
> + passive trip is crossed.
> +
> config SPEAR_THERMAL
> tristate "SPEAr thermal sensor driver"
> depends on PLAT_SPEAR || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> 10b07c1..6662232 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=3D db8500_thermal.o
> obj-$(CONFIG_ARMADA_THERMAL) +=3D armada_thermal.o
> obj-$(CONFIG_TANGO_THERMAL) +=3D tango_thermal.o
> obj-$(CONFIG_IMX_THERMAL) +=3D imx_thermal.o
> +obj-$(CONFIG_QORIQ_THERMAL) +=3D qoriq_thermal.o
> obj-$(CONFIG_DB8500_CPUFREQ_COOLING) +=3D db8500_cpufreq_cooling.o
> obj-$(CONFIG_INTEL_POWERCLAMP) +=3D intel_powerclamp.o
> obj-$(CONFIG_X86_PKG_TEMP_THERMAL) +=3D x86_pkg_temp_thermal.o
> diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_ther=
mal.c new
> file mode 100644 index 0000000..644ba52
> --- /dev/null
> +++ b/drivers/thermal/qoriq_thermal.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright 2016 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> +it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> +WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> +or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> +License for
> + * more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define SITES_MAX 16
> +
> +/*
> + * QorIQ TMU Registers
> + */
> +struct qoriq_tmu_site_regs {
> + u32 tritsr; /* Immediate Temperature Site Register */
> + u32 tratsr; /* Average Temperature Site Register */
> + u8 res0[0x8];
> +};
> +
> +struct qoriq_tmu_regs {
> + u32 tmr; /* Mode Register */
> +#define TMR_DISABLE 0x0
> +#define TMR_ME 0x80000000
> +#define TMR_ALPF 0x0c000000
> + u32 tsr; /* Status Register */
> + u32 tmtmir; /* Temperature measurement interval Register */
> +#define TMTMIR_DEFAULT 0x0000000f
> + u8 res0[0x14];
> + u32 tier; /* Interrupt Enable Register */
> +#define TIER_DISABLE 0x0
> + u32 tidr; /* Interrupt Detect Register */
> + u32 tiscr; /* Interrupt Site Capture Register */
> + u32 ticscr; /* Interrupt Critical Site Capture Register */
> + u8 res1[0x10];
> + u32 tmhtcrh; /* High Temperature Capture Register */
> + u32 tmhtcrl; /* Low Temperature Capture Register */
> + u8 res2[0x8];
> + u32 tmhtitr; /* High Temperature Immediate Threshold */
> + u32 tmhtatr; /* High Temperature Average Threshold */
> + u32 tmhtactr; /* High Temperature Average Crit Threshold */
> + u8 res3[0x24];
> + u32 ttcfgr; /* Temperature Configuration Register */
> + u32 tscfgr; /* Sensor Configuration Register */
> + u8 res4[0x78];
> + struct qoriq_tmu_site_regs site[SITES_MAX];
> + u8 res5[0x9f8];
> + u32 ipbrr0; /* IP Block Revision Register 0 */
> + u32 ipbrr1; /* IP Block Revision Register 1 */
> + u8 res6[0x310];
> + u32 ttr0cr; /* Temperature Range 0 Control Register */
> + u32 ttr1cr; /* Temperature Range 1 Control Register */
> + u32 ttr2cr; /* Temperature Range 2 Control Register */
> + u32 ttr3cr; /* Temperature Range 3 Control Register */
> +};
> +
> +/*
> + * Thermal zone data
> + */
> +struct qoriq_tmu_data {
> + struct thermal_zone_device *tz;
> + struct qoriq_tmu_regs __iomem *regs;
> + int sensor_id;
> + bool little_endian;
> +};
> +
> +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem
> +*addr) {
> + if (p->little_endian)
> + iowrite32(val, addr);
> + else
> + iowrite32be(val, addr);
> +}
> +
> +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) {
> + if (p->little_endian)
> + return ioread32(addr);
> + else
> + return ioread32be(addr);
> +}
> +
> +static int tmu_get_temp(void *p, int *temp) {
> + u32 val;
> + struct qoriq_tmu_data *data =3D p;
> +
> + val =3D tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
> + *temp =3D (val & 0xff) * 1000;
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_get_sensor_id(void) {
> + int ret, id;
> + struct of_phandle_args sensor_specs;
> + struct device_node *np, *sensor_np;
> +
> + np =3D of_find_node_by_name(NULL, "thermal-zones");
> + if (!np)
> + return -ENODEV;
> +
> + sensor_np =3D of_get_next_child(np, NULL);
> + ret =3D of_parse_phandle_with_args(sensor_np, "thermal-sensors",
> + "#thermal-sensor-cells",
> + 0, &sensor_specs);
> + if (ret) {
> + of_node_put(np);
> + of_node_put(sensor_np);
> + return ret;
> + }
> +
> + if (sensor_specs.args_count >=3D 1) {
> + id =3D sensor_specs.args[0];
> + WARN(sensor_specs.args_count > 1,
> + "%s: too many cells in sensor specifier %d\n",
> + sensor_specs.np->name,
> sensor_specs.args_count);
> + } else {
> + id =3D 0;
> + }
> +
> + of_node_put(np);
> + of_node_put(sensor_np);
> +
> + return id;
> +}
> +
> +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> + int i, val, len;
> + u32 range[4];
> + const u32 *calibration;
> + struct device_node *np =3D pdev->dev.of_node;
> + struct qoriq_tmu_data *data =3D platform_get_drvdata(pdev);
> +
> + if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
> + dev_err(&pdev->dev, "missing calibration range.\n");
> + return -ENODEV;
> + }
> +
> + /* Init temperature range registers */
> + tmu_write(data, range[0], &data->regs->ttr0cr);
> + tmu_write(data, range[1], &data->regs->ttr1cr);
> + tmu_write(data, range[2], &data->regs->ttr2cr);
> + tmu_write(data, range[3], &data->regs->ttr3cr);
> +
> + calibration =3D of_get_property(np, "fsl,tmu-calibration", &len);
> + if (calibration =3D=3D NULL || len % 8) {
> + dev_err(&pdev->dev, "invalid calibration data.\n");
> + return -ENODEV;
> + }
> +
> + for (i =3D 0; i < len; i +=3D 8, calibration +=3D 2) {
> + val =3D of_read_number(calibration, 1);
> + tmu_write(data, val, &data->regs->ttcfgr);
> + val =3D of_read_number(calibration + 1, 1);
> + tmu_write(data, val, &data->regs->tscfgr);
> + }
> +
> + return 0;
> +}
> +
> +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> + /* Disable interrupt, using polling instead */
> + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> +
> + /* Set update_interval */
> + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr); }
> +
> +static struct thermal_zone_of_device_ops tmu_tz_ops =3D {
> + .get_temp =3D tmu_get_temp,
> +};
> +
> +static int qoriq_tmu_probe(struct platform_device *pdev) {
> + int ret;
> + const struct thermal_trip *trip;
> + struct qoriq_tmu_data *data;
> + struct device_node *np =3D pdev->dev.of_node;
> + u32 site =3D 0;
> +
> + if (!np) {
> + dev_err(&pdev->dev, "Device OF-Node is NULL");
> + return -ENODEV;
> + }
> +
> + data =3D devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> + GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, data);
> +
> + data->little_endian =3D of_property_read_bool(np, "little-endian");
> +
> + data->sensor_id =3D qoriq_tmu_get_sensor_id();
> + if (data->sensor_id < 0) {
> + dev_err(&pdev->dev, "Failed to get sensor id\n");
> + ret =3D -ENODEV;
> + goto err_iomap;
> + }
> +
> + data->regs =3D of_iomap(np, 0);
> + if (!data->regs) {
> + dev_err(&pdev->dev, "Failed to get memory region\n");
> + ret =3D -ENODEV;
> + goto err_iomap;
> + }
> +
> + qoriq_tmu_init_device(data); /* TMU initialization */
> +
> + ret =3D qoriq_tmu_calibration(pdev); /* TMU calibration */
> + if (ret < 0)
> + goto err_tmu;
> +
> + data->tz =3D thermal_zone_of_sensor_register(&pdev->dev, data->sensor_i=
d,
> + data, &tmu_tz_ops);
> + if (IS_ERR(data->tz)) {
> + ret =3D PTR_ERR(data->tz);
> + dev_err(&pdev->dev,
> + "Failed to register thermal zone device %d\n", ret);
> + goto err_tmu;
> + }
> +
> + trip =3D of_thermal_get_trip_points(data->tz);
> +
> + /* Enable monitoring */
> + site |=3D 0x1 << (15 - data->sensor_id);
> + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> +
> + return 0;
> +
> +err_tmu:
> + iounmap(data->regs);
> +
> +err_iomap:
> + platform_set_drvdata(pdev, NULL);
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_remove(struct platform_device *pdev) {
> + struct qoriq_tmu_data *data =3D platform_get_drvdata(pdev);
> +
> + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +
> + iounmap(data->regs);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qoriq_tmu_suspend(struct device *dev) {
> + u32 tmr;
> + struct qoriq_tmu_data *data =3D dev_get_drvdata(dev);
> +
> + /* Disable monitoring */
> + tmr =3D tmu_read(data, &data->regs->tmr);
> + tmr &=3D ~TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_resume(struct device *dev) {
> + u32 tmr;
> + struct qoriq_tmu_data *data =3D dev_get_drvdata(dev);
> +
> + /* Enable monitoring */
> + tmr =3D tmu_read(data, &data->regs->tmr);
> + tmr |=3D TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> + qoriq_tmu_suspend, qoriq_tmu_resume);
> +
> +static const struct of_device_id qoriq_tmu_match[] =3D {
> + { .compatible =3D "fsl,qoriq-tmu", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> +
> +static struct platform_driver qoriq_tmu =3D {
> + .driver =3D {
> + .name =3D "qoriq_thermal",
> + .pm =3D &qoriq_tmu_pm_ops,
> + .of_match_table =3D qoriq_tmu_match,
> + },
> + .probe =3D qoriq_tmu_probe,
> + .remove =3D qoriq_tmu_remove,
> +};
> +module_platform_driver(qoriq_tmu);
> +
> +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0.27.g96db324
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-07-19 6:54 ` Hongtao Jia
0 siblings, 0 replies; 18+ messages in thread
From: Hongtao Jia @ 2016-07-19 6:54 UTC (permalink / raw)
To: linux-arm-kernel
Hi Eduardo,
Any comments on this patch?
Thanks.
-Hongtao.
> -----Original Message-----
> From: Jia Hongtao [mailto:hongtao.jia at nxp.com]
> Sent: Thursday, June 30, 2016 11:09 AM
> To: edubezval at gmail.com; rui.zhang at intel.com; robh+dt at kernel.org;
> galak at codeaurora.org; Scott Wood <scott.wood@nxp.com>;
> shawnguo at kernel.org
> Cc: linux-pm at vger.kernel.org; devicetree at vger.kernel.org; linux-
> kernel at vger.kernel.org; linuxppc-dev at lists.ozlabs.org; linux-arm-
> kernel at lists.infradead.org; Hongtao Jia <hongtao.jia@nxp.com>
> Subject: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> This driver add thermal management support by enabling TMU (Thermal
> Monitoring Unit) on QorIQ platform.
>
> It's based on thermal of framework:
> - Trip points defined in device tree.
> - Cpufreq as cooling device registered in qoriq cpufreq driver.
>
> Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
> ---
> Changes of V2:
> * Add HAS_IOMEM dependency to fix build error on UM
>
> drivers/thermal/Kconfig | 10 ++
> drivers/thermal/Makefile | 1 +
> drivers/thermal/qoriq_thermal.c | 328
> ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 339 insertions(+)
> create mode 100644 drivers/thermal/qoriq_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> 2d702ca..56ef30d 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -195,6 +195,16 @@ config IMX_THERMAL
> cpufreq is used as the cooling device to throttle CPUs when the
> passive trip is crossed.
>
> +config QORIQ_THERMAL
> + tristate "QorIQ Thermal Monitoring Unit"
> + depends on THERMAL_OF
> + depends on HAS_IOMEM
> + help
> + Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
> + It supports one critical trip point and one passive trip point. The
> + cpufreq is used as the cooling device to throttle CPUs when the
> + passive trip is crossed.
> +
> config SPEAR_THERMAL
> tristate "SPEAr thermal sensor driver"
> depends on PLAT_SPEAR || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> 10b07c1..6662232 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
> obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
> obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c new
> file mode 100644 index 0000000..644ba52
> --- /dev/null
> +++ b/drivers/thermal/qoriq_thermal.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright 2016 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> +it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> +WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> +or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> +License for
> + * more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define SITES_MAX 16
> +
> +/*
> + * QorIQ TMU Registers
> + */
> +struct qoriq_tmu_site_regs {
> + u32 tritsr; /* Immediate Temperature Site Register */
> + u32 tratsr; /* Average Temperature Site Register */
> + u8 res0[0x8];
> +};
> +
> +struct qoriq_tmu_regs {
> + u32 tmr; /* Mode Register */
> +#define TMR_DISABLE 0x0
> +#define TMR_ME 0x80000000
> +#define TMR_ALPF 0x0c000000
> + u32 tsr; /* Status Register */
> + u32 tmtmir; /* Temperature measurement interval Register */
> +#define TMTMIR_DEFAULT 0x0000000f
> + u8 res0[0x14];
> + u32 tier; /* Interrupt Enable Register */
> +#define TIER_DISABLE 0x0
> + u32 tidr; /* Interrupt Detect Register */
> + u32 tiscr; /* Interrupt Site Capture Register */
> + u32 ticscr; /* Interrupt Critical Site Capture Register */
> + u8 res1[0x10];
> + u32 tmhtcrh; /* High Temperature Capture Register */
> + u32 tmhtcrl; /* Low Temperature Capture Register */
> + u8 res2[0x8];
> + u32 tmhtitr; /* High Temperature Immediate Threshold */
> + u32 tmhtatr; /* High Temperature Average Threshold */
> + u32 tmhtactr; /* High Temperature Average Crit Threshold */
> + u8 res3[0x24];
> + u32 ttcfgr; /* Temperature Configuration Register */
> + u32 tscfgr; /* Sensor Configuration Register */
> + u8 res4[0x78];
> + struct qoriq_tmu_site_regs site[SITES_MAX];
> + u8 res5[0x9f8];
> + u32 ipbrr0; /* IP Block Revision Register 0 */
> + u32 ipbrr1; /* IP Block Revision Register 1 */
> + u8 res6[0x310];
> + u32 ttr0cr; /* Temperature Range 0 Control Register */
> + u32 ttr1cr; /* Temperature Range 1 Control Register */
> + u32 ttr2cr; /* Temperature Range 2 Control Register */
> + u32 ttr3cr; /* Temperature Range 3 Control Register */
> +};
> +
> +/*
> + * Thermal zone data
> + */
> +struct qoriq_tmu_data {
> + struct thermal_zone_device *tz;
> + struct qoriq_tmu_regs __iomem *regs;
> + int sensor_id;
> + bool little_endian;
> +};
> +
> +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem
> +*addr) {
> + if (p->little_endian)
> + iowrite32(val, addr);
> + else
> + iowrite32be(val, addr);
> +}
> +
> +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) {
> + if (p->little_endian)
> + return ioread32(addr);
> + else
> + return ioread32be(addr);
> +}
> +
> +static int tmu_get_temp(void *p, int *temp) {
> + u32 val;
> + struct qoriq_tmu_data *data = p;
> +
> + val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
> + *temp = (val & 0xff) * 1000;
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_get_sensor_id(void) {
> + int ret, id;
> + struct of_phandle_args sensor_specs;
> + struct device_node *np, *sensor_np;
> +
> + np = of_find_node_by_name(NULL, "thermal-zones");
> + if (!np)
> + return -ENODEV;
> +
> + sensor_np = of_get_next_child(np, NULL);
> + ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
> + "#thermal-sensor-cells",
> + 0, &sensor_specs);
> + if (ret) {
> + of_node_put(np);
> + of_node_put(sensor_np);
> + return ret;
> + }
> +
> + if (sensor_specs.args_count >= 1) {
> + id = sensor_specs.args[0];
> + WARN(sensor_specs.args_count > 1,
> + "%s: too many cells in sensor specifier %d\n",
> + sensor_specs.np->name,
> sensor_specs.args_count);
> + } else {
> + id = 0;
> + }
> +
> + of_node_put(np);
> + of_node_put(sensor_np);
> +
> + return id;
> +}
> +
> +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> + int i, val, len;
> + u32 range[4];
> + const u32 *calibration;
> + struct device_node *np = pdev->dev.of_node;
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
> + dev_err(&pdev->dev, "missing calibration range.\n");
> + return -ENODEV;
> + }
> +
> + /* Init temperature range registers */
> + tmu_write(data, range[0], &data->regs->ttr0cr);
> + tmu_write(data, range[1], &data->regs->ttr1cr);
> + tmu_write(data, range[2], &data->regs->ttr2cr);
> + tmu_write(data, range[3], &data->regs->ttr3cr);
> +
> + calibration = of_get_property(np, "fsl,tmu-calibration", &len);
> + if (calibration == NULL || len % 8) {
> + dev_err(&pdev->dev, "invalid calibration data.\n");
> + return -ENODEV;
> + }
> +
> + for (i = 0; i < len; i += 8, calibration += 2) {
> + val = of_read_number(calibration, 1);
> + tmu_write(data, val, &data->regs->ttcfgr);
> + val = of_read_number(calibration + 1, 1);
> + tmu_write(data, val, &data->regs->tscfgr);
> + }
> +
> + return 0;
> +}
> +
> +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> + /* Disable interrupt, using polling instead */
> + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> +
> + /* Set update_interval */
> + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr); }
> +
> +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> + .get_temp = tmu_get_temp,
> +};
> +
> +static int qoriq_tmu_probe(struct platform_device *pdev) {
> + int ret;
> + const struct thermal_trip *trip;
> + struct qoriq_tmu_data *data;
> + struct device_node *np = pdev->dev.of_node;
> + u32 site = 0;
> +
> + if (!np) {
> + dev_err(&pdev->dev, "Device OF-Node is NULL");
> + return -ENODEV;
> + }
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> + GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, data);
> +
> + data->little_endian = of_property_read_bool(np, "little-endian");
> +
> + data->sensor_id = qoriq_tmu_get_sensor_id();
> + if (data->sensor_id < 0) {
> + dev_err(&pdev->dev, "Failed to get sensor id\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + data->regs = of_iomap(np, 0);
> + if (!data->regs) {
> + dev_err(&pdev->dev, "Failed to get memory region\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + qoriq_tmu_init_device(data); /* TMU initialization */
> +
> + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> + if (ret < 0)
> + goto err_tmu;
> +
> + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
> + data, &tmu_tz_ops);
> + if (IS_ERR(data->tz)) {
> + ret = PTR_ERR(data->tz);
> + dev_err(&pdev->dev,
> + "Failed to register thermal zone device %d\n", ret);
> + goto err_tmu;
> + }
> +
> + trip = of_thermal_get_trip_points(data->tz);
> +
> + /* Enable monitoring */
> + site |= 0x1 << (15 - data->sensor_id);
> + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> +
> + return 0;
> +
> +err_tmu:
> + iounmap(data->regs);
> +
> +err_iomap:
> + platform_set_drvdata(pdev, NULL);
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_remove(struct platform_device *pdev) {
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +
> + iounmap(data->regs);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qoriq_tmu_suspend(struct device *dev) {
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Disable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr &= ~TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_resume(struct device *dev) {
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Enable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr |= TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> + qoriq_tmu_suspend, qoriq_tmu_resume);
> +
> +static const struct of_device_id qoriq_tmu_match[] = {
> + { .compatible = "fsl,qoriq-tmu", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> +
> +static struct platform_driver qoriq_tmu = {
> + .driver = {
> + .name = "qoriq_thermal",
> + .pm = &qoriq_tmu_pm_ops,
> + .of_match_table = qoriq_tmu_match,
> + },
> + .probe = qoriq_tmu_probe,
> + .remove = qoriq_tmu_remove,
> +};
> +module_platform_driver(qoriq_tmu);
> +
> +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0.27.g96db324
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
2016-07-19 6:54 ` Hongtao Jia
(?)
(?)
@ 2016-08-05 6:26 ` Hongtao Jia
-1 siblings, 0 replies; 18+ messages in thread
From: Hongtao Jia @ 2016-08-05 6:26 UTC (permalink / raw)
To: edubezval, rui.zhang, Scott Wood
Cc: devicetree, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-pm
Hi Eduardo,
If you have any comments please let me know.
Thanks.
-Hongtao.
> -----Original Message-----
> From: Linuxppc-dev [mailto:linuxppc-dev-
> bounces+b38951=freescale.com@lists.ozlabs.org] On Behalf Of Hongtao Jia
> Sent: Tuesday, July 19, 2016 2:54 PM
> To: edubezval@gmail.com; rui.zhang@intel.com; Scott Wood
> <scott.wood@nxp.com>
> Cc: devicetree@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-
> kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
> pm@vger.kernel.org
> Subject: RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> Hi Eduardo,
>
> Any comments on this patch?
>
> Thanks.
> -Hongtao.
>
> > -----Original Message-----
> > From: Jia Hongtao [mailto:hongtao.jia@nxp.com]
> > Sent: Thursday, June 30, 2016 11:09 AM
> > To: edubezval@gmail.com; rui.zhang@intel.com; robh+dt@kernel.org;
> > galak@codeaurora.org; Scott Wood <scott.wood@nxp.com>;
> > shawnguo@kernel.org
> > Cc: linux-pm@vger.kernel.org; devicetree@vger.kernel.org; linux-
> > kernel@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-arm-
> > kernel@lists.infradead.org; Hongtao Jia <hongtao.jia@nxp.com>
> > Subject: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
> >
> > This driver add thermal management support by enabling TMU (Thermal
> > Monitoring Unit) on QorIQ platform.
> >
> > It's based on thermal of framework:
> > - Trip points defined in device tree.
> > - Cpufreq as cooling device registered in qoriq cpufreq driver.
> >
> > Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
> > ---
> > Changes of V2:
> > * Add HAS_IOMEM dependency to fix build error on UM
> >
> > drivers/thermal/Kconfig | 10 ++
> > drivers/thermal/Makefile | 1 +
> > drivers/thermal/qoriq_thermal.c | 328
> > ++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 339 insertions(+)
> > create mode 100644 drivers/thermal/qoriq_thermal.c
> >
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> > 2d702ca..56ef30d 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -195,6 +195,16 @@ config IMX_THERMAL
> > cpufreq is used as the cooling device to throttle CPUs when the
> > passive trip is crossed.
> >
> > +config QORIQ_THERMAL
> > + tristate "QorIQ Thermal Monitoring Unit"
> > + depends on THERMAL_OF
> > + depends on HAS_IOMEM
> > + help
> > + Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
> > + It supports one critical trip point and one passive trip point. The
> > + cpufreq is used as the cooling device to throttle CPUs when the
> > + passive trip is crossed.
> > +
> > config SPEAR_THERMAL
> > tristate "SPEAr thermal sensor driver"
> > depends on PLAT_SPEAR || COMPILE_TEST diff --git
> > a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> > 10b07c1..6662232 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> db8500_thermal.o
> > obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> > obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> > obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> > +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> > obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
> > obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> > obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> > diff --git a/drivers/thermal/qoriq_thermal.c
> > b/drivers/thermal/qoriq_thermal.c new file mode 100644 index
> > 0000000..644ba52
> > --- /dev/null
> > +++ b/drivers/thermal/qoriq_thermal.c
> > @@ -0,0 +1,328 @@
> > +/*
> > + * Copyright 2016 Freescale Semiconductor, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but
> > +WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> > +or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> > +License for
> > + * more details.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/err.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/thermal.h>
> > +
> > +#include "thermal_core.h"
> > +
> > +#define SITES_MAX 16
> > +
> > +/*
> > + * QorIQ TMU Registers
> > + */
> > +struct qoriq_tmu_site_regs {
> > + u32 tritsr; /* Immediate Temperature Site Register */
> > + u32 tratsr; /* Average Temperature Site Register */
> > + u8 res0[0x8];
> > +};
> > +
> > +struct qoriq_tmu_regs {
> > + u32 tmr; /* Mode Register */
> > +#define TMR_DISABLE 0x0
> > +#define TMR_ME 0x80000000
> > +#define TMR_ALPF 0x0c000000
> > + u32 tsr; /* Status Register */
> > + u32 tmtmir; /* Temperature measurement interval Register */
> > +#define TMTMIR_DEFAULT 0x0000000f
> > + u8 res0[0x14];
> > + u32 tier; /* Interrupt Enable Register */
> > +#define TIER_DISABLE 0x0
> > + u32 tidr; /* Interrupt Detect Register */
> > + u32 tiscr; /* Interrupt Site Capture Register */
> > + u32 ticscr; /* Interrupt Critical Site Capture Register */
> > + u8 res1[0x10];
> > + u32 tmhtcrh; /* High Temperature Capture Register */
> > + u32 tmhtcrl; /* Low Temperature Capture Register */
> > + u8 res2[0x8];
> > + u32 tmhtitr; /* High Temperature Immediate Threshold */
> > + u32 tmhtatr; /* High Temperature Average Threshold */
> > + u32 tmhtactr; /* High Temperature Average Crit Threshold */
> > + u8 res3[0x24];
> > + u32 ttcfgr; /* Temperature Configuration Register */
> > + u32 tscfgr; /* Sensor Configuration Register */
> > + u8 res4[0x78];
> > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > + u8 res5[0x9f8];
> > + u32 ipbrr0; /* IP Block Revision Register 0 */
> > + u32 ipbrr1; /* IP Block Revision Register 1 */
> > + u8 res6[0x310];
> > + u32 ttr0cr; /* Temperature Range 0 Control Register */
> > + u32 ttr1cr; /* Temperature Range 1 Control Register */
> > + u32 ttr2cr; /* Temperature Range 2 Control Register */
> > + u32 ttr3cr; /* Temperature Range 3 Control Register */
> > +};
> > +
> > +/*
> > + * Thermal zone data
> > + */
> > +struct qoriq_tmu_data {
> > + struct thermal_zone_device *tz;
> > + struct qoriq_tmu_regs __iomem *regs;
> > + int sensor_id;
> > + bool little_endian;
> > +};
> > +
> > +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem
> > +*addr) {
> > + if (p->little_endian)
> > + iowrite32(val, addr);
> > + else
> > + iowrite32be(val, addr);
> > +}
> > +
> > +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) {
> > + if (p->little_endian)
> > + return ioread32(addr);
> > + else
> > + return ioread32be(addr);
> > +}
> > +
> > +static int tmu_get_temp(void *p, int *temp) {
> > + u32 val;
> > + struct qoriq_tmu_data *data = p;
> > +
> > + val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
> > + *temp = (val & 0xff) * 1000;
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_get_sensor_id(void) {
> > + int ret, id;
> > + struct of_phandle_args sensor_specs;
> > + struct device_node *np, *sensor_np;
> > +
> > + np = of_find_node_by_name(NULL, "thermal-zones");
> > + if (!np)
> > + return -ENODEV;
> > +
> > + sensor_np = of_get_next_child(np, NULL);
> > + ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
> > + "#thermal-sensor-cells",
> > + 0, &sensor_specs);
> > + if (ret) {
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > + return ret;
> > + }
> > +
> > + if (sensor_specs.args_count >= 1) {
> > + id = sensor_specs.args[0];
> > + WARN(sensor_specs.args_count > 1,
> > + "%s: too many cells in sensor specifier %d\n",
> > + sensor_specs.np->name,
> > sensor_specs.args_count);
> > + } else {
> > + id = 0;
> > + }
> > +
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > +
> > + return id;
> > +}
> > +
> > +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> > + int i, val, len;
> > + u32 range[4];
> > + const u32 *calibration;
> > + struct device_node *np = pdev->dev.of_node;
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
> > + dev_err(&pdev->dev, "missing calibration range.\n");
> > + return -ENODEV;
> > + }
> > +
> > + /* Init temperature range registers */
> > + tmu_write(data, range[0], &data->regs->ttr0cr);
> > + tmu_write(data, range[1], &data->regs->ttr1cr);
> > + tmu_write(data, range[2], &data->regs->ttr2cr);
> > + tmu_write(data, range[3], &data->regs->ttr3cr);
> > +
> > + calibration = of_get_property(np, "fsl,tmu-calibration", &len);
> > + if (calibration == NULL || len % 8) {
> > + dev_err(&pdev->dev, "invalid calibration data.\n");
> > + return -ENODEV;
> > + }
> > +
> > + for (i = 0; i < len; i += 8, calibration += 2) {
> > + val = of_read_number(calibration, 1);
> > + tmu_write(data, val, &data->regs->ttcfgr);
> > + val = of_read_number(calibration + 1, 1);
> > + tmu_write(data, val, &data->regs->tscfgr);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> > + /* Disable interrupt, using polling instead */
> > + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> > +
> > + /* Set update_interval */
> > + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr); }
> > +
> > +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> > + .get_temp = tmu_get_temp,
> > +};
> > +
> > +static int qoriq_tmu_probe(struct platform_device *pdev) {
> > + int ret;
> > + const struct thermal_trip *trip;
> > + struct qoriq_tmu_data *data;
> > + struct device_node *np = pdev->dev.of_node;
> > + u32 site = 0;
> > +
> > + if (!np) {
> > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > + return -ENODEV;
> > + }
> > +
> > + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> > + GFP_KERNEL);
> > + if (!data)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, data);
> > +
> > + data->little_endian = of_property_read_bool(np, "little-endian");
> > +
> > + data->sensor_id = qoriq_tmu_get_sensor_id();
> > + if (data->sensor_id < 0) {
> > + dev_err(&pdev->dev, "Failed to get sensor id\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + data->regs = of_iomap(np, 0);
> > + if (!data->regs) {
> > + dev_err(&pdev->dev, "Failed to get memory region\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + qoriq_tmu_init_device(data); /* TMU initialization */
> > +
> > + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> > + if (ret < 0)
> > + goto err_tmu;
> > +
> > + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
> > + data, &tmu_tz_ops);
> > + if (IS_ERR(data->tz)) {
> > + ret = PTR_ERR(data->tz);
> > + dev_err(&pdev->dev,
> > + "Failed to register thermal zone device %d\n", ret);
> > + goto err_tmu;
> > + }
> > +
> > + trip = of_thermal_get_trip_points(data->tz);
> > +
> > + /* Enable monitoring */
> > + site |= 0x1 << (15 - data->sensor_id);
> > + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> > +
> > + return 0;
> > +
> > +err_tmu:
> > + iounmap(data->regs);
> > +
> > +err_iomap:
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return ret;
> > +}
> > +
> > +static int qoriq_tmu_remove(struct platform_device *pdev) {
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +
> > + iounmap(data->regs);
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int qoriq_tmu_suspend(struct device *dev) {
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Disable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr &= ~TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_resume(struct device *dev) {
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Enable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr |= TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> > + qoriq_tmu_suspend, qoriq_tmu_resume);
> > +
> > +static const struct of_device_id qoriq_tmu_match[] = {
> > + { .compatible = "fsl,qoriq-tmu", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> > +
> > +static struct platform_driver qoriq_tmu = {
> > + .driver = {
> > + .name = "qoriq_thermal",
> > + .pm = &qoriq_tmu_pm_ops,
> > + .of_match_table = qoriq_tmu_match,
> > + },
> > + .probe = qoriq_tmu_probe,
> > + .remove = qoriq_tmu_remove,
> > +};
> > +module_platform_driver(qoriq_tmu);
> > +
> > +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> > +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.1.0.27.g96db324
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-08-05 6:26 ` Hongtao Jia
0 siblings, 0 replies; 18+ messages in thread
From: Hongtao Jia @ 2016-08-05 6:26 UTC (permalink / raw)
To: edubezval, rui.zhang, Scott Wood
Cc: devicetree, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-pm
Hi Eduardo,
If you have any comments please let me know.
Thanks.
-Hongtao.
> -----Original Message-----
> From: Linuxppc-dev [mailto:linuxppc-dev-
> bounces+b38951=freescale.com@lists.ozlabs.org] On Behalf Of Hongtao Jia
> Sent: Tuesday, July 19, 2016 2:54 PM
> To: edubezval@gmail.com; rui.zhang@intel.com; Scott Wood
> <scott.wood@nxp.com>
> Cc: devicetree@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-
> kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
> pm@vger.kernel.org
> Subject: RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> Hi Eduardo,
>
> Any comments on this patch?
>
> Thanks.
> -Hongtao.
>
> > -----Original Message-----
> > From: Jia Hongtao [mailto:hongtao.jia@nxp.com]
> > Sent: Thursday, June 30, 2016 11:09 AM
> > To: edubezval@gmail.com; rui.zhang@intel.com; robh+dt@kernel.org;
> > galak@codeaurora.org; Scott Wood <scott.wood@nxp.com>;
> > shawnguo@kernel.org
> > Cc: linux-pm@vger.kernel.org; devicetree@vger.kernel.org; linux-
> > kernel@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-arm-
> > kernel@lists.infradead.org; Hongtao Jia <hongtao.jia@nxp.com>
> > Subject: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
> >
> > This driver add thermal management support by enabling TMU (Thermal
> > Monitoring Unit) on QorIQ platform.
> >
> > It's based on thermal of framework:
> > - Trip points defined in device tree.
> > - Cpufreq as cooling device registered in qoriq cpufreq driver.
> >
> > Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
> > ---
> > Changes of V2:
> > * Add HAS_IOMEM dependency to fix build error on UM
> >
> > drivers/thermal/Kconfig | 10 ++
> > drivers/thermal/Makefile | 1 +
> > drivers/thermal/qoriq_thermal.c | 328
> > ++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 339 insertions(+)
> > create mode 100644 drivers/thermal/qoriq_thermal.c
> >
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> > 2d702ca..56ef30d 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -195,6 +195,16 @@ config IMX_THERMAL
> > cpufreq is used as the cooling device to throttle CPUs when the
> > passive trip is crossed.
> >
> > +config QORIQ_THERMAL
> > + tristate "QorIQ Thermal Monitoring Unit"
> > + depends on THERMAL_OF
> > + depends on HAS_IOMEM
> > + help
> > + Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
> > + It supports one critical trip point and one passive trip point. The
> > + cpufreq is used as the cooling device to throttle CPUs when the
> > + passive trip is crossed.
> > +
> > config SPEAR_THERMAL
> > tristate "SPEAr thermal sensor driver"
> > depends on PLAT_SPEAR || COMPILE_TEST diff --git
> > a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> > 10b07c1..6662232 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> db8500_thermal.o
> > obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> > obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> > obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> > +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> > obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
> > obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> > obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> > diff --git a/drivers/thermal/qoriq_thermal.c
> > b/drivers/thermal/qoriq_thermal.c new file mode 100644 index
> > 0000000..644ba52
> > --- /dev/null
> > +++ b/drivers/thermal/qoriq_thermal.c
> > @@ -0,0 +1,328 @@
> > +/*
> > + * Copyright 2016 Freescale Semiconductor, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but
> > +WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> > +or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> > +License for
> > + * more details.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/err.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/thermal.h>
> > +
> > +#include "thermal_core.h"
> > +
> > +#define SITES_MAX 16
> > +
> > +/*
> > + * QorIQ TMU Registers
> > + */
> > +struct qoriq_tmu_site_regs {
> > + u32 tritsr; /* Immediate Temperature Site Register */
> > + u32 tratsr; /* Average Temperature Site Register */
> > + u8 res0[0x8];
> > +};
> > +
> > +struct qoriq_tmu_regs {
> > + u32 tmr; /* Mode Register */
> > +#define TMR_DISABLE 0x0
> > +#define TMR_ME 0x80000000
> > +#define TMR_ALPF 0x0c000000
> > + u32 tsr; /* Status Register */
> > + u32 tmtmir; /* Temperature measurement interval Register */
> > +#define TMTMIR_DEFAULT 0x0000000f
> > + u8 res0[0x14];
> > + u32 tier; /* Interrupt Enable Register */
> > +#define TIER_DISABLE 0x0
> > + u32 tidr; /* Interrupt Detect Register */
> > + u32 tiscr; /* Interrupt Site Capture Register */
> > + u32 ticscr; /* Interrupt Critical Site Capture Register */
> > + u8 res1[0x10];
> > + u32 tmhtcrh; /* High Temperature Capture Register */
> > + u32 tmhtcrl; /* Low Temperature Capture Register */
> > + u8 res2[0x8];
> > + u32 tmhtitr; /* High Temperature Immediate Threshold */
> > + u32 tmhtatr; /* High Temperature Average Threshold */
> > + u32 tmhtactr; /* High Temperature Average Crit Threshold */
> > + u8 res3[0x24];
> > + u32 ttcfgr; /* Temperature Configuration Register */
> > + u32 tscfgr; /* Sensor Configuration Register */
> > + u8 res4[0x78];
> > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > + u8 res5[0x9f8];
> > + u32 ipbrr0; /* IP Block Revision Register 0 */
> > + u32 ipbrr1; /* IP Block Revision Register 1 */
> > + u8 res6[0x310];
> > + u32 ttr0cr; /* Temperature Range 0 Control Register */
> > + u32 ttr1cr; /* Temperature Range 1 Control Register */
> > + u32 ttr2cr; /* Temperature Range 2 Control Register */
> > + u32 ttr3cr; /* Temperature Range 3 Control Register */
> > +};
> > +
> > +/*
> > + * Thermal zone data
> > + */
> > +struct qoriq_tmu_data {
> > + struct thermal_zone_device *tz;
> > + struct qoriq_tmu_regs __iomem *regs;
> > + int sensor_id;
> > + bool little_endian;
> > +};
> > +
> > +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem
> > +*addr) {
> > + if (p->little_endian)
> > + iowrite32(val, addr);
> > + else
> > + iowrite32be(val, addr);
> > +}
> > +
> > +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) {
> > + if (p->little_endian)
> > + return ioread32(addr);
> > + else
> > + return ioread32be(addr);
> > +}
> > +
> > +static int tmu_get_temp(void *p, int *temp) {
> > + u32 val;
> > + struct qoriq_tmu_data *data = p;
> > +
> > + val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
> > + *temp = (val & 0xff) * 1000;
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_get_sensor_id(void) {
> > + int ret, id;
> > + struct of_phandle_args sensor_specs;
> > + struct device_node *np, *sensor_np;
> > +
> > + np = of_find_node_by_name(NULL, "thermal-zones");
> > + if (!np)
> > + return -ENODEV;
> > +
> > + sensor_np = of_get_next_child(np, NULL);
> > + ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
> > + "#thermal-sensor-cells",
> > + 0, &sensor_specs);
> > + if (ret) {
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > + return ret;
> > + }
> > +
> > + if (sensor_specs.args_count >= 1) {
> > + id = sensor_specs.args[0];
> > + WARN(sensor_specs.args_count > 1,
> > + "%s: too many cells in sensor specifier %d\n",
> > + sensor_specs.np->name,
> > sensor_specs.args_count);
> > + } else {
> > + id = 0;
> > + }
> > +
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > +
> > + return id;
> > +}
> > +
> > +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> > + int i, val, len;
> > + u32 range[4];
> > + const u32 *calibration;
> > + struct device_node *np = pdev->dev.of_node;
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
> > + dev_err(&pdev->dev, "missing calibration range.\n");
> > + return -ENODEV;
> > + }
> > +
> > + /* Init temperature range registers */
> > + tmu_write(data, range[0], &data->regs->ttr0cr);
> > + tmu_write(data, range[1], &data->regs->ttr1cr);
> > + tmu_write(data, range[2], &data->regs->ttr2cr);
> > + tmu_write(data, range[3], &data->regs->ttr3cr);
> > +
> > + calibration = of_get_property(np, "fsl,tmu-calibration", &len);
> > + if (calibration == NULL || len % 8) {
> > + dev_err(&pdev->dev, "invalid calibration data.\n");
> > + return -ENODEV;
> > + }
> > +
> > + for (i = 0; i < len; i += 8, calibration += 2) {
> > + val = of_read_number(calibration, 1);
> > + tmu_write(data, val, &data->regs->ttcfgr);
> > + val = of_read_number(calibration + 1, 1);
> > + tmu_write(data, val, &data->regs->tscfgr);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> > + /* Disable interrupt, using polling instead */
> > + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> > +
> > + /* Set update_interval */
> > + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr); }
> > +
> > +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> > + .get_temp = tmu_get_temp,
> > +};
> > +
> > +static int qoriq_tmu_probe(struct platform_device *pdev) {
> > + int ret;
> > + const struct thermal_trip *trip;
> > + struct qoriq_tmu_data *data;
> > + struct device_node *np = pdev->dev.of_node;
> > + u32 site = 0;
> > +
> > + if (!np) {
> > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > + return -ENODEV;
> > + }
> > +
> > + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> > + GFP_KERNEL);
> > + if (!data)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, data);
> > +
> > + data->little_endian = of_property_read_bool(np, "little-endian");
> > +
> > + data->sensor_id = qoriq_tmu_get_sensor_id();
> > + if (data->sensor_id < 0) {
> > + dev_err(&pdev->dev, "Failed to get sensor id\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + data->regs = of_iomap(np, 0);
> > + if (!data->regs) {
> > + dev_err(&pdev->dev, "Failed to get memory region\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + qoriq_tmu_init_device(data); /* TMU initialization */
> > +
> > + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> > + if (ret < 0)
> > + goto err_tmu;
> > +
> > + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
> > + data, &tmu_tz_ops);
> > + if (IS_ERR(data->tz)) {
> > + ret = PTR_ERR(data->tz);
> > + dev_err(&pdev->dev,
> > + "Failed to register thermal zone device %d\n", ret);
> > + goto err_tmu;
> > + }
> > +
> > + trip = of_thermal_get_trip_points(data->tz);
> > +
> > + /* Enable monitoring */
> > + site |= 0x1 << (15 - data->sensor_id);
> > + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> > +
> > + return 0;
> > +
> > +err_tmu:
> > + iounmap(data->regs);
> > +
> > +err_iomap:
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return ret;
> > +}
> > +
> > +static int qoriq_tmu_remove(struct platform_device *pdev) {
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +
> > + iounmap(data->regs);
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int qoriq_tmu_suspend(struct device *dev) {
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Disable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr &= ~TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_resume(struct device *dev) {
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Enable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr |= TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> > + qoriq_tmu_suspend, qoriq_tmu_resume);
> > +
> > +static const struct of_device_id qoriq_tmu_match[] = {
> > + { .compatible = "fsl,qoriq-tmu", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> > +
> > +static struct platform_driver qoriq_tmu = {
> > + .driver = {
> > + .name = "qoriq_thermal",
> > + .pm = &qoriq_tmu_pm_ops,
> > + .of_match_table = qoriq_tmu_match,
> > + },
> > + .probe = qoriq_tmu_probe,
> > + .remove = qoriq_tmu_remove,
> > +};
> > +module_platform_driver(qoriq_tmu);
> > +
> > +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> > +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.1.0.27.g96db324
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-08-05 6:26 ` Hongtao Jia
0 siblings, 0 replies; 18+ messages in thread
From: Hongtao Jia @ 2016-08-05 6:26 UTC (permalink / raw)
To: edubezval, rui.zhang, Scott Wood
Cc: devicetree, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-pm
SGkgRWR1YXJkbywNCg0KSWYgeW91IGhhdmUgYW55IGNvbW1lbnRzIHBsZWFzZSBsZXQgbWUga25v
dy4NCg0KVGhhbmtzLg0KLUhvbmd0YW8uIA0KDQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0t
LS0NCj4gRnJvbTogTGludXhwcGMtZGV2IFttYWlsdG86bGludXhwcGMtZGV2LQ0KPiBib3VuY2Vz
K2IzODk1MT1mcmVlc2NhbGUuY29tQGxpc3RzLm96bGFicy5vcmddIE9uIEJlaGFsZiBPZiBIb25n
dGFvIEppYQ0KPiBTZW50OiBUdWVzZGF5LCBKdWx5IDE5LCAyMDE2IDI6NTQgUE0NCj4gVG86IGVk
dWJlenZhbEBnbWFpbC5jb207IHJ1aS56aGFuZ0BpbnRlbC5jb207IFNjb3R0IFdvb2QNCj4gPHNj
b3R0Lndvb2RAbnhwLmNvbT4NCj4gQ2M6IGRldmljZXRyZWVAdmdlci5rZXJuZWwub3JnOyBsaW51
eHBwYy1kZXZAbGlzdHMub3psYWJzLm9yZzsgbGludXgtDQo+IGtlcm5lbEB2Z2VyLmtlcm5lbC5v
cmc7IGxpbnV4LWFybS1rZXJuZWxAbGlzdHMuaW5mcmFkZWFkLm9yZzsgbGludXgtDQo+IHBtQHZn
ZXIua2VybmVsLm9yZw0KPiBTdWJqZWN0OiBSRTogW1BBVENIIFYyIDcvN10gdGhlcm1hbDogcW9y
aXE6IEFkZCB0aGVybWFsIG1hbmFnZW1lbnQgc3VwcG9ydA0KPiANCj4gSGkgRWR1YXJkbywNCj4g
DQo+IEFueSBjb21tZW50cyBvbiB0aGlzIHBhdGNoPw0KPiANCj4gVGhhbmtzLg0KPiAtSG9uZ3Rh
by4NCj4gDQo+ID4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gPiBGcm9tOiBKaWEgSG9u
Z3RhbyBbbWFpbHRvOmhvbmd0YW8uamlhQG54cC5jb21dDQo+ID4gU2VudDogVGh1cnNkYXksIEp1
bmUgMzAsIDIwMTYgMTE6MDkgQU0NCj4gPiBUbzogZWR1YmV6dmFsQGdtYWlsLmNvbTsgcnVpLnpo
YW5nQGludGVsLmNvbTsgcm9iaCtkdEBrZXJuZWwub3JnOw0KPiA+IGdhbGFrQGNvZGVhdXJvcmEu
b3JnOyBTY290dCBXb29kIDxzY290dC53b29kQG54cC5jb20+Ow0KPiA+IHNoYXduZ3VvQGtlcm5l
bC5vcmcNCj4gPiBDYzogbGludXgtcG1Admdlci5rZXJuZWwub3JnOyBkZXZpY2V0cmVlQHZnZXIu
a2VybmVsLm9yZzsgbGludXgtDQo+ID4ga2VybmVsQHZnZXIua2VybmVsLm9yZzsgbGludXhwcGMt
ZGV2QGxpc3RzLm96bGFicy5vcmc7IGxpbnV4LWFybS0NCj4gPiBrZXJuZWxAbGlzdHMuaW5mcmFk
ZWFkLm9yZzsgSG9uZ3RhbyBKaWEgPGhvbmd0YW8uamlhQG54cC5jb20+DQo+ID4gU3ViamVjdDog
W1BBVENIIFYyIDcvN10gdGhlcm1hbDogcW9yaXE6IEFkZCB0aGVybWFsIG1hbmFnZW1lbnQgc3Vw
cG9ydA0KPiA+DQo+ID4gVGhpcyBkcml2ZXIgYWRkIHRoZXJtYWwgbWFuYWdlbWVudCBzdXBwb3J0
IGJ5IGVuYWJsaW5nIFRNVSAoVGhlcm1hbA0KPiA+IE1vbml0b3JpbmcgVW5pdCkgb24gUW9ySVEg
cGxhdGZvcm0uDQo+ID4NCj4gPiBJdCdzIGJhc2VkIG9uIHRoZXJtYWwgb2YgZnJhbWV3b3JrOg0K
PiA+IC0gVHJpcCBwb2ludHMgZGVmaW5lZCBpbiBkZXZpY2UgdHJlZS4NCj4gPiAtIENwdWZyZXEg
YXMgY29vbGluZyBkZXZpY2UgcmVnaXN0ZXJlZCBpbiBxb3JpcSBjcHVmcmVxIGRyaXZlci4NCj4g
Pg0KPiA+IFNpZ25lZC1vZmYtYnk6IEppYSBIb25ndGFvIDxob25ndGFvLmppYUBueHAuY29tPg0K
PiA+IC0tLQ0KPiA+IENoYW5nZXMgb2YgVjI6DQo+ID4gKiBBZGQgSEFTX0lPTUVNIGRlcGVuZGVu
Y3kgdG8gZml4IGJ1aWxkIGVycm9yIG9uIFVNDQo+ID4NCj4gPiAgZHJpdmVycy90aGVybWFsL0tj
b25maWcgICAgICAgICB8ICAxMCArKw0KPiA+ICBkcml2ZXJzL3RoZXJtYWwvTWFrZWZpbGUgICAg
ICAgIHwgICAxICsNCj4gPiAgZHJpdmVycy90aGVybWFsL3FvcmlxX3RoZXJtYWwuYyB8IDMyOA0K
PiA+ICsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysNCj4gPiAgMyBmaWxl
cyBjaGFuZ2VkLCAzMzkgaW5zZXJ0aW9ucygrKQ0KPiA+ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJp
dmVycy90aGVybWFsL3FvcmlxX3RoZXJtYWwuYw0KPiA+DQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZl
cnMvdGhlcm1hbC9LY29uZmlnIGIvZHJpdmVycy90aGVybWFsL0tjb25maWcgaW5kZXgNCj4gPiAy
ZDcwMmNhLi41NmVmMzBkIDEwMDY0NA0KPiA+IC0tLSBhL2RyaXZlcnMvdGhlcm1hbC9LY29uZmln
DQo+ID4gKysrIGIvZHJpdmVycy90aGVybWFsL0tjb25maWcNCj4gPiBAQCAtMTk1LDYgKzE5NSwx
NiBAQCBjb25maWcgSU1YX1RIRVJNQUwNCj4gPiAgCSAgY3B1ZnJlcSBpcyB1c2VkIGFzIHRoZSBj
b29saW5nIGRldmljZSB0byB0aHJvdHRsZSBDUFVzIHdoZW4gdGhlDQo+ID4gIAkgIHBhc3NpdmUg
dHJpcCBpcyBjcm9zc2VkLg0KPiA+DQo+ID4gK2NvbmZpZyBRT1JJUV9USEVSTUFMDQo+ID4gKwl0
cmlzdGF0ZSAiUW9ySVEgVGhlcm1hbCBNb25pdG9yaW5nIFVuaXQiDQo+ID4gKwlkZXBlbmRzIG9u
IFRIRVJNQUxfT0YNCj4gPiArCWRlcGVuZHMgb24gSEFTX0lPTUVNDQo+ID4gKwloZWxwDQo+ID4g
KwkgIFN1cHBvcnQgZm9yIFRoZXJtYWwgTW9uaXRvcmluZyBVbml0IChUTVUpIGZvdW5kIG9uIFFv
cklRIHBsYXRmb3Jtcy4NCj4gPiArCSAgSXQgc3VwcG9ydHMgb25lIGNyaXRpY2FsIHRyaXAgcG9p
bnQgYW5kIG9uZSBwYXNzaXZlIHRyaXAgcG9pbnQuIFRoZQ0KPiA+ICsJICBjcHVmcmVxIGlzIHVz
ZWQgYXMgdGhlIGNvb2xpbmcgZGV2aWNlIHRvIHRocm90dGxlIENQVXMgd2hlbiB0aGUNCj4gPiAr
CSAgcGFzc2l2ZSB0cmlwIGlzIGNyb3NzZWQuDQo+ID4gKw0KPiA+ICBjb25maWcgU1BFQVJfVEhF
Uk1BTA0KPiA+ICAJdHJpc3RhdGUgIlNQRUFyIHRoZXJtYWwgc2Vuc29yIGRyaXZlciINCj4gPiAg
CWRlcGVuZHMgb24gUExBVF9TUEVBUiB8fCBDT01QSUxFX1RFU1QgZGlmZiAtLWdpdA0KPiA+IGEv
ZHJpdmVycy90aGVybWFsL01ha2VmaWxlIGIvZHJpdmVycy90aGVybWFsL01ha2VmaWxlIGluZGV4
DQo+ID4gMTBiMDdjMS4uNjY2MjIzMiAxMDA2NDQNCj4gPiAtLS0gYS9kcml2ZXJzL3RoZXJtYWwv
TWFrZWZpbGUNCj4gPiArKysgYi9kcml2ZXJzL3RoZXJtYWwvTWFrZWZpbGUNCj4gPiBAQCAtMzcs
NiArMzcsNyBAQCBvYmotJChDT05GSUdfREI4NTAwX1RIRVJNQUwpCSs9DQo+IGRiODUwMF90aGVy
bWFsLm8NCj4gPiAgb2JqLSQoQ09ORklHX0FSTUFEQV9USEVSTUFMKQkrPSBhcm1hZGFfdGhlcm1h
bC5vDQo+ID4gIG9iai0kKENPTkZJR19UQU5HT19USEVSTUFMKQkrPSB0YW5nb190aGVybWFsLm8N
Cj4gPiAgb2JqLSQoQ09ORklHX0lNWF9USEVSTUFMKQkrPSBpbXhfdGhlcm1hbC5vDQo+ID4gK29i
ai0kKENPTkZJR19RT1JJUV9USEVSTUFMKQkrPSBxb3JpcV90aGVybWFsLm8NCj4gPiAgb2JqLSQo
Q09ORklHX0RCODUwMF9DUFVGUkVRX0NPT0xJTkcpCSs9IGRiODUwMF9jcHVmcmVxX2Nvb2xpbmcu
bw0KPiA+ICBvYmotJChDT05GSUdfSU5URUxfUE9XRVJDTEFNUCkJKz0gaW50ZWxfcG93ZXJjbGFt
cC5vDQo+ID4gIG9iai0kKENPTkZJR19YODZfUEtHX1RFTVBfVEhFUk1BTCkJKz0geDg2X3BrZ190
ZW1wX3RoZXJtYWwubw0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3RoZXJtYWwvcW9yaXFfdGhl
cm1hbC5jDQo+ID4gYi9kcml2ZXJzL3RoZXJtYWwvcW9yaXFfdGhlcm1hbC5jIG5ldyBmaWxlIG1v
ZGUgMTAwNjQ0IGluZGV4DQo+ID4gMDAwMDAwMC4uNjQ0YmE1Mg0KPiA+IC0tLSAvZGV2L251bGwN
Cj4gPiArKysgYi9kcml2ZXJzL3RoZXJtYWwvcW9yaXFfdGhlcm1hbC5jDQo+ID4gQEAgLTAsMCAr
MSwzMjggQEANCj4gPiArLyoNCj4gPiArICogQ29weXJpZ2h0IDIwMTYgRnJlZXNjYWxlIFNlbWlj
b25kdWN0b3IsIEluYy4NCj4gPiArICoNCj4gPiArICogVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29m
dHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcg0KPiA+ICttb2RpZnkgaXQNCj4g
PiArICogdW5kZXIgdGhlIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHRoZSBHTlUgR2VuZXJhbCBQ
dWJsaWMgTGljZW5zZSwNCj4gPiArICogdmVyc2lvbiAyLCBhcyBwdWJsaXNoZWQgYnkgdGhlIEZy
ZWUgU29mdHdhcmUgRm91bmRhdGlvbi4NCj4gPiArICoNCj4gPiArICogVGhpcyBwcm9ncmFtIGlz
IGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIGl0IHdpbGwgYmUgdXNlZnVsLCBidXQNCj4gPiArV0lU
SE9VVA0KPiA+ICsgKiBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJy
YW50eSBvZiBNRVJDSEFOVEFCSUxJVFkNCj4gPiArb3INCj4gPiArICogRklUTkVTUyBGT1IgQSBQ
QVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYw0KPiA+ICtMaWNl
bnNlIGZvcg0KPiA+ICsgKiBtb3JlIGRldGFpbHMuDQo+ID4gKyAqDQo+ID4gKyAqLw0KPiA+ICsN
Cj4gPiArI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPg0KPiA+ICsjaW5jbHVkZSA8bGludXgvcGxh
dGZvcm1fZGV2aWNlLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9lcnIuaD4NCj4gPiArI2luY2x1
ZGUgPGxpbnV4L2lvLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9vZi5oPg0KPiA+ICsjaW5jbHVk
ZSA8bGludXgvb2ZfYWRkcmVzcy5oPg0KPiA+ICsjaW5jbHVkZSA8bGludXgvdGhlcm1hbC5oPg0K
PiA+ICsNCj4gPiArI2luY2x1ZGUgInRoZXJtYWxfY29yZS5oIg0KPiA+ICsNCj4gPiArI2RlZmlu
ZSBTSVRFU19NQVgJMTYNCj4gPiArDQo+ID4gKy8qDQo+ID4gKyAqIFFvcklRIFRNVSBSZWdpc3Rl
cnMNCj4gPiArICovDQo+ID4gK3N0cnVjdCBxb3JpcV90bXVfc2l0ZV9yZWdzIHsNCj4gPiArCXUz
MiB0cml0c3I7CQkvKiBJbW1lZGlhdGUgVGVtcGVyYXR1cmUgU2l0ZSBSZWdpc3RlciAqLw0KPiA+
ICsJdTMyIHRyYXRzcjsJCS8qIEF2ZXJhZ2UgVGVtcGVyYXR1cmUgU2l0ZSBSZWdpc3RlciAqLw0K
PiA+ICsJdTggcmVzMFsweDhdOw0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArc3RydWN0IHFvcmlxX3Rt
dV9yZWdzIHsNCj4gPiArCXUzMiB0bXI7CQkvKiBNb2RlIFJlZ2lzdGVyICovDQo+ID4gKyNkZWZp
bmUgVE1SX0RJU0FCTEUJMHgwDQo+ID4gKyNkZWZpbmUgVE1SX01FCQkweDgwMDAwMDAwDQo+ID4g
KyNkZWZpbmUgVE1SX0FMUEYJMHgwYzAwMDAwMA0KPiA+ICsJdTMyIHRzcjsJCS8qIFN0YXR1cyBS
ZWdpc3RlciAqLw0KPiA+ICsJdTMyIHRtdG1pcjsJCS8qIFRlbXBlcmF0dXJlIG1lYXN1cmVtZW50
IGludGVydmFsIFJlZ2lzdGVyICovDQo+ID4gKyNkZWZpbmUgVE1UTUlSX0RFRkFVTFQJMHgwMDAw
MDAwZg0KPiA+ICsJdTggcmVzMFsweDE0XTsNCj4gPiArCXUzMiB0aWVyOwkJLyogSW50ZXJydXB0
IEVuYWJsZSBSZWdpc3RlciAqLw0KPiA+ICsjZGVmaW5lIFRJRVJfRElTQUJMRQkweDANCj4gPiAr
CXUzMiB0aWRyOwkJLyogSW50ZXJydXB0IERldGVjdCBSZWdpc3RlciAqLw0KPiA+ICsJdTMyIHRp
c2NyOwkJLyogSW50ZXJydXB0IFNpdGUgQ2FwdHVyZSBSZWdpc3RlciAqLw0KPiA+ICsJdTMyIHRp
Y3NjcjsJCS8qIEludGVycnVwdCBDcml0aWNhbCBTaXRlIENhcHR1cmUgUmVnaXN0ZXIgKi8NCj4g
PiArCXU4IHJlczFbMHgxMF07DQo+ID4gKwl1MzIgdG1odGNyaDsJCS8qIEhpZ2ggVGVtcGVyYXR1
cmUgQ2FwdHVyZSBSZWdpc3RlciAqLw0KPiA+ICsJdTMyIHRtaHRjcmw7CQkvKiBMb3cgVGVtcGVy
YXR1cmUgQ2FwdHVyZSBSZWdpc3RlciAqLw0KPiA+ICsJdTggcmVzMlsweDhdOw0KPiA+ICsJdTMy
IHRtaHRpdHI7CQkvKiBIaWdoIFRlbXBlcmF0dXJlIEltbWVkaWF0ZSBUaHJlc2hvbGQgKi8NCj4g
PiArCXUzMiB0bWh0YXRyOwkJLyogSGlnaCBUZW1wZXJhdHVyZSBBdmVyYWdlIFRocmVzaG9sZCAq
Lw0KPiA+ICsJdTMyIHRtaHRhY3RyOwkvKiBIaWdoIFRlbXBlcmF0dXJlIEF2ZXJhZ2UgQ3JpdCBU
aHJlc2hvbGQgKi8NCj4gPiArCXU4IHJlczNbMHgyNF07DQo+ID4gKwl1MzIgdHRjZmdyOwkJLyog
VGVtcGVyYXR1cmUgQ29uZmlndXJhdGlvbiBSZWdpc3RlciAqLw0KPiA+ICsJdTMyIHRzY2ZncjsJ
CS8qIFNlbnNvciBDb25maWd1cmF0aW9uIFJlZ2lzdGVyICovDQo+ID4gKwl1OCByZXM0WzB4Nzhd
Ow0KPiA+ICsJc3RydWN0IHFvcmlxX3RtdV9zaXRlX3JlZ3Mgc2l0ZVtTSVRFU19NQVhdOw0KPiA+
ICsJdTggcmVzNVsweDlmOF07DQo+ID4gKwl1MzIgaXBicnIwOwkJLyogSVAgQmxvY2sgUmV2aXNp
b24gUmVnaXN0ZXIgMCAqLw0KPiA+ICsJdTMyIGlwYnJyMTsJCS8qIElQIEJsb2NrIFJldmlzaW9u
IFJlZ2lzdGVyIDEgKi8NCj4gPiArCXU4IHJlczZbMHgzMTBdOw0KPiA+ICsJdTMyIHR0cjBjcjsJ
CS8qIFRlbXBlcmF0dXJlIFJhbmdlIDAgQ29udHJvbCBSZWdpc3RlciAqLw0KPiA+ICsJdTMyIHR0
cjFjcjsJCS8qIFRlbXBlcmF0dXJlIFJhbmdlIDEgQ29udHJvbCBSZWdpc3RlciAqLw0KPiA+ICsJ
dTMyIHR0cjJjcjsJCS8qIFRlbXBlcmF0dXJlIFJhbmdlIDIgQ29udHJvbCBSZWdpc3RlciAqLw0K
PiA+ICsJdTMyIHR0cjNjcjsJCS8qIFRlbXBlcmF0dXJlIFJhbmdlIDMgQ29udHJvbCBSZWdpc3Rl
ciAqLw0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArLyoNCj4gPiArICogVGhlcm1hbCB6b25lIGRhdGEN
Cj4gPiArICovDQo+ID4gK3N0cnVjdCBxb3JpcV90bXVfZGF0YSB7DQo+ID4gKwlzdHJ1Y3QgdGhl
cm1hbF96b25lX2RldmljZSAqdHo7DQo+ID4gKwlzdHJ1Y3QgcW9yaXFfdG11X3JlZ3MgX19pb21l
bSAqcmVnczsNCj4gPiArCWludCBzZW5zb3JfaWQ7DQo+ID4gKwlib29sIGxpdHRsZV9lbmRpYW47
DQo+ID4gK307DQo+ID4gKw0KPiA+ICtzdGF0aWMgdm9pZCB0bXVfd3JpdGUoc3RydWN0IHFvcmlx
X3RtdV9kYXRhICpwLCB1MzIgdmFsLCB2b2lkIF9faW9tZW0NCj4gPiArKmFkZHIpIHsNCj4gPiAr
CWlmIChwLT5saXR0bGVfZW5kaWFuKQ0KPiA+ICsJCWlvd3JpdGUzMih2YWwsIGFkZHIpOw0KPiA+
ICsJZWxzZQ0KPiA+ICsJCWlvd3JpdGUzMmJlKHZhbCwgYWRkcik7DQo+ID4gK30NCj4gPiArDQo+
ID4gK3N0YXRpYyB1MzIgdG11X3JlYWQoc3RydWN0IHFvcmlxX3RtdV9kYXRhICpwLCB2b2lkIF9f
aW9tZW0gKmFkZHIpIHsNCj4gPiArCWlmIChwLT5saXR0bGVfZW5kaWFuKQ0KPiA+ICsJCXJldHVy
biBpb3JlYWQzMihhZGRyKTsNCj4gPiArCWVsc2UNCj4gPiArCQlyZXR1cm4gaW9yZWFkMzJiZShh
ZGRyKTsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIGludCB0bXVfZ2V0X3RlbXAodm9pZCAq
cCwgaW50ICp0ZW1wKSB7DQo+ID4gKwl1MzIgdmFsOw0KPiA+ICsJc3RydWN0IHFvcmlxX3RtdV9k
YXRhICpkYXRhID0gcDsNCj4gPiArDQo+ID4gKwl2YWwgPSB0bXVfcmVhZChkYXRhLCAmZGF0YS0+
cmVncy0+c2l0ZVtkYXRhLT5zZW5zb3JfaWRdLnRyaXRzcik7DQo+ID4gKwkqdGVtcCA9ICh2YWwg
JiAweGZmKSAqIDEwMDA7DQo+ID4gKw0KPiA+ICsJcmV0dXJuIDA7DQo+ID4gK30NCj4gPiArDQo+
ID4gK3N0YXRpYyBpbnQgcW9yaXFfdG11X2dldF9zZW5zb3JfaWQodm9pZCkgew0KPiA+ICsJaW50
IHJldCwgaWQ7DQo+ID4gKwlzdHJ1Y3Qgb2ZfcGhhbmRsZV9hcmdzIHNlbnNvcl9zcGVjczsNCj4g
PiArCXN0cnVjdCBkZXZpY2Vfbm9kZSAqbnAsICpzZW5zb3JfbnA7DQo+ID4gKw0KPiA+ICsJbnAg
PSBvZl9maW5kX25vZGVfYnlfbmFtZShOVUxMLCAidGhlcm1hbC16b25lcyIpOw0KPiA+ICsJaWYg
KCFucCkNCj4gPiArCQlyZXR1cm4gLUVOT0RFVjsNCj4gPiArDQo+ID4gKwlzZW5zb3JfbnAgPSBv
Zl9nZXRfbmV4dF9jaGlsZChucCwgTlVMTCk7DQo+ID4gKwlyZXQgPSBvZl9wYXJzZV9waGFuZGxl
X3dpdGhfYXJncyhzZW5zb3JfbnAsICJ0aGVybWFsLXNlbnNvcnMiLA0KPiA+ICsJCQkiI3RoZXJt
YWwtc2Vuc29yLWNlbGxzIiwNCj4gPiArCQkJMCwgJnNlbnNvcl9zcGVjcyk7DQo+ID4gKwlpZiAo
cmV0KSB7DQo+ID4gKwkJb2Zfbm9kZV9wdXQobnApOw0KPiA+ICsJCW9mX25vZGVfcHV0KHNlbnNv
cl9ucCk7DQo+ID4gKwkJcmV0dXJuIHJldDsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlpZiAoc2Vu
c29yX3NwZWNzLmFyZ3NfY291bnQgPj0gMSkgew0KPiA+ICsJCWlkID0gc2Vuc29yX3NwZWNzLmFy
Z3NbMF07DQo+ID4gKwkJV0FSTihzZW5zb3Jfc3BlY3MuYXJnc19jb3VudCA+IDEsDQo+ID4gKwkJ
CQkiJXM6IHRvbyBtYW55IGNlbGxzIGluIHNlbnNvciBzcGVjaWZpZXIgJWRcbiIsDQo+ID4gKwkJ
CQlzZW5zb3Jfc3BlY3MubnAtPm5hbWUsDQo+ID4gc2Vuc29yX3NwZWNzLmFyZ3NfY291bnQpOw0K
PiA+ICsJfSBlbHNlIHsNCj4gPiArCQlpZCA9IDA7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJb2Zf
bm9kZV9wdXQobnApOw0KPiA+ICsJb2Zfbm9kZV9wdXQoc2Vuc29yX25wKTsNCj4gPiArDQo+ID4g
KwlyZXR1cm4gaWQ7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgcW9yaXFfdG11X2Nh
bGlicmF0aW9uKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpIHsNCj4gPiArCWludCBpLCB2
YWwsIGxlbjsNCj4gPiArCXUzMiByYW5nZVs0XTsNCj4gPiArCWNvbnN0IHUzMiAqY2FsaWJyYXRp
b247DQo+ID4gKwlzdHJ1Y3QgZGV2aWNlX25vZGUgKm5wID0gcGRldi0+ZGV2Lm9mX25vZGU7DQo+
ID4gKwlzdHJ1Y3QgcW9yaXFfdG11X2RhdGEgKmRhdGEgPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0YShw
ZGV2KTsNCj4gPiArDQo+ID4gKwlpZiAob2ZfcHJvcGVydHlfcmVhZF91MzJfYXJyYXkobnAsICJm
c2wsdG11LXJhbmdlIiwgcmFuZ2UsIDQpKSB7DQo+ID4gKwkJZGV2X2VycigmcGRldi0+ZGV2LCAi
bWlzc2luZyBjYWxpYnJhdGlvbiByYW5nZS5cbiIpOw0KPiA+ICsJCXJldHVybiAtRU5PREVWOw0K
PiA+ICsJfQ0KPiA+ICsNCj4gPiArCS8qIEluaXQgdGVtcGVyYXR1cmUgcmFuZ2UgcmVnaXN0ZXJz
ICovDQo+ID4gKwl0bXVfd3JpdGUoZGF0YSwgcmFuZ2VbMF0sICZkYXRhLT5yZWdzLT50dHIwY3Ip
Ow0KPiA+ICsJdG11X3dyaXRlKGRhdGEsIHJhbmdlWzFdLCAmZGF0YS0+cmVncy0+dHRyMWNyKTsN
Cj4gPiArCXRtdV93cml0ZShkYXRhLCByYW5nZVsyXSwgJmRhdGEtPnJlZ3MtPnR0cjJjcik7DQo+
ID4gKwl0bXVfd3JpdGUoZGF0YSwgcmFuZ2VbM10sICZkYXRhLT5yZWdzLT50dHIzY3IpOw0KPiA+
ICsNCj4gPiArCWNhbGlicmF0aW9uID0gb2ZfZ2V0X3Byb3BlcnR5KG5wLCAiZnNsLHRtdS1jYWxp
YnJhdGlvbiIsICZsZW4pOw0KPiA+ICsJaWYgKGNhbGlicmF0aW9uID09IE5VTEwgfHwgbGVuICUg
OCkgew0KPiA+ICsJCWRldl9lcnIoJnBkZXYtPmRldiwgImludmFsaWQgY2FsaWJyYXRpb24gZGF0
YS5cbiIpOw0KPiA+ICsJCXJldHVybiAtRU5PREVWOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCWZv
ciAoaSA9IDA7IGkgPCBsZW47IGkgKz0gOCwgY2FsaWJyYXRpb24gKz0gMikgew0KPiA+ICsJCXZh
bCA9IG9mX3JlYWRfbnVtYmVyKGNhbGlicmF0aW9uLCAxKTsNCj4gPiArCQl0bXVfd3JpdGUoZGF0
YSwgdmFsLCAmZGF0YS0+cmVncy0+dHRjZmdyKTsNCj4gPiArCQl2YWwgPSBvZl9yZWFkX251bWJl
cihjYWxpYnJhdGlvbiArIDEsIDEpOw0KPiA+ICsJCXRtdV93cml0ZShkYXRhLCB2YWwsICZkYXRh
LT5yZWdzLT50c2NmZ3IpOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9
DQo+ID4gKw0KPiA+ICtzdGF0aWMgdm9pZCBxb3JpcV90bXVfaW5pdF9kZXZpY2Uoc3RydWN0IHFv
cmlxX3RtdV9kYXRhICpkYXRhKSB7DQo+ID4gKwkvKiBEaXNhYmxlIGludGVycnVwdCwgdXNpbmcg
cG9sbGluZyBpbnN0ZWFkICovDQo+ID4gKwl0bXVfd3JpdGUoZGF0YSwgVElFUl9ESVNBQkxFLCAm
ZGF0YS0+cmVncy0+dGllcik7DQo+ID4gKw0KPiA+ICsJLyogU2V0IHVwZGF0ZV9pbnRlcnZhbCAq
Lw0KPiA+ICsJdG11X3dyaXRlKGRhdGEsIFRNVE1JUl9ERUZBVUxULCAmZGF0YS0+cmVncy0+dG10
bWlyKTsNCj4gPiArDQo+ID4gKwkvKiBEaXNhYmxlIG1vbml0b3JpbmcgKi8NCj4gPiArCXRtdV93
cml0ZShkYXRhLCBUTVJfRElTQUJMRSwgJmRhdGEtPnJlZ3MtPnRtcik7IH0NCj4gPiArDQo+ID4g
K3N0YXRpYyBzdHJ1Y3QgdGhlcm1hbF96b25lX29mX2RldmljZV9vcHMgdG11X3R6X29wcyA9IHsN
Cj4gPiArCS5nZXRfdGVtcCA9IHRtdV9nZXRfdGVtcCwNCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0
YXRpYyBpbnQgcW9yaXFfdG11X3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpIHsN
Cj4gPiArCWludCByZXQ7DQo+ID4gKwljb25zdCBzdHJ1Y3QgdGhlcm1hbF90cmlwICp0cmlwOw0K
PiA+ICsJc3RydWN0IHFvcmlxX3RtdV9kYXRhICpkYXRhOw0KPiA+ICsJc3RydWN0IGRldmljZV9u
b2RlICpucCA9IHBkZXYtPmRldi5vZl9ub2RlOw0KPiA+ICsJdTMyIHNpdGUgPSAwOw0KPiA+ICsN
Cj4gPiArCWlmICghbnApIHsNCj4gPiArCQlkZXZfZXJyKCZwZGV2LT5kZXYsICJEZXZpY2UgT0Yt
Tm9kZSBpcyBOVUxMIik7DQo+ID4gKwkJcmV0dXJuIC1FTk9ERVY7DQo+ID4gKwl9DQo+ID4gKw0K
PiA+ICsJZGF0YSA9IGRldm1fa3phbGxvYygmcGRldi0+ZGV2LCBzaXplb2Yoc3RydWN0IHFvcmlx
X3RtdV9kYXRhKSwNCj4gPiArCQkJICAgIEdGUF9LRVJORUwpOw0KPiA+ICsJaWYgKCFkYXRhKQ0K
PiA+ICsJCXJldHVybiAtRU5PTUVNOw0KPiA+ICsNCj4gPiArCXBsYXRmb3JtX3NldF9kcnZkYXRh
KHBkZXYsIGRhdGEpOw0KPiA+ICsNCj4gPiArCWRhdGEtPmxpdHRsZV9lbmRpYW4gPSBvZl9wcm9w
ZXJ0eV9yZWFkX2Jvb2wobnAsICJsaXR0bGUtZW5kaWFuIik7DQo+ID4gKw0KPiA+ICsJZGF0YS0+
c2Vuc29yX2lkID0gcW9yaXFfdG11X2dldF9zZW5zb3JfaWQoKTsNCj4gPiArCWlmIChkYXRhLT5z
ZW5zb3JfaWQgPCAwKSB7DQo+ID4gKwkJZGV2X2VycigmcGRldi0+ZGV2LCAiRmFpbGVkIHRvIGdl
dCBzZW5zb3IgaWRcbiIpOw0KPiA+ICsJCXJldCA9IC1FTk9ERVY7DQo+ID4gKwkJZ290byBlcnJf
aW9tYXA7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJZGF0YS0+cmVncyA9IG9mX2lvbWFwKG5wLCAw
KTsNCj4gPiArCWlmICghZGF0YS0+cmVncykgew0KPiA+ICsJCWRldl9lcnIoJnBkZXYtPmRldiwg
IkZhaWxlZCB0byBnZXQgbWVtb3J5IHJlZ2lvblxuIik7DQo+ID4gKwkJcmV0ID0gLUVOT0RFVjsN
Cj4gPiArCQlnb3RvIGVycl9pb21hcDsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlxb3JpcV90bXVf
aW5pdF9kZXZpY2UoZGF0YSk7CS8qIFRNVSBpbml0aWFsaXphdGlvbiAqLw0KPiA+ICsNCj4gPiAr
CXJldCA9IHFvcmlxX3RtdV9jYWxpYnJhdGlvbihwZGV2KTsJLyogVE1VIGNhbGlicmF0aW9uICov
DQo+ID4gKwlpZiAocmV0IDwgMCkNCj4gPiArCQlnb3RvIGVycl90bXU7DQo+ID4gKw0KPiA+ICsJ
ZGF0YS0+dHogPSB0aGVybWFsX3pvbmVfb2Zfc2Vuc29yX3JlZ2lzdGVyKCZwZGV2LT5kZXYsIGRh
dGEtPnNlbnNvcl9pZCwNCj4gPiArCQkJCWRhdGEsICZ0bXVfdHpfb3BzKTsNCj4gPiArCWlmIChJ
U19FUlIoZGF0YS0+dHopKSB7DQo+ID4gKwkJcmV0ID0gUFRSX0VSUihkYXRhLT50eik7DQo+ID4g
KwkJZGV2X2VycigmcGRldi0+ZGV2LA0KPiA+ICsJCQkiRmFpbGVkIHRvIHJlZ2lzdGVyIHRoZXJt
YWwgem9uZSBkZXZpY2UgJWRcbiIsIHJldCk7DQo+ID4gKwkJZ290byBlcnJfdG11Ow0KPiA+ICsJ
fQ0KPiA+ICsNCj4gPiArCXRyaXAgPSBvZl90aGVybWFsX2dldF90cmlwX3BvaW50cyhkYXRhLT50
eik7DQo+ID4gKw0KPiA+ICsJLyogRW5hYmxlIG1vbml0b3JpbmcgKi8NCj4gPiArCXNpdGUgfD0g
MHgxIDw8ICgxNSAtIGRhdGEtPnNlbnNvcl9pZCk7DQo+ID4gKwl0bXVfd3JpdGUoZGF0YSwgc2l0
ZSB8IFRNUl9NRSB8IFRNUl9BTFBGLCAmZGF0YS0+cmVncy0+dG1yKTsNCj4gPiArDQo+ID4gKwly
ZXR1cm4gMDsNCj4gPiArDQo+ID4gK2Vycl90bXU6DQo+ID4gKwlpb3VubWFwKGRhdGEtPnJlZ3Mp
Ow0KPiA+ICsNCj4gPiArZXJyX2lvbWFwOg0KPiA+ICsJcGxhdGZvcm1fc2V0X2RydmRhdGEocGRl
diwgTlVMTCk7DQo+ID4gKw0KPiA+ICsJcmV0dXJuIHJldDsNCj4gPiArfQ0KPiA+ICsNCj4gPiAr
c3RhdGljIGludCBxb3JpcV90bXVfcmVtb3ZlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYp
IHsNCj4gPiArCXN0cnVjdCBxb3JpcV90bXVfZGF0YSAqZGF0YSA9IHBsYXRmb3JtX2dldF9kcnZk
YXRhKHBkZXYpOw0KPiA+ICsNCj4gPiArCXRoZXJtYWxfem9uZV9vZl9zZW5zb3JfdW5yZWdpc3Rl
cigmcGRldi0+ZGV2LCBkYXRhLT50eik7DQo+ID4gKw0KPiA+ICsJLyogRGlzYWJsZSBtb25pdG9y
aW5nICovDQo+ID4gKwl0bXVfd3JpdGUoZGF0YSwgVE1SX0RJU0FCTEUsICZkYXRhLT5yZWdzLT50
bXIpOw0KPiA+ICsNCj4gPiArCWlvdW5tYXAoZGF0YS0+cmVncyk7DQo+ID4gKwlwbGF0Zm9ybV9z
ZXRfZHJ2ZGF0YShwZGV2LCBOVUxMKTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gMDsNCj4gPiArfQ0K
PiA+ICsNCj4gPiArI2lmZGVmIENPTkZJR19QTV9TTEVFUA0KPiA+ICtzdGF0aWMgaW50IHFvcmlx
X3RtdV9zdXNwZW5kKHN0cnVjdCBkZXZpY2UgKmRldikgew0KPiA+ICsJdTMyIHRtcjsNCj4gPiAr
CXN0cnVjdCBxb3JpcV90bXVfZGF0YSAqZGF0YSA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+
ICsNCj4gPiArCS8qIERpc2FibGUgbW9uaXRvcmluZyAqLw0KPiA+ICsJdG1yID0gdG11X3JlYWQo
ZGF0YSwgJmRhdGEtPnJlZ3MtPnRtcik7DQo+ID4gKwl0bXIgJj0gflRNUl9NRTsNCj4gPiArCXRt
dV93cml0ZShkYXRhLCB0bXIsICZkYXRhLT5yZWdzLT50bXIpOw0KPiA+ICsNCj4gPiArCXJldHVy
biAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IHFvcmlxX3RtdV9yZXN1bWUoc3Ry
dWN0IGRldmljZSAqZGV2KSB7DQo+ID4gKwl1MzIgdG1yOw0KPiA+ICsJc3RydWN0IHFvcmlxX3Rt
dV9kYXRhICpkYXRhID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4gKw0KPiA+ICsJLyogRW5h
YmxlIG1vbml0b3JpbmcgKi8NCj4gPiArCXRtciA9IHRtdV9yZWFkKGRhdGEsICZkYXRhLT5yZWdz
LT50bXIpOw0KPiA+ICsJdG1yIHw9IFRNUl9NRTsNCj4gPiArCXRtdV93cml0ZShkYXRhLCB0bXIs
ICZkYXRhLT5yZWdzLT50bXIpOw0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4g
KyNlbmRpZg0KPiA+ICsNCj4gPiArc3RhdGljIFNJTVBMRV9ERVZfUE1fT1BTKHFvcmlxX3RtdV9w
bV9vcHMsDQo+ID4gKwkJCSBxb3JpcV90bXVfc3VzcGVuZCwgcW9yaXFfdG11X3Jlc3VtZSk7DQo+
ID4gKw0KPiA+ICtzdGF0aWMgY29uc3Qgc3RydWN0IG9mX2RldmljZV9pZCBxb3JpcV90bXVfbWF0
Y2hbXSA9IHsNCj4gPiArCXsgLmNvbXBhdGlibGUgPSAiZnNsLHFvcmlxLXRtdSIsIH0sDQo+ID4g
Kwl7fSwNCj4gPiArfTsNCj4gPiArTU9EVUxFX0RFVklDRV9UQUJMRShvZiwgcW9yaXFfdG11X21h
dGNoKTsNCj4gPiArDQo+ID4gK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVyIHFvcmlxX3Rt
dSA9IHsNCj4gPiArCS5kcml2ZXIJPSB7DQo+ID4gKwkJLm5hbWUJCT0gInFvcmlxX3RoZXJtYWwi
LA0KPiA+ICsJCS5wbQkJPSAmcW9yaXFfdG11X3BtX29wcywNCj4gPiArCQkub2ZfbWF0Y2hfdGFi
bGUJPSBxb3JpcV90bXVfbWF0Y2gsDQo+ID4gKwl9LA0KPiA+ICsJLnByb2JlCT0gcW9yaXFfdG11
X3Byb2JlLA0KPiA+ICsJLnJlbW92ZQk9IHFvcmlxX3RtdV9yZW1vdmUsDQo+ID4gK307DQo+ID4g
K21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIocW9yaXFfdG11KTsNCj4gPiArDQo+ID4gK01PRFVMRV9B
VVRIT1IoIkppYSBIb25ndGFvIDxob25ndGFvLmppYUBueHAuY29tPiIpOw0KPiA+ICtNT0RVTEVf
REVTQ1JJUFRJT04oIlFvcklRIFRoZXJtYWwgTW9uaXRvcmluZyBVbml0IGRyaXZlciIpOw0KPiA+
ICtNT0RVTEVfTElDRU5TRSgiR1BMIHYyIik7DQo+ID4gLS0NCj4gPiAyLjEuMC4yNy5nOTZkYjMy
NA0KPiANCj4gX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18N
Cj4gTGludXhwcGMtZGV2IG1haWxpbmcgbGlzdA0KPiBMaW51eHBwYy1kZXZAbGlzdHMub3psYWJz
Lm9yZw0KPiBodHRwczovL2xpc3RzLm96bGFicy5vcmcvbGlzdGluZm8vbGludXhwcGMtZGV2DQo=
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-08-05 6:26 ` Hongtao Jia
0 siblings, 0 replies; 18+ messages in thread
From: Hongtao Jia @ 2016-08-05 6:26 UTC (permalink / raw)
To: linux-arm-kernel
Hi Eduardo,
If you have any comments please let me know.
Thanks.
-Hongtao.
> -----Original Message-----
> From: Linuxppc-dev [mailto:linuxppc-dev-
> bounces+b38951=freescale.com at lists.ozlabs.org] On Behalf Of Hongtao Jia
> Sent: Tuesday, July 19, 2016 2:54 PM
> To: edubezval at gmail.com; rui.zhang at intel.com; Scott Wood
> <scott.wood@nxp.com>
> Cc: devicetree at vger.kernel.org; linuxppc-dev at lists.ozlabs.org; linux-
> kernel at vger.kernel.org; linux-arm-kernel at lists.infradead.org; linux-
> pm at vger.kernel.org
> Subject: RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> Hi Eduardo,
>
> Any comments on this patch?
>
> Thanks.
> -Hongtao.
>
> > -----Original Message-----
> > From: Jia Hongtao [mailto:hongtao.jia at nxp.com]
> > Sent: Thursday, June 30, 2016 11:09 AM
> > To: edubezval at gmail.com; rui.zhang at intel.com; robh+dt at kernel.org;
> > galak at codeaurora.org; Scott Wood <scott.wood@nxp.com>;
> > shawnguo at kernel.org
> > Cc: linux-pm at vger.kernel.org; devicetree at vger.kernel.org; linux-
> > kernel at vger.kernel.org; linuxppc-dev at lists.ozlabs.org; linux-arm-
> > kernel at lists.infradead.org; Hongtao Jia <hongtao.jia@nxp.com>
> > Subject: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
> >
> > This driver add thermal management support by enabling TMU (Thermal
> > Monitoring Unit) on QorIQ platform.
> >
> > It's based on thermal of framework:
> > - Trip points defined in device tree.
> > - Cpufreq as cooling device registered in qoriq cpufreq driver.
> >
> > Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
> > ---
> > Changes of V2:
> > * Add HAS_IOMEM dependency to fix build error on UM
> >
> > drivers/thermal/Kconfig | 10 ++
> > drivers/thermal/Makefile | 1 +
> > drivers/thermal/qoriq_thermal.c | 328
> > ++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 339 insertions(+)
> > create mode 100644 drivers/thermal/qoriq_thermal.c
> >
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> > 2d702ca..56ef30d 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -195,6 +195,16 @@ config IMX_THERMAL
> > cpufreq is used as the cooling device to throttle CPUs when the
> > passive trip is crossed.
> >
> > +config QORIQ_THERMAL
> > + tristate "QorIQ Thermal Monitoring Unit"
> > + depends on THERMAL_OF
> > + depends on HAS_IOMEM
> > + help
> > + Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
> > + It supports one critical trip point and one passive trip point. The
> > + cpufreq is used as the cooling device to throttle CPUs when the
> > + passive trip is crossed.
> > +
> > config SPEAR_THERMAL
> > tristate "SPEAr thermal sensor driver"
> > depends on PLAT_SPEAR || COMPILE_TEST diff --git
> > a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> > 10b07c1..6662232 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> db8500_thermal.o
> > obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> > obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> > obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> > +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> > obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
> > obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> > obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> > diff --git a/drivers/thermal/qoriq_thermal.c
> > b/drivers/thermal/qoriq_thermal.c new file mode 100644 index
> > 0000000..644ba52
> > --- /dev/null
> > +++ b/drivers/thermal/qoriq_thermal.c
> > @@ -0,0 +1,328 @@
> > +/*
> > + * Copyright 2016 Freescale Semiconductor, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but
> > +WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> > +or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> > +License for
> > + * more details.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/err.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/thermal.h>
> > +
> > +#include "thermal_core.h"
> > +
> > +#define SITES_MAX 16
> > +
> > +/*
> > + * QorIQ TMU Registers
> > + */
> > +struct qoriq_tmu_site_regs {
> > + u32 tritsr; /* Immediate Temperature Site Register */
> > + u32 tratsr; /* Average Temperature Site Register */
> > + u8 res0[0x8];
> > +};
> > +
> > +struct qoriq_tmu_regs {
> > + u32 tmr; /* Mode Register */
> > +#define TMR_DISABLE 0x0
> > +#define TMR_ME 0x80000000
> > +#define TMR_ALPF 0x0c000000
> > + u32 tsr; /* Status Register */
> > + u32 tmtmir; /* Temperature measurement interval Register */
> > +#define TMTMIR_DEFAULT 0x0000000f
> > + u8 res0[0x14];
> > + u32 tier; /* Interrupt Enable Register */
> > +#define TIER_DISABLE 0x0
> > + u32 tidr; /* Interrupt Detect Register */
> > + u32 tiscr; /* Interrupt Site Capture Register */
> > + u32 ticscr; /* Interrupt Critical Site Capture Register */
> > + u8 res1[0x10];
> > + u32 tmhtcrh; /* High Temperature Capture Register */
> > + u32 tmhtcrl; /* Low Temperature Capture Register */
> > + u8 res2[0x8];
> > + u32 tmhtitr; /* High Temperature Immediate Threshold */
> > + u32 tmhtatr; /* High Temperature Average Threshold */
> > + u32 tmhtactr; /* High Temperature Average Crit Threshold */
> > + u8 res3[0x24];
> > + u32 ttcfgr; /* Temperature Configuration Register */
> > + u32 tscfgr; /* Sensor Configuration Register */
> > + u8 res4[0x78];
> > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > + u8 res5[0x9f8];
> > + u32 ipbrr0; /* IP Block Revision Register 0 */
> > + u32 ipbrr1; /* IP Block Revision Register 1 */
> > + u8 res6[0x310];
> > + u32 ttr0cr; /* Temperature Range 0 Control Register */
> > + u32 ttr1cr; /* Temperature Range 1 Control Register */
> > + u32 ttr2cr; /* Temperature Range 2 Control Register */
> > + u32 ttr3cr; /* Temperature Range 3 Control Register */
> > +};
> > +
> > +/*
> > + * Thermal zone data
> > + */
> > +struct qoriq_tmu_data {
> > + struct thermal_zone_device *tz;
> > + struct qoriq_tmu_regs __iomem *regs;
> > + int sensor_id;
> > + bool little_endian;
> > +};
> > +
> > +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem
> > +*addr) {
> > + if (p->little_endian)
> > + iowrite32(val, addr);
> > + else
> > + iowrite32be(val, addr);
> > +}
> > +
> > +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) {
> > + if (p->little_endian)
> > + return ioread32(addr);
> > + else
> > + return ioread32be(addr);
> > +}
> > +
> > +static int tmu_get_temp(void *p, int *temp) {
> > + u32 val;
> > + struct qoriq_tmu_data *data = p;
> > +
> > + val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
> > + *temp = (val & 0xff) * 1000;
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_get_sensor_id(void) {
> > + int ret, id;
> > + struct of_phandle_args sensor_specs;
> > + struct device_node *np, *sensor_np;
> > +
> > + np = of_find_node_by_name(NULL, "thermal-zones");
> > + if (!np)
> > + return -ENODEV;
> > +
> > + sensor_np = of_get_next_child(np, NULL);
> > + ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
> > + "#thermal-sensor-cells",
> > + 0, &sensor_specs);
> > + if (ret) {
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > + return ret;
> > + }
> > +
> > + if (sensor_specs.args_count >= 1) {
> > + id = sensor_specs.args[0];
> > + WARN(sensor_specs.args_count > 1,
> > + "%s: too many cells in sensor specifier %d\n",
> > + sensor_specs.np->name,
> > sensor_specs.args_count);
> > + } else {
> > + id = 0;
> > + }
> > +
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > +
> > + return id;
> > +}
> > +
> > +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> > + int i, val, len;
> > + u32 range[4];
> > + const u32 *calibration;
> > + struct device_node *np = pdev->dev.of_node;
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
> > + dev_err(&pdev->dev, "missing calibration range.\n");
> > + return -ENODEV;
> > + }
> > +
> > + /* Init temperature range registers */
> > + tmu_write(data, range[0], &data->regs->ttr0cr);
> > + tmu_write(data, range[1], &data->regs->ttr1cr);
> > + tmu_write(data, range[2], &data->regs->ttr2cr);
> > + tmu_write(data, range[3], &data->regs->ttr3cr);
> > +
> > + calibration = of_get_property(np, "fsl,tmu-calibration", &len);
> > + if (calibration == NULL || len % 8) {
> > + dev_err(&pdev->dev, "invalid calibration data.\n");
> > + return -ENODEV;
> > + }
> > +
> > + for (i = 0; i < len; i += 8, calibration += 2) {
> > + val = of_read_number(calibration, 1);
> > + tmu_write(data, val, &data->regs->ttcfgr);
> > + val = of_read_number(calibration + 1, 1);
> > + tmu_write(data, val, &data->regs->tscfgr);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> > + /* Disable interrupt, using polling instead */
> > + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> > +
> > + /* Set update_interval */
> > + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr); }
> > +
> > +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> > + .get_temp = tmu_get_temp,
> > +};
> > +
> > +static int qoriq_tmu_probe(struct platform_device *pdev) {
> > + int ret;
> > + const struct thermal_trip *trip;
> > + struct qoriq_tmu_data *data;
> > + struct device_node *np = pdev->dev.of_node;
> > + u32 site = 0;
> > +
> > + if (!np) {
> > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > + return -ENODEV;
> > + }
> > +
> > + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> > + GFP_KERNEL);
> > + if (!data)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, data);
> > +
> > + data->little_endian = of_property_read_bool(np, "little-endian");
> > +
> > + data->sensor_id = qoriq_tmu_get_sensor_id();
> > + if (data->sensor_id < 0) {
> > + dev_err(&pdev->dev, "Failed to get sensor id\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + data->regs = of_iomap(np, 0);
> > + if (!data->regs) {
> > + dev_err(&pdev->dev, "Failed to get memory region\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + qoriq_tmu_init_device(data); /* TMU initialization */
> > +
> > + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> > + if (ret < 0)
> > + goto err_tmu;
> > +
> > + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
> > + data, &tmu_tz_ops);
> > + if (IS_ERR(data->tz)) {
> > + ret = PTR_ERR(data->tz);
> > + dev_err(&pdev->dev,
> > + "Failed to register thermal zone device %d\n", ret);
> > + goto err_tmu;
> > + }
> > +
> > + trip = of_thermal_get_trip_points(data->tz);
> > +
> > + /* Enable monitoring */
> > + site |= 0x1 << (15 - data->sensor_id);
> > + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> > +
> > + return 0;
> > +
> > +err_tmu:
> > + iounmap(data->regs);
> > +
> > +err_iomap:
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return ret;
> > +}
> > +
> > +static int qoriq_tmu_remove(struct platform_device *pdev) {
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +
> > + iounmap(data->regs);
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int qoriq_tmu_suspend(struct device *dev) {
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Disable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr &= ~TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_resume(struct device *dev) {
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Enable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr |= TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> > + qoriq_tmu_suspend, qoriq_tmu_resume);
> > +
> > +static const struct of_device_id qoriq_tmu_match[] = {
> > + { .compatible = "fsl,qoriq-tmu", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> > +
> > +static struct platform_driver qoriq_tmu = {
> > + .driver = {
> > + .name = "qoriq_thermal",
> > + .pm = &qoriq_tmu_pm_ops,
> > + .of_match_table = qoriq_tmu_match,
> > + },
> > + .probe = qoriq_tmu_probe,
> > + .remove = qoriq_tmu_remove,
> > +};
> > +module_platform_driver(qoriq_tmu);
> > +
> > +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> > +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.1.0.27.g96db324
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
2016-06-30 3:08 ` Jia Hongtao
(?)
@ 2016-08-19 12:39 ` Zhang Rui
-1 siblings, 0 replies; 18+ messages in thread
From: Zhang Rui @ 2016-08-19 12:39 UTC (permalink / raw)
To: Jia Hongtao, edubezval, robh+dt, galak, scott.wood, shawnguo
Cc: linux-pm, devicetree, linux-kernel, linuxppc-dev, linux-arm-kernel
On 四, 2016-06-30 at 11:08 +0800, Jia Hongtao wrote:
> This driver add thermal management support by enabling TMU (Thermal
> Monitoring Unit) on QorIQ platform.
>
> It's based on thermal of framework:
> - Trip points defined in device tree.
> - Cpufreq as cooling device registered in qoriq cpufreq driver.
>
> Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
The patch looks good to me.
I only need to take this patch, right?
thanks,
rui
> ---
> Changes of V2:
> * Add HAS_IOMEM dependency to fix build error on UM
>
> drivers/thermal/Kconfig | 10 ++
> drivers/thermal/Makefile | 1 +
> drivers/thermal/qoriq_thermal.c | 328
> ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 339 insertions(+)
> create mode 100644 drivers/thermal/qoriq_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 2d702ca..56ef30d 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -195,6 +195,16 @@ config IMX_THERMAL
> cpufreq is used as the cooling device to throttle CPUs
> when the
> passive trip is crossed.
>
> +config QORIQ_THERMAL
> + tristate "QorIQ Thermal Monitoring Unit"
> + depends on THERMAL_OF
> + depends on HAS_IOMEM
> + help
> + Support for Thermal Monitoring Unit (TMU) found on QorIQ
> platforms.
> + It supports one critical trip point and one passive trip
> point. The
> + cpufreq is used as the cooling device to throttle CPUs
> when the
> + passive trip is crossed.
> +
> config SPEAR_THERMAL
> tristate "SPEAr thermal sensor driver"
> depends on PLAT_SPEAR || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 10b07c1..6662232 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> db8500_thermal.o
> obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> obj-$(CONFIG_DB8500_CPUFREQ_COOLING) +=
> db8500_cpufreq_cooling.o
> obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> diff --git a/drivers/thermal/qoriq_thermal.c
> b/drivers/thermal/qoriq_thermal.c
> new file mode 100644
> index 0000000..644ba52
> --- /dev/null
> +++ b/drivers/thermal/qoriq_thermal.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright 2016 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> License for
> + * more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define SITES_MAX 16
> +
> +/*
> + * QorIQ TMU Registers
> + */
> +struct qoriq_tmu_site_regs {
> + u32 tritsr; /* Immediate Temperature Site
> Register */
> + u32 tratsr; /* Average Temperature Site
> Register */
> + u8 res0[0x8];
> +};
> +
> +struct qoriq_tmu_regs {
> + u32 tmr; /* Mode Register */
> +#define TMR_DISABLE 0x0
> +#define TMR_ME 0x80000000
> +#define TMR_ALPF 0x0c000000
> + u32 tsr; /* Status Register */
> + u32 tmtmir; /* Temperature measurement
> interval Register */
> +#define TMTMIR_DEFAULT 0x0000000f
> + u8 res0[0x14];
> + u32 tier; /* Interrupt Enable Register */
> +#define TIER_DISABLE 0x0
> + u32 tidr; /* Interrupt Detect Register */
> + u32 tiscr; /* Interrupt Site Capture Register
> */
> + u32 ticscr; /* Interrupt Critical Site
> Capture Register */
> + u8 res1[0x10];
> + u32 tmhtcrh; /* High Temperature Capture
> Register */
> + u32 tmhtcrl; /* Low Temperature Capture
> Register */
> + u8 res2[0x8];
> + u32 tmhtitr; /* High Temperature Immediate
> Threshold */
> + u32 tmhtatr; /* High Temperature Average
> Threshold */
> + u32 tmhtactr; /* High Temperature Average Crit
> Threshold */
> + u8 res3[0x24];
> + u32 ttcfgr; /* Temperature Configuration
> Register */
> + u32 tscfgr; /* Sensor Configuration Register
> */
> + u8 res4[0x78];
> + struct qoriq_tmu_site_regs site[SITES_MAX];
> + u8 res5[0x9f8];
> + u32 ipbrr0; /* IP Block Revision Register 0
> */
> + u32 ipbrr1; /* IP Block Revision Register 1
> */
> + u8 res6[0x310];
> + u32 ttr0cr; /* Temperature Range 0 Control
> Register */
> + u32 ttr1cr; /* Temperature Range 1 Control
> Register */
> + u32 ttr2cr; /* Temperature Range 2 Control
> Register */
> + u32 ttr3cr; /* Temperature Range 3 Control
> Register */
> +};
> +
> +/*
> + * Thermal zone data
> + */
> +struct qoriq_tmu_data {
> + struct thermal_zone_device *tz;
> + struct qoriq_tmu_regs __iomem *regs;
> + int sensor_id;
> + bool little_endian;
> +};
> +
> +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void
> __iomem *addr)
> +{
> + if (p->little_endian)
> + iowrite32(val, addr);
> + else
> + iowrite32be(val, addr);
> +}
> +
> +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
> +{
> + if (p->little_endian)
> + return ioread32(addr);
> + else
> + return ioread32be(addr);
> +}
> +
> +static int tmu_get_temp(void *p, int *temp)
> +{
> + u32 val;
> + struct qoriq_tmu_data *data = p;
> +
> + val = tmu_read(data, &data->regs->site[data-
> >sensor_id].tritsr);
> + *temp = (val & 0xff) * 1000;
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_get_sensor_id(void)
> +{
> + int ret, id;
> + struct of_phandle_args sensor_specs;
> + struct device_node *np, *sensor_np;
> +
> + np = of_find_node_by_name(NULL, "thermal-zones");
> + if (!np)
> + return -ENODEV;
> +
> + sensor_np = of_get_next_child(np, NULL);
> + ret = of_parse_phandle_with_args(sensor_np, "thermal-
> sensors",
> + "#thermal-sensor-cells",
> + 0, &sensor_specs);
> + if (ret) {
> + of_node_put(np);
> + of_node_put(sensor_np);
> + return ret;
> + }
> +
> + if (sensor_specs.args_count >= 1) {
> + id = sensor_specs.args[0];
> + WARN(sensor_specs.args_count > 1,
> + "%s: too many cells in sensor
> specifier %d\n",
> + sensor_specs.np->name,
> sensor_specs.args_count);
> + } else {
> + id = 0;
> + }
> +
> + of_node_put(np);
> + of_node_put(sensor_np);
> +
> + return id;
> +}
> +
> +static int qoriq_tmu_calibration(struct platform_device *pdev)
> +{
> + int i, val, len;
> + u32 range[4];
> + const u32 *calibration;
> + struct device_node *np = pdev->dev.of_node;
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + if (of_property_read_u32_array(np, "fsl,tmu-range", range,
> 4)) {
> + dev_err(&pdev->dev, "missing calibration range.\n");
> + return -ENODEV;
> + }
> +
> + /* Init temperature range registers */
> + tmu_write(data, range[0], &data->regs->ttr0cr);
> + tmu_write(data, range[1], &data->regs->ttr1cr);
> + tmu_write(data, range[2], &data->regs->ttr2cr);
> + tmu_write(data, range[3], &data->regs->ttr3cr);
> +
> + calibration = of_get_property(np, "fsl,tmu-calibration",
> &len);
> + if (calibration == NULL || len % 8) {
> + dev_err(&pdev->dev, "invalid calibration data.\n");
> + return -ENODEV;
> + }
> +
> + for (i = 0; i < len; i += 8, calibration += 2) {
> + val = of_read_number(calibration, 1);
> + tmu_write(data, val, &data->regs->ttcfgr);
> + val = of_read_number(calibration + 1, 1);
> + tmu_write(data, val, &data->regs->tscfgr);
> + }
> +
> + return 0;
> +}
> +
> +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
> +{
> + /* Disable interrupt, using polling instead */
> + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> +
> + /* Set update_interval */
> + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +}
> +
> +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> + .get_temp = tmu_get_temp,
> +};
> +
> +static int qoriq_tmu_probe(struct platform_device *pdev)
> +{
> + int ret;
> + const struct thermal_trip *trip;
> + struct qoriq_tmu_data *data;
> + struct device_node *np = pdev->dev.of_node;
> + u32 site = 0;
> +
> + if (!np) {
> + dev_err(&pdev->dev, "Device OF-Node is NULL");
> + return -ENODEV;
> + }
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(struct
> qoriq_tmu_data),
> + GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, data);
> +
> + data->little_endian = of_property_read_bool(np, "little-
> endian");
> +
> + data->sensor_id = qoriq_tmu_get_sensor_id();
> + if (data->sensor_id < 0) {
> + dev_err(&pdev->dev, "Failed to get sensor id\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + data->regs = of_iomap(np, 0);
> + if (!data->regs) {
> + dev_err(&pdev->dev, "Failed to get memory
> region\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + qoriq_tmu_init_device(data); /* TMU initialization */
> +
> + ret = qoriq_tmu_calibration(pdev); /* TMU calibration
> */
> + if (ret < 0)
> + goto err_tmu;
> +
> + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data-
> >sensor_id,
> + data, &tmu_tz_ops);
> + if (IS_ERR(data->tz)) {
> + ret = PTR_ERR(data->tz);
> + dev_err(&pdev->dev,
> + "Failed to register thermal zone device
> %d\n", ret);
> + goto err_tmu;
> + }
> +
> + trip = of_thermal_get_trip_points(data->tz);
> +
> + /* Enable monitoring */
> + site |= 0x1 << (15 - data->sensor_id);
> + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> +
> + return 0;
> +
> +err_tmu:
> + iounmap(data->regs);
> +
> +err_iomap:
> + platform_set_drvdata(pdev, NULL);
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_remove(struct platform_device *pdev)
> +{
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +
> + iounmap(data->regs);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qoriq_tmu_suspend(struct device *dev)
> +{
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Disable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr &= ~TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_resume(struct device *dev)
> +{
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Enable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr |= TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> + qoriq_tmu_suspend, qoriq_tmu_resume);
> +
> +static const struct of_device_id qoriq_tmu_match[] = {
> + { .compatible = "fsl,qoriq-tmu", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> +
> +static struct platform_driver qoriq_tmu = {
> + .driver = {
> + .name = "qoriq_thermal",
> + .pm = &qoriq_tmu_pm_ops,
> + .of_match_table = qoriq_tmu_match,
> + },
> + .probe = qoriq_tmu_probe,
> + .remove = qoriq_tmu_remove,
> +};
> +module_platform_driver(qoriq_tmu);
> +
> +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0.27.g96db324
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-08-19 12:39 ` Zhang Rui
0 siblings, 0 replies; 18+ messages in thread
From: Zhang Rui @ 2016-08-19 12:39 UTC (permalink / raw)
To: Jia Hongtao, edubezval, robh+dt, galak, scott.wood, shawnguo
Cc: linux-pm, devicetree, linux-kernel, linuxppc-dev, linux-arm-kernel
On 四, 2016-06-30 at 11:08 +0800, Jia Hongtao wrote:
> This driver add thermal management support by enabling TMU (Thermal
> Monitoring Unit) on QorIQ platform.
>
> It's based on thermal of framework:
> - Trip points defined in device tree.
> - Cpufreq as cooling device registered in qoriq cpufreq driver.
>
> Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
The patch looks good to me.
I only need to take this patch, right?
thanks,
rui
> ---
> Changes of V2:
> * Add HAS_IOMEM dependency to fix build error on UM
>
> drivers/thermal/Kconfig | 10 ++
> drivers/thermal/Makefile | 1 +
> drivers/thermal/qoriq_thermal.c | 328
> ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 339 insertions(+)
> create mode 100644 drivers/thermal/qoriq_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 2d702ca..56ef30d 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -195,6 +195,16 @@ config IMX_THERMAL
> cpufreq is used as the cooling device to throttle CPUs
> when the
> passive trip is crossed.
>
> +config QORIQ_THERMAL
> + tristate "QorIQ Thermal Monitoring Unit"
> + depends on THERMAL_OF
> + depends on HAS_IOMEM
> + help
> + Support for Thermal Monitoring Unit (TMU) found on QorIQ
> platforms.
> + It supports one critical trip point and one passive trip
> point. The
> + cpufreq is used as the cooling device to throttle CPUs
> when the
> + passive trip is crossed.
> +
> config SPEAR_THERMAL
> tristate "SPEAr thermal sensor driver"
> depends on PLAT_SPEAR || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 10b07c1..6662232 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> db8500_thermal.o
> obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> obj-$(CONFIG_DB8500_CPUFREQ_COOLING) +=
> db8500_cpufreq_cooling.o
> obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> diff --git a/drivers/thermal/qoriq_thermal.c
> b/drivers/thermal/qoriq_thermal.c
> new file mode 100644
> index 0000000..644ba52
> --- /dev/null
> +++ b/drivers/thermal/qoriq_thermal.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright 2016 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> License for
> + * more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define SITES_MAX 16
> +
> +/*
> + * QorIQ TMU Registers
> + */
> +struct qoriq_tmu_site_regs {
> + u32 tritsr; /* Immediate Temperature Site
> Register */
> + u32 tratsr; /* Average Temperature Site
> Register */
> + u8 res0[0x8];
> +};
> +
> +struct qoriq_tmu_regs {
> + u32 tmr; /* Mode Register */
> +#define TMR_DISABLE 0x0
> +#define TMR_ME 0x80000000
> +#define TMR_ALPF 0x0c000000
> + u32 tsr; /* Status Register */
> + u32 tmtmir; /* Temperature measurement
> interval Register */
> +#define TMTMIR_DEFAULT 0x0000000f
> + u8 res0[0x14];
> + u32 tier; /* Interrupt Enable Register */
> +#define TIER_DISABLE 0x0
> + u32 tidr; /* Interrupt Detect Register */
> + u32 tiscr; /* Interrupt Site Capture Register
> */
> + u32 ticscr; /* Interrupt Critical Site
> Capture Register */
> + u8 res1[0x10];
> + u32 tmhtcrh; /* High Temperature Capture
> Register */
> + u32 tmhtcrl; /* Low Temperature Capture
> Register */
> + u8 res2[0x8];
> + u32 tmhtitr; /* High Temperature Immediate
> Threshold */
> + u32 tmhtatr; /* High Temperature Average
> Threshold */
> + u32 tmhtactr; /* High Temperature Average Crit
> Threshold */
> + u8 res3[0x24];
> + u32 ttcfgr; /* Temperature Configuration
> Register */
> + u32 tscfgr; /* Sensor Configuration Register
> */
> + u8 res4[0x78];
> + struct qoriq_tmu_site_regs site[SITES_MAX];
> + u8 res5[0x9f8];
> + u32 ipbrr0; /* IP Block Revision Register 0
> */
> + u32 ipbrr1; /* IP Block Revision Register 1
> */
> + u8 res6[0x310];
> + u32 ttr0cr; /* Temperature Range 0 Control
> Register */
> + u32 ttr1cr; /* Temperature Range 1 Control
> Register */
> + u32 ttr2cr; /* Temperature Range 2 Control
> Register */
> + u32 ttr3cr; /* Temperature Range 3 Control
> Register */
> +};
> +
> +/*
> + * Thermal zone data
> + */
> +struct qoriq_tmu_data {
> + struct thermal_zone_device *tz;
> + struct qoriq_tmu_regs __iomem *regs;
> + int sensor_id;
> + bool little_endian;
> +};
> +
> +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void
> __iomem *addr)
> +{
> + if (p->little_endian)
> + iowrite32(val, addr);
> + else
> + iowrite32be(val, addr);
> +}
> +
> +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
> +{
> + if (p->little_endian)
> + return ioread32(addr);
> + else
> + return ioread32be(addr);
> +}
> +
> +static int tmu_get_temp(void *p, int *temp)
> +{
> + u32 val;
> + struct qoriq_tmu_data *data = p;
> +
> + val = tmu_read(data, &data->regs->site[data-
> >sensor_id].tritsr);
> + *temp = (val & 0xff) * 1000;
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_get_sensor_id(void)
> +{
> + int ret, id;
> + struct of_phandle_args sensor_specs;
> + struct device_node *np, *sensor_np;
> +
> + np = of_find_node_by_name(NULL, "thermal-zones");
> + if (!np)
> + return -ENODEV;
> +
> + sensor_np = of_get_next_child(np, NULL);
> + ret = of_parse_phandle_with_args(sensor_np, "thermal-
> sensors",
> + "#thermal-sensor-cells",
> + 0, &sensor_specs);
> + if (ret) {
> + of_node_put(np);
> + of_node_put(sensor_np);
> + return ret;
> + }
> +
> + if (sensor_specs.args_count >= 1) {
> + id = sensor_specs.args[0];
> + WARN(sensor_specs.args_count > 1,
> + "%s: too many cells in sensor
> specifier %d\n",
> + sensor_specs.np->name,
> sensor_specs.args_count);
> + } else {
> + id = 0;
> + }
> +
> + of_node_put(np);
> + of_node_put(sensor_np);
> +
> + return id;
> +}
> +
> +static int qoriq_tmu_calibration(struct platform_device *pdev)
> +{
> + int i, val, len;
> + u32 range[4];
> + const u32 *calibration;
> + struct device_node *np = pdev->dev.of_node;
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + if (of_property_read_u32_array(np, "fsl,tmu-range", range,
> 4)) {
> + dev_err(&pdev->dev, "missing calibration range.\n");
> + return -ENODEV;
> + }
> +
> + /* Init temperature range registers */
> + tmu_write(data, range[0], &data->regs->ttr0cr);
> + tmu_write(data, range[1], &data->regs->ttr1cr);
> + tmu_write(data, range[2], &data->regs->ttr2cr);
> + tmu_write(data, range[3], &data->regs->ttr3cr);
> +
> + calibration = of_get_property(np, "fsl,tmu-calibration",
> &len);
> + if (calibration == NULL || len % 8) {
> + dev_err(&pdev->dev, "invalid calibration data.\n");
> + return -ENODEV;
> + }
> +
> + for (i = 0; i < len; i += 8, calibration += 2) {
> + val = of_read_number(calibration, 1);
> + tmu_write(data, val, &data->regs->ttcfgr);
> + val = of_read_number(calibration + 1, 1);
> + tmu_write(data, val, &data->regs->tscfgr);
> + }
> +
> + return 0;
> +}
> +
> +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
> +{
> + /* Disable interrupt, using polling instead */
> + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> +
> + /* Set update_interval */
> + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +}
> +
> +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> + .get_temp = tmu_get_temp,
> +};
> +
> +static int qoriq_tmu_probe(struct platform_device *pdev)
> +{
> + int ret;
> + const struct thermal_trip *trip;
> + struct qoriq_tmu_data *data;
> + struct device_node *np = pdev->dev.of_node;
> + u32 site = 0;
> +
> + if (!np) {
> + dev_err(&pdev->dev, "Device OF-Node is NULL");
> + return -ENODEV;
> + }
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(struct
> qoriq_tmu_data),
> + GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, data);
> +
> + data->little_endian = of_property_read_bool(np, "little-
> endian");
> +
> + data->sensor_id = qoriq_tmu_get_sensor_id();
> + if (data->sensor_id < 0) {
> + dev_err(&pdev->dev, "Failed to get sensor id\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + data->regs = of_iomap(np, 0);
> + if (!data->regs) {
> + dev_err(&pdev->dev, "Failed to get memory
> region\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + qoriq_tmu_init_device(data); /* TMU initialization */
> +
> + ret = qoriq_tmu_calibration(pdev); /* TMU calibration
> */
> + if (ret < 0)
> + goto err_tmu;
> +
> + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data-
> >sensor_id,
> + data, &tmu_tz_ops);
> + if (IS_ERR(data->tz)) {
> + ret = PTR_ERR(data->tz);
> + dev_err(&pdev->dev,
> + "Failed to register thermal zone device
> %d\n", ret);
> + goto err_tmu;
> + }
> +
> + trip = of_thermal_get_trip_points(data->tz);
> +
> + /* Enable monitoring */
> + site |= 0x1 << (15 - data->sensor_id);
> + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> +
> + return 0;
> +
> +err_tmu:
> + iounmap(data->regs);
> +
> +err_iomap:
> + platform_set_drvdata(pdev, NULL);
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_remove(struct platform_device *pdev)
> +{
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +
> + iounmap(data->regs);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qoriq_tmu_suspend(struct device *dev)
> +{
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Disable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr &= ~TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_resume(struct device *dev)
> +{
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Enable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr |= TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> + qoriq_tmu_suspend, qoriq_tmu_resume);
> +
> +static const struct of_device_id qoriq_tmu_match[] = {
> + { .compatible = "fsl,qoriq-tmu", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> +
> +static struct platform_driver qoriq_tmu = {
> + .driver = {
> + .name = "qoriq_thermal",
> + .pm = &qoriq_tmu_pm_ops,
> + .of_match_table = qoriq_tmu_match,
> + },
> + .probe = qoriq_tmu_probe,
> + .remove = qoriq_tmu_remove,
> +};
> +module_platform_driver(qoriq_tmu);
> +
> +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0.27.g96db324
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-08-19 12:39 ` Zhang Rui
0 siblings, 0 replies; 18+ messages in thread
From: Zhang Rui @ 2016-08-19 12:39 UTC (permalink / raw)
To: linux-arm-kernel
On ?, 2016-06-30 at 11:08 +0800, Jia Hongtao wrote:
> This driver add thermal management support by enabling TMU (Thermal
> Monitoring Unit) on QorIQ platform.
>
> It's based on thermal of framework:
> - Trip points defined in device tree.
> - Cpufreq as cooling device registered in qoriq cpufreq driver.
>
> Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
The patch looks good to me.
I only need to take this patch, right?
thanks,
rui
> ---
> Changes of V2:
> * Add HAS_IOMEM dependency to fix build error on UM
>
> ?drivers/thermal/Kconfig?????????|??10 ++
> ?drivers/thermal/Makefile????????|???1 +
> ?drivers/thermal/qoriq_thermal.c | 328
> ++++++++++++++++++++++++++++++++++++++++
> ?3 files changed, 339 insertions(+)
> ?create mode 100644 drivers/thermal/qoriq_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 2d702ca..56ef30d 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -195,6 +195,16 @@ config IMX_THERMAL
> ? ??cpufreq is used as the cooling device to throttle CPUs
> when the
> ? ??passive trip is crossed.
>
> +config QORIQ_THERMAL
> + tristate "QorIQ Thermal Monitoring Unit"
> + depends on THERMAL_OF
> + depends on HAS_IOMEM
> + help
> + ??Support for Thermal Monitoring Unit (TMU) found on QorIQ
> platforms.
> + ??It supports one critical trip point and one passive trip
> point. The
> + ??cpufreq is used as the cooling device to throttle CPUs
> when the
> + ??passive trip is crossed.
> +
> ?config SPEAR_THERMAL
> ? tristate "SPEAr thermal sensor driver"
> ? depends on PLAT_SPEAR || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 10b07c1..6662232 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> db8500_thermal.o
> ?obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> ?obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> ?obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> ?obj-$(CONFIG_DB8500_CPUFREQ_COOLING) +=
> db8500_cpufreq_cooling.o
> ?obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> ?obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> diff --git a/drivers/thermal/qoriq_thermal.c
> b/drivers/thermal/qoriq_thermal.c
> new file mode 100644
> index 0000000..644ba52
> --- /dev/null
> +++ b/drivers/thermal/qoriq_thermal.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright 2016 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.??See the GNU General Public
> License for
> + * more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define SITES_MAX 16
> +
> +/*
> + * QorIQ TMU Registers
> + */
> +struct qoriq_tmu_site_regs {
> + u32 tritsr; /* Immediate Temperature Site
> Register */
> + u32 tratsr; /* Average Temperature Site
> Register */
> + u8 res0[0x8];
> +};
> +
> +struct qoriq_tmu_regs {
> + u32 tmr; /* Mode Register */
> +#define TMR_DISABLE 0x0
> +#define TMR_ME 0x80000000
> +#define TMR_ALPF 0x0c000000
> + u32 tsr; /* Status Register */
> + u32 tmtmir; /* Temperature measurement
> interval Register */
> +#define TMTMIR_DEFAULT 0x0000000f
> + u8 res0[0x14];
> + u32 tier; /* Interrupt Enable Register */
> +#define TIER_DISABLE 0x0
> + u32 tidr; /* Interrupt Detect Register */
> + u32 tiscr; /* Interrupt Site Capture Register
> */
> + u32 ticscr; /* Interrupt Critical Site
> Capture Register */
> + u8 res1[0x10];
> + u32 tmhtcrh; /* High Temperature Capture
> Register */
> + u32 tmhtcrl; /* Low Temperature Capture
> Register */
> + u8 res2[0x8];
> + u32 tmhtitr; /* High Temperature Immediate
> Threshold */
> + u32 tmhtatr; /* High Temperature Average
> Threshold */
> + u32 tmhtactr; /* High Temperature Average Crit
> Threshold */
> + u8 res3[0x24];
> + u32 ttcfgr; /* Temperature Configuration
> Register */
> + u32 tscfgr; /* Sensor Configuration Register
> */
> + u8 res4[0x78];
> + struct qoriq_tmu_site_regs site[SITES_MAX];
> + u8 res5[0x9f8];
> + u32 ipbrr0; /* IP Block Revision Register 0
> */
> + u32 ipbrr1; /* IP Block Revision Register 1
> */
> + u8 res6[0x310];
> + u32 ttr0cr; /* Temperature Range 0 Control
> Register */
> + u32 ttr1cr; /* Temperature Range 1 Control
> Register */
> + u32 ttr2cr; /* Temperature Range 2 Control
> Register */
> + u32 ttr3cr; /* Temperature Range 3 Control
> Register */
> +};
> +
> +/*
> + * Thermal zone data
> + */
> +struct qoriq_tmu_data {
> + struct thermal_zone_device *tz;
> + struct qoriq_tmu_regs __iomem *regs;
> + int sensor_id;
> + bool little_endian;
> +};
> +
> +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void
> __iomem *addr)
> +{
> + if (p->little_endian)
> + iowrite32(val, addr);
> + else
> + iowrite32be(val, addr);
> +}
> +
> +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
> +{
> + if (p->little_endian)
> + return ioread32(addr);
> + else
> + return ioread32be(addr);
> +}
> +
> +static int tmu_get_temp(void *p, int *temp)
> +{
> + u32 val;
> + struct qoriq_tmu_data *data = p;
> +
> + val = tmu_read(data, &data->regs->site[data-
> >sensor_id].tritsr);
> + *temp = (val & 0xff) * 1000;
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_get_sensor_id(void)
> +{
> + int ret, id;
> + struct of_phandle_args sensor_specs;
> + struct device_node *np, *sensor_np;
> +
> + np = of_find_node_by_name(NULL, "thermal-zones");
> + if (!np)
> + return -ENODEV;
> +
> + sensor_np = of_get_next_child(np, NULL);
> + ret = of_parse_phandle_with_args(sensor_np, "thermal-
> sensors",
> + "#thermal-sensor-cells",
> + 0, &sensor_specs);
> + if (ret) {
> + of_node_put(np);
> + of_node_put(sensor_np);
> + return ret;
> + }
> +
> + if (sensor_specs.args_count >= 1) {
> + id = sensor_specs.args[0];
> + WARN(sensor_specs.args_count > 1,
> + "%s: too many cells in sensor
> specifier %d\n",
> + sensor_specs.np->name,
> sensor_specs.args_count);
> + } else {
> + id = 0;
> + }
> +
> + of_node_put(np);
> + of_node_put(sensor_np);
> +
> + return id;
> +}
> +
> +static int qoriq_tmu_calibration(struct platform_device *pdev)
> +{
> + int i, val, len;
> + u32 range[4];
> + const u32 *calibration;
> + struct device_node *np = pdev->dev.of_node;
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + if (of_property_read_u32_array(np, "fsl,tmu-range", range,
> 4)) {
> + dev_err(&pdev->dev, "missing calibration range.\n");
> + return -ENODEV;
> + }
> +
> + /* Init temperature range registers */
> + tmu_write(data, range[0], &data->regs->ttr0cr);
> + tmu_write(data, range[1], &data->regs->ttr1cr);
> + tmu_write(data, range[2], &data->regs->ttr2cr);
> + tmu_write(data, range[3], &data->regs->ttr3cr);
> +
> + calibration = of_get_property(np, "fsl,tmu-calibration",
> &len);
> + if (calibration == NULL || len % 8) {
> + dev_err(&pdev->dev, "invalid calibration data.\n");
> + return -ENODEV;
> + }
> +
> + for (i = 0; i < len; i += 8, calibration += 2) {
> + val = of_read_number(calibration, 1);
> + tmu_write(data, val, &data->regs->ttcfgr);
> + val = of_read_number(calibration + 1, 1);
> + tmu_write(data, val, &data->regs->tscfgr);
> + }
> +
> + return 0;
> +}
> +
> +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
> +{
> + /* Disable interrupt, using polling instead */
> + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> +
> + /* Set update_interval */
> + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +}
> +
> +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> + .get_temp = tmu_get_temp,
> +};
> +
> +static int qoriq_tmu_probe(struct platform_device *pdev)
> +{
> + int ret;
> + const struct thermal_trip *trip;
> + struct qoriq_tmu_data *data;
> + struct device_node *np = pdev->dev.of_node;
> + u32 site = 0;
> +
> + if (!np) {
> + dev_err(&pdev->dev, "Device OF-Node is NULL");
> + return -ENODEV;
> + }
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(struct
> qoriq_tmu_data),
> + ????GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, data);
> +
> + data->little_endian = of_property_read_bool(np, "little-
> endian");
> +
> + data->sensor_id = qoriq_tmu_get_sensor_id();
> + if (data->sensor_id < 0) {
> + dev_err(&pdev->dev, "Failed to get sensor id\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + data->regs = of_iomap(np, 0);
> + if (!data->regs) {
> + dev_err(&pdev->dev, "Failed to get memory
> region\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + qoriq_tmu_init_device(data); /* TMU initialization */
> +
> + ret = qoriq_tmu_calibration(pdev); /* TMU calibration
> */
> + if (ret < 0)
> + goto err_tmu;
> +
> + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data-
> >sensor_id,
> + data, &tmu_tz_ops);
> + if (IS_ERR(data->tz)) {
> + ret = PTR_ERR(data->tz);
> + dev_err(&pdev->dev,
> + "Failed to register thermal zone device
> %d\n", ret);
> + goto err_tmu;
> + }
> +
> + trip = of_thermal_get_trip_points(data->tz);
> +
> + /* Enable monitoring */
> + site |= 0x1 << (15 - data->sensor_id);
> + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> +
> + return 0;
> +
> +err_tmu:
> + iounmap(data->regs);
> +
> +err_iomap:
> + platform_set_drvdata(pdev, NULL);
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_remove(struct platform_device *pdev)
> +{
> + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> +
> + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> +
> + /* Disable monitoring */
> + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> +
> + iounmap(data->regs);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qoriq_tmu_suspend(struct device *dev)
> +{
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Disable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr &= ~TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_resume(struct device *dev)
> +{
> + u32 tmr;
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Enable monitoring */
> + tmr = tmu_read(data, &data->regs->tmr);
> + tmr |= TMR_ME;
> + tmu_write(data, tmr, &data->regs->tmr);
> +
> + return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> + ?qoriq_tmu_suspend, qoriq_tmu_resume);
> +
> +static const struct of_device_id qoriq_tmu_match[] = {
> + { .compatible = "fsl,qoriq-tmu", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> +
> +static struct platform_driver qoriq_tmu = {
> + .driver = {
> + .name = "qoriq_thermal",
> + .pm = &qoriq_tmu_pm_ops,
> + .of_match_table = qoriq_tmu_match,
> + },
> + .probe = qoriq_tmu_probe,
> + .remove = qoriq_tmu_remove,
> +};
> +module_platform_driver(qoriq_tmu);
> +
> +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0.27.g96db324
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
2016-08-19 12:39 ` Zhang Rui
(?)
(?)
@ 2016-09-08 7:11 ` Troy Jia
-1 siblings, 0 replies; 18+ messages in thread
From: Troy Jia @ 2016-09-08 7:11 UTC (permalink / raw)
To: Zhang Rui, edubezval, robh+dt, galak, Scott Wood, shawnguo
Cc: linux-pm, devicetree, linux-kernel, linuxppc-dev, linux-arm-kernel
> -----Original Message-----
> From: Zhang Rui [mailto:rui.zhang@intel.com]
> Sent: Friday, August 19, 2016 8:40 PM
> To: Hongtao Jia <hongtao.jia@nxp.com>; edubezval@gmail.com;
> robh+dt@kernel.org; galak@codeaurora.org; Scott Wood
> <scott.wood@nxp.com>; shawnguo@kernel.org
> Cc: linux-pm@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-arm-
> kernel@lists.infradead.org
> Subject: Re: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> On 四, 2016-06-30 at 11:08 +0800, Jia Hongtao wrote:
> > This driver add thermal management support by enabling TMU (Thermal
> > Monitoring Unit) on QorIQ platform.
> >
> > It's based on thermal of framework:
> > - Trip points defined in device tree.
> > - Cpufreq as cooling device registered in qoriq cpufreq driver.
> >
> > Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
>
> The patch looks good to me.
> I only need to take this patch, right?
>
> thanks,
> rui
Yes. You just need to take this patch.
Sorry for the late response. I was on my long vacation back then.
Thanks,
Hongtao.
> > ---
> > Changes of V2:
> > * Add HAS_IOMEM dependency to fix build error on UM
> >
> > drivers/thermal/Kconfig | 10 ++
> > drivers/thermal/Makefile | 1 +
> > drivers/thermal/qoriq_thermal.c | 328
> > ++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 339 insertions(+)
> > create mode 100644 drivers/thermal/qoriq_thermal.c
> >
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > index 2d702ca..56ef30d 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -195,6 +195,16 @@ config IMX_THERMAL
> > cpufreq is used as the cooling device to throttle CPUs
> > when the
> > passive trip is crossed.
> >
> > +config QORIQ_THERMAL
> > + tristate "QorIQ Thermal Monitoring Unit"
> > + depends on THERMAL_OF
> > + depends on HAS_IOMEM
> > + help
> > + Support for Thermal Monitoring Unit (TMU) found on QorIQ
> > platforms.
> > + It supports one critical trip point and one passive trip
> > point. The
> > + cpufreq is used as the cooling device to throttle CPUs
> > when the
> > + passive trip is crossed.
> > +
> > config SPEAR_THERMAL
> > tristate "SPEAr thermal sensor driver"
> > depends on PLAT_SPEAR || COMPILE_TEST
> > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > index 10b07c1..6662232 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> > db8500_thermal.o
> > obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> > obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> > obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> > +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> > obj-$(CONFIG_DB8500_CPUFREQ_COOLING) +=
> > db8500_cpufreq_cooling.o
> > obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> > obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> > diff --git a/drivers/thermal/qoriq_thermal.c
> > b/drivers/thermal/qoriq_thermal.c
> > new file mode 100644
> > index 0000000..644ba52
> > --- /dev/null
> > +++ b/drivers/thermal/qoriq_thermal.c
> > @@ -0,0 +1,328 @@
> > +/*
> > + * Copyright 2016 Freescale Semiconductor, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but
> > WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> > License for
> > + * more details.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/err.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/thermal.h>
> > +
> > +#include "thermal_core.h"
> > +
> > +#define SITES_MAX 16
> > +
> > +/*
> > + * QorIQ TMU Registers
> > + */
> > +struct qoriq_tmu_site_regs {
> > + u32 tritsr; /* Immediate Temperature Site
> > Register */
> > + u32 tratsr; /* Average Temperature Site
> > Register */
> > + u8 res0[0x8];
> > +};
> > +
> > +struct qoriq_tmu_regs {
> > + u32 tmr; /* Mode Register */
> > +#define TMR_DISABLE 0x0
> > +#define TMR_ME 0x80000000
> > +#define TMR_ALPF 0x0c000000
> > + u32 tsr; /* Status Register */
> > + u32 tmtmir; /* Temperature measurement
> > interval Register */
> > +#define TMTMIR_DEFAULT 0x0000000f
> > + u8 res0[0x14];
> > + u32 tier; /* Interrupt Enable Register */
> > +#define TIER_DISABLE 0x0
> > + u32 tidr; /* Interrupt Detect Register */
> > + u32 tiscr; /* Interrupt Site Capture Register
> > */
> > + u32 ticscr; /* Interrupt Critical Site
> > Capture Register */
> > + u8 res1[0x10];
> > + u32 tmhtcrh; /* High Temperature Capture
> > Register */
> > + u32 tmhtcrl; /* Low Temperature Capture
> > Register */
> > + u8 res2[0x8];
> > + u32 tmhtitr; /* High Temperature Immediate
> > Threshold */
> > + u32 tmhtatr; /* High Temperature Average
> > Threshold */
> > + u32 tmhtactr; /* High Temperature Average Crit
> > Threshold */
> > + u8 res3[0x24];
> > + u32 ttcfgr; /* Temperature Configuration
> > Register */
> > + u32 tscfgr; /* Sensor Configuration Register
> > */
> > + u8 res4[0x78];
> > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > + u8 res5[0x9f8];
> > + u32 ipbrr0; /* IP Block Revision Register 0
> > */
> > + u32 ipbrr1; /* IP Block Revision Register 1
> > */
> > + u8 res6[0x310];
> > + u32 ttr0cr; /* Temperature Range 0 Control
> > Register */
> > + u32 ttr1cr; /* Temperature Range 1 Control
> > Register */
> > + u32 ttr2cr; /* Temperature Range 2 Control
> > Register */
> > + u32 ttr3cr; /* Temperature Range 3 Control
> > Register */
> > +};
> > +
> > +/*
> > + * Thermal zone data
> > + */
> > +struct qoriq_tmu_data {
> > + struct thermal_zone_device *tz;
> > + struct qoriq_tmu_regs __iomem *regs;
> > + int sensor_id;
> > + bool little_endian;
> > +};
> > +
> > +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void
> > __iomem *addr)
> > +{
> > + if (p->little_endian)
> > + iowrite32(val, addr);
> > + else
> > + iowrite32be(val, addr);
> > +}
> > +
> > +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
> > +{
> > + if (p->little_endian)
> > + return ioread32(addr);
> > + else
> > + return ioread32be(addr);
> > +}
> > +
> > +static int tmu_get_temp(void *p, int *temp)
> > +{
> > + u32 val;
> > + struct qoriq_tmu_data *data = p;
> > +
> > + val = tmu_read(data, &data->regs->site[data-
> > >sensor_id].tritsr);
> > + *temp = (val & 0xff) * 1000;
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_get_sensor_id(void)
> > +{
> > + int ret, id;
> > + struct of_phandle_args sensor_specs;
> > + struct device_node *np, *sensor_np;
> > +
> > + np = of_find_node_by_name(NULL, "thermal-zones");
> > + if (!np)
> > + return -ENODEV;
> > +
> > + sensor_np = of_get_next_child(np, NULL);
> > + ret = of_parse_phandle_with_args(sensor_np, "thermal-
> > sensors",
> > + "#thermal-sensor-cells",
> > + 0, &sensor_specs);
> > + if (ret) {
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > + return ret;
> > + }
> > +
> > + if (sensor_specs.args_count >= 1) {
> > + id = sensor_specs.args[0];
> > + WARN(sensor_specs.args_count > 1,
> > + "%s: too many cells in sensor
> > specifier %d\n",
> > + sensor_specs.np->name,
> > sensor_specs.args_count);
> > + } else {
> > + id = 0;
> > + }
> > +
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > +
> > + return id;
> > +}
> > +
> > +static int qoriq_tmu_calibration(struct platform_device *pdev)
> > +{
> > + int i, val, len;
> > + u32 range[4];
> > + const u32 *calibration;
> > + struct device_node *np = pdev->dev.of_node;
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + if (of_property_read_u32_array(np, "fsl,tmu-range", range,
> > 4)) {
> > + dev_err(&pdev->dev, "missing calibration range.\n");
> > + return -ENODEV;
> > + }
> > +
> > + /* Init temperature range registers */
> > + tmu_write(data, range[0], &data->regs->ttr0cr);
> > + tmu_write(data, range[1], &data->regs->ttr1cr);
> > + tmu_write(data, range[2], &data->regs->ttr2cr);
> > + tmu_write(data, range[3], &data->regs->ttr3cr);
> > +
> > + calibration = of_get_property(np, "fsl,tmu-calibration",
> > &len);
> > + if (calibration == NULL || len % 8) {
> > + dev_err(&pdev->dev, "invalid calibration data.\n");
> > + return -ENODEV;
> > + }
> > +
> > + for (i = 0; i < len; i += 8, calibration += 2) {
> > + val = of_read_number(calibration, 1);
> > + tmu_write(data, val, &data->regs->ttcfgr);
> > + val = of_read_number(calibration + 1, 1);
> > + tmu_write(data, val, &data->regs->tscfgr);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
> > +{
> > + /* Disable interrupt, using polling instead */
> > + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> > +
> > + /* Set update_interval */
> > + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +}
> > +
> > +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> > + .get_temp = tmu_get_temp,
> > +};
> > +
> > +static int qoriq_tmu_probe(struct platform_device *pdev)
> > +{
> > + int ret;
> > + const struct thermal_trip *trip;
> > + struct qoriq_tmu_data *data;
> > + struct device_node *np = pdev->dev.of_node;
> > + u32 site = 0;
> > +
> > + if (!np) {
> > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > + return -ENODEV;
> > + }
> > +
> > + data = devm_kzalloc(&pdev->dev, sizeof(struct
> > qoriq_tmu_data),
> > + GFP_KERNEL);
> > + if (!data)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, data);
> > +
> > + data->little_endian = of_property_read_bool(np, "little-
> > endian");
> > +
> > + data->sensor_id = qoriq_tmu_get_sensor_id();
> > + if (data->sensor_id < 0) {
> > + dev_err(&pdev->dev, "Failed to get sensor id\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + data->regs = of_iomap(np, 0);
> > + if (!data->regs) {
> > + dev_err(&pdev->dev, "Failed to get memory
> > region\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + qoriq_tmu_init_device(data); /* TMU initialization */
> > +
> > + ret = qoriq_tmu_calibration(pdev); /* TMU calibration
> > */
> > + if (ret < 0)
> > + goto err_tmu;
> > +
> > + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data-
> > >sensor_id,
> > + data, &tmu_tz_ops);
> > + if (IS_ERR(data->tz)) {
> > + ret = PTR_ERR(data->tz);
> > + dev_err(&pdev->dev,
> > + "Failed to register thermal zone device
> > %d\n", ret);
> > + goto err_tmu;
> > + }
> > +
> > + trip = of_thermal_get_trip_points(data->tz);
> > +
> > + /* Enable monitoring */
> > + site |= 0x1 << (15 - data->sensor_id);
> > + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> > +
> > + return 0;
> > +
> > +err_tmu:
> > + iounmap(data->regs);
> > +
> > +err_iomap:
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return ret;
> > +}
> > +
> > +static int qoriq_tmu_remove(struct platform_device *pdev)
> > +{
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +
> > + iounmap(data->regs);
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int qoriq_tmu_suspend(struct device *dev)
> > +{
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Disable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr &= ~TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_resume(struct device *dev)
> > +{
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Enable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr |= TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> > + qoriq_tmu_suspend, qoriq_tmu_resume);
> > +
> > +static const struct of_device_id qoriq_tmu_match[] = {
> > + { .compatible = "fsl,qoriq-tmu", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> > +
> > +static struct platform_driver qoriq_tmu = {
> > + .driver = {
> > + .name = "qoriq_thermal",
> > + .pm = &qoriq_tmu_pm_ops,
> > + .of_match_table = qoriq_tmu_match,
> > + },
> > + .probe = qoriq_tmu_probe,
> > + .remove = qoriq_tmu_remove,
> > +};
> > +module_platform_driver(qoriq_tmu);
> > +
> > +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> > +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.1.0.27.g96db324
> >
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-09-08 7:11 ` Troy Jia
0 siblings, 0 replies; 18+ messages in thread
From: Troy Jia @ 2016-09-08 7:11 UTC (permalink / raw)
To: Zhang Rui, edubezval, robh+dt, galak, Scott Wood, shawnguo
Cc: devicetree, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-pm
> -----Original Message-----
> From: Zhang Rui [mailto:rui.zhang@intel.com]
> Sent: Friday, August 19, 2016 8:40 PM
> To: Hongtao Jia <hongtao.jia@nxp.com>; edubezval@gmail.com;
> robh+dt@kernel.org; galak@codeaurora.org; Scott Wood
> <scott.wood@nxp.com>; shawnguo@kernel.org
> Cc: linux-pm@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; linux-arm-
> kernel@lists.infradead.org
> Subject: Re: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> On 四, 2016-06-30 at 11:08 +0800, Jia Hongtao wrote:
> > This driver add thermal management support by enabling TMU (Thermal
> > Monitoring Unit) on QorIQ platform.
> >
> > It's based on thermal of framework:
> > - Trip points defined in device tree.
> > - Cpufreq as cooling device registered in qoriq cpufreq driver.
> >
> > Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
>
> The patch looks good to me.
> I only need to take this patch, right?
>
> thanks,
> rui
Yes. You just need to take this patch.
Sorry for the late response. I was on my long vacation back then.
Thanks,
Hongtao.
> > ---
> > Changes of V2:
> > * Add HAS_IOMEM dependency to fix build error on UM
> >
> > drivers/thermal/Kconfig | 10 ++
> > drivers/thermal/Makefile | 1 +
> > drivers/thermal/qoriq_thermal.c | 328
> > ++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 339 insertions(+)
> > create mode 100644 drivers/thermal/qoriq_thermal.c
> >
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > index 2d702ca..56ef30d 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -195,6 +195,16 @@ config IMX_THERMAL
> > cpufreq is used as the cooling device to throttle CPUs
> > when the
> > passive trip is crossed.
> >
> > +config QORIQ_THERMAL
> > + tristate "QorIQ Thermal Monitoring Unit"
> > + depends on THERMAL_OF
> > + depends on HAS_IOMEM
> > + help
> > + Support for Thermal Monitoring Unit (TMU) found on QorIQ
> > platforms.
> > + It supports one critical trip point and one passive trip
> > point. The
> > + cpufreq is used as the cooling device to throttle CPUs
> > when the
> > + passive trip is crossed.
> > +
> > config SPEAR_THERMAL
> > tristate "SPEAr thermal sensor driver"
> > depends on PLAT_SPEAR || COMPILE_TEST
> > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > index 10b07c1..6662232 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> > db8500_thermal.o
> > obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> > obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> > obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> > +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> > obj-$(CONFIG_DB8500_CPUFREQ_COOLING) +=
> > db8500_cpufreq_cooling.o
> > obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> > obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> > diff --git a/drivers/thermal/qoriq_thermal.c
> > b/drivers/thermal/qoriq_thermal.c
> > new file mode 100644
> > index 0000000..644ba52
> > --- /dev/null
> > +++ b/drivers/thermal/qoriq_thermal.c
> > @@ -0,0 +1,328 @@
> > +/*
> > + * Copyright 2016 Freescale Semiconductor, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but
> > WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> > License for
> > + * more details.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/err.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/thermal.h>
> > +
> > +#include "thermal_core.h"
> > +
> > +#define SITES_MAX 16
> > +
> > +/*
> > + * QorIQ TMU Registers
> > + */
> > +struct qoriq_tmu_site_regs {
> > + u32 tritsr; /* Immediate Temperature Site
> > Register */
> > + u32 tratsr; /* Average Temperature Site
> > Register */
> > + u8 res0[0x8];
> > +};
> > +
> > +struct qoriq_tmu_regs {
> > + u32 tmr; /* Mode Register */
> > +#define TMR_DISABLE 0x0
> > +#define TMR_ME 0x80000000
> > +#define TMR_ALPF 0x0c000000
> > + u32 tsr; /* Status Register */
> > + u32 tmtmir; /* Temperature measurement
> > interval Register */
> > +#define TMTMIR_DEFAULT 0x0000000f
> > + u8 res0[0x14];
> > + u32 tier; /* Interrupt Enable Register */
> > +#define TIER_DISABLE 0x0
> > + u32 tidr; /* Interrupt Detect Register */
> > + u32 tiscr; /* Interrupt Site Capture Register
> > */
> > + u32 ticscr; /* Interrupt Critical Site
> > Capture Register */
> > + u8 res1[0x10];
> > + u32 tmhtcrh; /* High Temperature Capture
> > Register */
> > + u32 tmhtcrl; /* Low Temperature Capture
> > Register */
> > + u8 res2[0x8];
> > + u32 tmhtitr; /* High Temperature Immediate
> > Threshold */
> > + u32 tmhtatr; /* High Temperature Average
> > Threshold */
> > + u32 tmhtactr; /* High Temperature Average Crit
> > Threshold */
> > + u8 res3[0x24];
> > + u32 ttcfgr; /* Temperature Configuration
> > Register */
> > + u32 tscfgr; /* Sensor Configuration Register
> > */
> > + u8 res4[0x78];
> > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > + u8 res5[0x9f8];
> > + u32 ipbrr0; /* IP Block Revision Register 0
> > */
> > + u32 ipbrr1; /* IP Block Revision Register 1
> > */
> > + u8 res6[0x310];
> > + u32 ttr0cr; /* Temperature Range 0 Control
> > Register */
> > + u32 ttr1cr; /* Temperature Range 1 Control
> > Register */
> > + u32 ttr2cr; /* Temperature Range 2 Control
> > Register */
> > + u32 ttr3cr; /* Temperature Range 3 Control
> > Register */
> > +};
> > +
> > +/*
> > + * Thermal zone data
> > + */
> > +struct qoriq_tmu_data {
> > + struct thermal_zone_device *tz;
> > + struct qoriq_tmu_regs __iomem *regs;
> > + int sensor_id;
> > + bool little_endian;
> > +};
> > +
> > +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void
> > __iomem *addr)
> > +{
> > + if (p->little_endian)
> > + iowrite32(val, addr);
> > + else
> > + iowrite32be(val, addr);
> > +}
> > +
> > +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
> > +{
> > + if (p->little_endian)
> > + return ioread32(addr);
> > + else
> > + return ioread32be(addr);
> > +}
> > +
> > +static int tmu_get_temp(void *p, int *temp)
> > +{
> > + u32 val;
> > + struct qoriq_tmu_data *data = p;
> > +
> > + val = tmu_read(data, &data->regs->site[data-
> > >sensor_id].tritsr);
> > + *temp = (val & 0xff) * 1000;
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_get_sensor_id(void)
> > +{
> > + int ret, id;
> > + struct of_phandle_args sensor_specs;
> > + struct device_node *np, *sensor_np;
> > +
> > + np = of_find_node_by_name(NULL, "thermal-zones");
> > + if (!np)
> > + return -ENODEV;
> > +
> > + sensor_np = of_get_next_child(np, NULL);
> > + ret = of_parse_phandle_with_args(sensor_np, "thermal-
> > sensors",
> > + "#thermal-sensor-cells",
> > + 0, &sensor_specs);
> > + if (ret) {
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > + return ret;
> > + }
> > +
> > + if (sensor_specs.args_count >= 1) {
> > + id = sensor_specs.args[0];
> > + WARN(sensor_specs.args_count > 1,
> > + "%s: too many cells in sensor
> > specifier %d\n",
> > + sensor_specs.np->name,
> > sensor_specs.args_count);
> > + } else {
> > + id = 0;
> > + }
> > +
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > +
> > + return id;
> > +}
> > +
> > +static int qoriq_tmu_calibration(struct platform_device *pdev)
> > +{
> > + int i, val, len;
> > + u32 range[4];
> > + const u32 *calibration;
> > + struct device_node *np = pdev->dev.of_node;
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + if (of_property_read_u32_array(np, "fsl,tmu-range", range,
> > 4)) {
> > + dev_err(&pdev->dev, "missing calibration range.\n");
> > + return -ENODEV;
> > + }
> > +
> > + /* Init temperature range registers */
> > + tmu_write(data, range[0], &data->regs->ttr0cr);
> > + tmu_write(data, range[1], &data->regs->ttr1cr);
> > + tmu_write(data, range[2], &data->regs->ttr2cr);
> > + tmu_write(data, range[3], &data->regs->ttr3cr);
> > +
> > + calibration = of_get_property(np, "fsl,tmu-calibration",
> > &len);
> > + if (calibration == NULL || len % 8) {
> > + dev_err(&pdev->dev, "invalid calibration data.\n");
> > + return -ENODEV;
> > + }
> > +
> > + for (i = 0; i < len; i += 8, calibration += 2) {
> > + val = of_read_number(calibration, 1);
> > + tmu_write(data, val, &data->regs->ttcfgr);
> > + val = of_read_number(calibration + 1, 1);
> > + tmu_write(data, val, &data->regs->tscfgr);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
> > +{
> > + /* Disable interrupt, using polling instead */
> > + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> > +
> > + /* Set update_interval */
> > + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +}
> > +
> > +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> > + .get_temp = tmu_get_temp,
> > +};
> > +
> > +static int qoriq_tmu_probe(struct platform_device *pdev)
> > +{
> > + int ret;
> > + const struct thermal_trip *trip;
> > + struct qoriq_tmu_data *data;
> > + struct device_node *np = pdev->dev.of_node;
> > + u32 site = 0;
> > +
> > + if (!np) {
> > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > + return -ENODEV;
> > + }
> > +
> > + data = devm_kzalloc(&pdev->dev, sizeof(struct
> > qoriq_tmu_data),
> > + GFP_KERNEL);
> > + if (!data)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, data);
> > +
> > + data->little_endian = of_property_read_bool(np, "little-
> > endian");
> > +
> > + data->sensor_id = qoriq_tmu_get_sensor_id();
> > + if (data->sensor_id < 0) {
> > + dev_err(&pdev->dev, "Failed to get sensor id\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + data->regs = of_iomap(np, 0);
> > + if (!data->regs) {
> > + dev_err(&pdev->dev, "Failed to get memory
> > region\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + qoriq_tmu_init_device(data); /* TMU initialization */
> > +
> > + ret = qoriq_tmu_calibration(pdev); /* TMU calibration
> > */
> > + if (ret < 0)
> > + goto err_tmu;
> > +
> > + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data-
> > >sensor_id,
> > + data, &tmu_tz_ops);
> > + if (IS_ERR(data->tz)) {
> > + ret = PTR_ERR(data->tz);
> > + dev_err(&pdev->dev,
> > + "Failed to register thermal zone device
> > %d\n", ret);
> > + goto err_tmu;
> > + }
> > +
> > + trip = of_thermal_get_trip_points(data->tz);
> > +
> > + /* Enable monitoring */
> > + site |= 0x1 << (15 - data->sensor_id);
> > + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> > +
> > + return 0;
> > +
> > +err_tmu:
> > + iounmap(data->regs);
> > +
> > +err_iomap:
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return ret;
> > +}
> > +
> > +static int qoriq_tmu_remove(struct platform_device *pdev)
> > +{
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +
> > + iounmap(data->regs);
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int qoriq_tmu_suspend(struct device *dev)
> > +{
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Disable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr &= ~TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_resume(struct device *dev)
> > +{
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Enable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr |= TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> > + qoriq_tmu_suspend, qoriq_tmu_resume);
> > +
> > +static const struct of_device_id qoriq_tmu_match[] = {
> > + { .compatible = "fsl,qoriq-tmu", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> > +
> > +static struct platform_driver qoriq_tmu = {
> > + .driver = {
> > + .name = "qoriq_thermal",
> > + .pm = &qoriq_tmu_pm_ops,
> > + .of_match_table = qoriq_tmu_match,
> > + },
> > + .probe = qoriq_tmu_probe,
> > + .remove = qoriq_tmu_remove,
> > +};
> > +module_platform_driver(qoriq_tmu);
> > +
> > +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> > +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.1.0.27.g96db324
> >
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-09-08 7:11 ` Troy Jia
0 siblings, 0 replies; 18+ messages in thread
From: Troy Jia @ 2016-09-08 7:11 UTC (permalink / raw)
To: Zhang Rui, edubezval, robh+dt, galak, Scott Wood, shawnguo
Cc: linux-pm, devicetree, linux-kernel, linuxppc-dev, linux-arm-kernel
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogWmhhbmcgUnVpIFttYWls
dG86cnVpLnpoYW5nQGludGVsLmNvbV0NCj4gU2VudDogRnJpZGF5LCBBdWd1c3QgMTksIDIwMTYg
ODo0MCBQTQ0KPiBUbzogSG9uZ3RhbyBKaWEgPGhvbmd0YW8uamlhQG54cC5jb20+OyBlZHViZXp2
YWxAZ21haWwuY29tOw0KPiByb2JoK2R0QGtlcm5lbC5vcmc7IGdhbGFrQGNvZGVhdXJvcmEub3Jn
OyBTY290dCBXb29kDQo+IDxzY290dC53b29kQG54cC5jb20+OyBzaGF3bmd1b0BrZXJuZWwub3Jn
DQo+IENjOiBsaW51eC1wbUB2Z2VyLmtlcm5lbC5vcmc7IGRldmljZXRyZWVAdmdlci5rZXJuZWwu
b3JnOyBsaW51eC0NCj4ga2VybmVsQHZnZXIua2VybmVsLm9yZzsgbGludXhwcGMtZGV2QGxpc3Rz
Lm96bGFicy5vcmc7IGxpbnV4LWFybS0NCj4ga2VybmVsQGxpc3RzLmluZnJhZGVhZC5vcmcNCj4g
U3ViamVjdDogUmU6IFtQQVRDSCBWMiA3LzddIHRoZXJtYWw6IHFvcmlxOiBBZGQgdGhlcm1hbCBt
YW5hZ2VtZW50IHN1cHBvcnQNCj4gDQo+IE9uIOWbmywgMjAxNi0wNi0zMCBhdCAxMTowOCArMDgw
MCwgSmlhIEhvbmd0YW8gd3JvdGU6DQo+ID4gVGhpcyBkcml2ZXIgYWRkIHRoZXJtYWwgbWFuYWdl
bWVudCBzdXBwb3J0IGJ5IGVuYWJsaW5nIFRNVSAoVGhlcm1hbA0KPiA+IE1vbml0b3JpbmcgVW5p
dCkgb24gUW9ySVEgcGxhdGZvcm0uDQo+ID4NCj4gPiBJdCdzIGJhc2VkIG9uIHRoZXJtYWwgb2Yg
ZnJhbWV3b3JrOg0KPiA+IC0gVHJpcCBwb2ludHMgZGVmaW5lZCBpbiBkZXZpY2UgdHJlZS4NCj4g
PiAtIENwdWZyZXEgYXMgY29vbGluZyBkZXZpY2UgcmVnaXN0ZXJlZCBpbiBxb3JpcSBjcHVmcmVx
IGRyaXZlci4NCj4gPg0KPiA+IFNpZ25lZC1vZmYtYnk6IEppYSBIb25ndGFvIDxob25ndGFvLmpp
YUBueHAuY29tPg0KPiANCj4gVGhlIHBhdGNoIGxvb2tzIGdvb2QgdG8gbWUuDQo+IEkgb25seSBu
ZWVkIHRvIHRha2UgdGhpcyBwYXRjaCwgcmlnaHQ/DQo+IA0KPiB0aGFua3MsDQo+IHJ1aQ0KDQpZ
ZXMuIFlvdSBqdXN0IG5lZWQgdG8gdGFrZSB0aGlzIHBhdGNoLg0KU29ycnkgZm9yIHRoZSBsYXRl
IHJlc3BvbnNlLiBJIHdhcyBvbiBteSBsb25nIHZhY2F0aW9uIGJhY2sgdGhlbi4NCg0KVGhhbmtz
LA0KSG9uZ3Rhby4NCg0KPiA+IC0tLQ0KPiA+IENoYW5nZXMgb2YgVjI6DQo+ID4gKiBBZGQgSEFT
X0lPTUVNIGRlcGVuZGVuY3kgdG8gZml4IGJ1aWxkIGVycm9yIG9uIFVNDQo+ID4NCj4gPiDCoGRy
aXZlcnMvdGhlcm1hbC9LY29uZmlnwqDCoMKgwqDCoMKgwqDCoMKgfMKgwqAxMCArKw0KPiA+IMKg
ZHJpdmVycy90aGVybWFsL01ha2VmaWxlwqDCoMKgwqDCoMKgwqDCoHzCoMKgwqAxICsNCj4gPiDC
oGRyaXZlcnMvdGhlcm1hbC9xb3JpcV90aGVybWFsLmMgfCAzMjgNCj4gPiArKysrKysrKysrKysr
KysrKysrKysrKysrKysrKysrKysrKysrKysrDQo+ID4gwqAzIGZpbGVzIGNoYW5nZWQsIDMzOSBp
bnNlcnRpb25zKCspDQo+ID4gwqBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy90aGVybWFsL3Fv
cmlxX3RoZXJtYWwuYw0KPiA+DQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvdGhlcm1hbC9LY29u
ZmlnIGIvZHJpdmVycy90aGVybWFsL0tjb25maWcNCj4gPiBpbmRleCAyZDcwMmNhLi41NmVmMzBk
IDEwMDY0NA0KPiA+IC0tLSBhL2RyaXZlcnMvdGhlcm1hbC9LY29uZmlnDQo+ID4gKysrIGIvZHJp
dmVycy90aGVybWFsL0tjb25maWcNCj4gPiBAQCAtMTk1LDYgKzE5NSwxNiBAQCBjb25maWcgSU1Y
X1RIRVJNQUwNCj4gPiDCoAnCoMKgY3B1ZnJlcSBpcyB1c2VkIGFzIHRoZSBjb29saW5nIGRldmlj
ZSB0byB0aHJvdHRsZSBDUFVzDQo+ID4gd2hlbiB0aGUNCj4gPiDCoAnCoMKgcGFzc2l2ZSB0cmlw
IGlzIGNyb3NzZWQuDQo+ID4NCj4gPiArY29uZmlnIFFPUklRX1RIRVJNQUwNCj4gPiArCXRyaXN0
YXRlICJRb3JJUSBUaGVybWFsIE1vbml0b3JpbmcgVW5pdCINCj4gPiArCWRlcGVuZHMgb24gVEhF
Uk1BTF9PRg0KPiA+ICsJZGVwZW5kcyBvbiBIQVNfSU9NRU0NCj4gPiArCWhlbHANCj4gPiArCcKg
wqBTdXBwb3J0IGZvciBUaGVybWFsIE1vbml0b3JpbmcgVW5pdCAoVE1VKSBmb3VuZCBvbiBRb3JJ
UQ0KPiA+IHBsYXRmb3Jtcy4NCj4gPiArCcKgwqBJdCBzdXBwb3J0cyBvbmUgY3JpdGljYWwgdHJp
cCBwb2ludCBhbmQgb25lIHBhc3NpdmUgdHJpcA0KPiA+IHBvaW50LiBUaGUNCj4gPiArCcKgwqBj
cHVmcmVxIGlzIHVzZWQgYXMgdGhlIGNvb2xpbmcgZGV2aWNlIHRvIHRocm90dGxlIENQVXMNCj4g
PiB3aGVuIHRoZQ0KPiA+ICsJwqDCoHBhc3NpdmUgdHJpcCBpcyBjcm9zc2VkLg0KPiA+ICsNCj4g
PiDCoGNvbmZpZyBTUEVBUl9USEVSTUFMDQo+ID4gwqAJdHJpc3RhdGUgIlNQRUFyIHRoZXJtYWwg
c2Vuc29yIGRyaXZlciINCj4gPiDCoAlkZXBlbmRzIG9uIFBMQVRfU1BFQVIgfHwgQ09NUElMRV9U
RVNUDQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvdGhlcm1hbC9NYWtlZmlsZSBiL2RyaXZlcnMv
dGhlcm1hbC9NYWtlZmlsZQ0KPiA+IGluZGV4IDEwYjA3YzEuLjY2NjIyMzIgMTAwNjQ0DQo+ID4g
LS0tIGEvZHJpdmVycy90aGVybWFsL01ha2VmaWxlDQo+ID4gKysrIGIvZHJpdmVycy90aGVybWFs
L01ha2VmaWxlDQo+ID4gQEAgLTM3LDYgKzM3LDcgQEAgb2JqLSQoQ09ORklHX0RCODUwMF9USEVS
TUFMKQkrPQ0KPiA+IGRiODUwMF90aGVybWFsLm8NCj4gPiDCoG9iai0kKENPTkZJR19BUk1BREFf
VEhFUk1BTCkJKz0gYXJtYWRhX3RoZXJtYWwubw0KPiA+IMKgb2JqLSQoQ09ORklHX1RBTkdPX1RI
RVJNQUwpCSs9IHRhbmdvX3RoZXJtYWwubw0KPiA+IMKgb2JqLSQoQ09ORklHX0lNWF9USEVSTUFM
KQkrPSBpbXhfdGhlcm1hbC5vDQo+ID4gK29iai0kKENPTkZJR19RT1JJUV9USEVSTUFMKQkrPSBx
b3JpcV90aGVybWFsLm8NCj4gPiDCoG9iai0kKENPTkZJR19EQjg1MDBfQ1BVRlJFUV9DT09MSU5H
KQkrPQ0KPiA+IGRiODUwMF9jcHVmcmVxX2Nvb2xpbmcubw0KPiA+IMKgb2JqLSQoQ09ORklHX0lO
VEVMX1BPV0VSQ0xBTVApCSs9IGludGVsX3Bvd2VyY2xhbXAubw0KPiA+IMKgb2JqLSQoQ09ORklH
X1g4Nl9QS0dfVEVNUF9USEVSTUFMKQkrPSB4ODZfcGtnX3RlbXBfdGhlcm1hbC5vDQo+ID4gZGlm
ZiAtLWdpdCBhL2RyaXZlcnMvdGhlcm1hbC9xb3JpcV90aGVybWFsLmMNCj4gPiBiL2RyaXZlcnMv
dGhlcm1hbC9xb3JpcV90aGVybWFsLmMNCj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiA+IGlu
ZGV4IDAwMDAwMDAuLjY0NGJhNTINCj4gPiAtLS0gL2Rldi9udWxsDQo+ID4gKysrIGIvZHJpdmVy
cy90aGVybWFsL3FvcmlxX3RoZXJtYWwuYw0KPiA+IEBAIC0wLDAgKzEsMzI4IEBADQo+ID4gKy8q
DQo+ID4gKyAqIENvcHlyaWdodCAyMDE2IEZyZWVzY2FsZSBTZW1pY29uZHVjdG9yLCBJbmMuDQo+
ID4gKyAqDQo+ID4gKyAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJl
ZGlzdHJpYnV0ZSBpdCBhbmQvb3INCj4gPiBtb2RpZnkgaXQNCj4gPiArICogdW5kZXIgdGhlIHRl
cm1zIGFuZCBjb25kaXRpb25zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSwNCj4g
PiArICogdmVyc2lvbiAyLCBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRh
dGlvbi4NCj4gPiArICoNCj4gPiArICogVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVkIGluIHRo
ZSBob3BlIGl0IHdpbGwgYmUgdXNlZnVsLCBidXQNCj4gPiBXSVRIT1VUDQo+ID4gKyAqIEFOWSBX
QVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mDQo+ID4gTUVSQ0hB
TlRBQklMSVRZIG9yDQo+ID4gKyAqIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLsKg
wqBTZWUgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYw0KPiA+IExpY2Vuc2UgZm9yDQo+ID4gKyAqIG1v
cmUgZGV0YWlscy4NCj4gPiArICoNCj4gPiArICovDQo+ID4gKw0KPiA+ICsjaW5jbHVkZSA8bGlu
dXgvbW9kdWxlLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9wbGF0Zm9ybV9kZXZpY2UuaD4NCj4g
PiArI2luY2x1ZGUgPGxpbnV4L2Vyci5oPg0KPiA+ICsjaW5jbHVkZSA8bGludXgvaW8uaD4NCj4g
PiArI2luY2x1ZGUgPGxpbnV4L29mLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9vZl9hZGRyZXNz
Lmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC90aGVybWFsLmg+DQo+ID4gKw0KPiA+ICsjaW5jbHVk
ZSAidGhlcm1hbF9jb3JlLmgiDQo+ID4gKw0KPiA+ICsjZGVmaW5lIFNJVEVTX01BWAkxNg0KPiA+
ICsNCj4gPiArLyoNCj4gPiArICogUW9ySVEgVE1VIFJlZ2lzdGVycw0KPiA+ICsgKi8NCj4gPiAr
c3RydWN0IHFvcmlxX3RtdV9zaXRlX3JlZ3Mgew0KPiA+ICsJdTMyIHRyaXRzcjsJCS8qIEltbWVk
aWF0ZSBUZW1wZXJhdHVyZSBTaXRlDQo+ID4gUmVnaXN0ZXIgKi8NCj4gPiArCXUzMiB0cmF0c3I7
CQkvKiBBdmVyYWdlIFRlbXBlcmF0dXJlIFNpdGUNCj4gPiBSZWdpc3RlciAqLw0KPiA+ICsJdTgg
cmVzMFsweDhdOw0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArc3RydWN0IHFvcmlxX3RtdV9yZWdzIHsN
Cj4gPiArCXUzMiB0bXI7CQkvKiBNb2RlIFJlZ2lzdGVyICovDQo+ID4gKyNkZWZpbmUgVE1SX0RJ
U0FCTEUJMHgwDQo+ID4gKyNkZWZpbmUgVE1SX01FCQkweDgwMDAwMDAwDQo+ID4gKyNkZWZpbmUg
VE1SX0FMUEYJMHgwYzAwMDAwMA0KPiA+ICsJdTMyIHRzcjsJCS8qIFN0YXR1cyBSZWdpc3RlciAq
Lw0KPiA+ICsJdTMyIHRtdG1pcjsJCS8qIFRlbXBlcmF0dXJlIG1lYXN1cmVtZW50DQo+ID4gaW50
ZXJ2YWwgUmVnaXN0ZXIgKi8NCj4gPiArI2RlZmluZSBUTVRNSVJfREVGQVVMVAkweDAwMDAwMDBm
DQo+ID4gKwl1OCByZXMwWzB4MTRdOw0KPiA+ICsJdTMyIHRpZXI7CQkvKiBJbnRlcnJ1cHQgRW5h
YmxlIFJlZ2lzdGVyICovDQo+ID4gKyNkZWZpbmUgVElFUl9ESVNBQkxFCTB4MA0KPiA+ICsJdTMy
IHRpZHI7CQkvKiBJbnRlcnJ1cHQgRGV0ZWN0IFJlZ2lzdGVyICovDQo+ID4gKwl1MzIgdGlzY3I7
CQkvKiBJbnRlcnJ1cHQgU2l0ZSBDYXB0dXJlIFJlZ2lzdGVyDQo+ID4gKi8NCj4gPiArCXUzMiB0
aWNzY3I7CQkvKiBJbnRlcnJ1cHQgQ3JpdGljYWwgU2l0ZQ0KPiA+IENhcHR1cmUgUmVnaXN0ZXIg
Ki8NCj4gPiArCXU4IHJlczFbMHgxMF07DQo+ID4gKwl1MzIgdG1odGNyaDsJCS8qIEhpZ2ggVGVt
cGVyYXR1cmUgQ2FwdHVyZQ0KPiA+IFJlZ2lzdGVyICovDQo+ID4gKwl1MzIgdG1odGNybDsJCS8q
IExvdyBUZW1wZXJhdHVyZSBDYXB0dXJlDQo+ID4gUmVnaXN0ZXIgKi8NCj4gPiArCXU4IHJlczJb
MHg4XTsNCj4gPiArCXUzMiB0bWh0aXRyOwkJLyogSGlnaCBUZW1wZXJhdHVyZSBJbW1lZGlhdGUN
Cj4gPiBUaHJlc2hvbGQgKi8NCj4gPiArCXUzMiB0bWh0YXRyOwkJLyogSGlnaCBUZW1wZXJhdHVy
ZSBBdmVyYWdlDQo+ID4gVGhyZXNob2xkICovDQo+ID4gKwl1MzIgdG1odGFjdHI7CS8qIEhpZ2gg
VGVtcGVyYXR1cmUgQXZlcmFnZSBDcml0DQo+ID4gVGhyZXNob2xkICovDQo+ID4gKwl1OCByZXMz
WzB4MjRdOw0KPiA+ICsJdTMyIHR0Y2ZncjsJCS8qIFRlbXBlcmF0dXJlIENvbmZpZ3VyYXRpb24N
Cj4gPiBSZWdpc3RlciAqLw0KPiA+ICsJdTMyIHRzY2ZncjsJCS8qIFNlbnNvciBDb25maWd1cmF0
aW9uIFJlZ2lzdGVyDQo+ID4gKi8NCj4gPiArCXU4IHJlczRbMHg3OF07DQo+ID4gKwlzdHJ1Y3Qg
cW9yaXFfdG11X3NpdGVfcmVncyBzaXRlW1NJVEVTX01BWF07DQo+ID4gKwl1OCByZXM1WzB4OWY4
XTsNCj4gPiArCXUzMiBpcGJycjA7CQkvKiBJUCBCbG9jayBSZXZpc2lvbiBSZWdpc3RlciAwDQo+
ID4gKi8NCj4gPiArCXUzMiBpcGJycjE7CQkvKiBJUCBCbG9jayBSZXZpc2lvbiBSZWdpc3RlciAx
DQo+ID4gKi8NCj4gPiArCXU4IHJlczZbMHgzMTBdOw0KPiA+ICsJdTMyIHR0cjBjcjsJCS8qIFRl
bXBlcmF0dXJlIFJhbmdlIDAgQ29udHJvbA0KPiA+IFJlZ2lzdGVyICovDQo+ID4gKwl1MzIgdHRy
MWNyOwkJLyogVGVtcGVyYXR1cmUgUmFuZ2UgMSBDb250cm9sDQo+ID4gUmVnaXN0ZXIgKi8NCj4g
PiArCXUzMiB0dHIyY3I7CQkvKiBUZW1wZXJhdHVyZSBSYW5nZSAyIENvbnRyb2wNCj4gPiBSZWdp
c3RlciAqLw0KPiA+ICsJdTMyIHR0cjNjcjsJCS8qIFRlbXBlcmF0dXJlIFJhbmdlIDMgQ29udHJv
bA0KPiA+IFJlZ2lzdGVyICovDQo+ID4gK307DQo+ID4gKw0KPiA+ICsvKg0KPiA+ICsgKiBUaGVy
bWFsIHpvbmUgZGF0YQ0KPiA+ICsgKi8NCj4gPiArc3RydWN0IHFvcmlxX3RtdV9kYXRhIHsNCj4g
PiArCXN0cnVjdCB0aGVybWFsX3pvbmVfZGV2aWNlICp0ejsNCj4gPiArCXN0cnVjdCBxb3JpcV90
bXVfcmVncyBfX2lvbWVtICpyZWdzOw0KPiA+ICsJaW50IHNlbnNvcl9pZDsNCj4gPiArCWJvb2wg
bGl0dGxlX2VuZGlhbjsNCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0YXRpYyB2b2lkIHRtdV93cml0
ZShzdHJ1Y3QgcW9yaXFfdG11X2RhdGEgKnAsIHUzMiB2YWwsIHZvaWQNCj4gPiBfX2lvbWVtICph
ZGRyKQ0KPiA+ICt7DQo+ID4gKwlpZiAocC0+bGl0dGxlX2VuZGlhbikNCj4gPiArCQlpb3dyaXRl
MzIodmFsLCBhZGRyKTsNCj4gPiArCWVsc2UNCj4gPiArCQlpb3dyaXRlMzJiZSh2YWwsIGFkZHIp
Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgdTMyIHRtdV9yZWFkKHN0cnVjdCBxb3JpcV90
bXVfZGF0YSAqcCwgdm9pZCBfX2lvbWVtICphZGRyKQ0KPiA+ICt7DQo+ID4gKwlpZiAocC0+bGl0
dGxlX2VuZGlhbikNCj4gPiArCQlyZXR1cm4gaW9yZWFkMzIoYWRkcik7DQo+ID4gKwllbHNlDQo+
ID4gKwkJcmV0dXJuIGlvcmVhZDMyYmUoYWRkcik7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRp
YyBpbnQgdG11X2dldF90ZW1wKHZvaWQgKnAsIGludCAqdGVtcCkNCj4gPiArew0KPiA+ICsJdTMy
IHZhbDsNCj4gPiArCXN0cnVjdCBxb3JpcV90bXVfZGF0YSAqZGF0YSA9IHA7DQo+ID4gKw0KPiA+
ICsJdmFsID0gdG11X3JlYWQoZGF0YSwgJmRhdGEtPnJlZ3MtPnNpdGVbZGF0YS0NCj4gPiA+c2Vu
c29yX2lkXS50cml0c3IpOw0KPiA+ICsJKnRlbXAgPSAodmFsICYgMHhmZikgKiAxMDAwOw0KPiA+
ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IHFvcmlx
X3RtdV9nZXRfc2Vuc29yX2lkKHZvaWQpDQo+ID4gK3sNCj4gPiArCWludCByZXQsIGlkOw0KPiA+
ICsJc3RydWN0IG9mX3BoYW5kbGVfYXJncyBzZW5zb3Jfc3BlY3M7DQo+ID4gKwlzdHJ1Y3QgZGV2
aWNlX25vZGUgKm5wLCAqc2Vuc29yX25wOw0KPiA+ICsNCj4gPiArCW5wID0gb2ZfZmluZF9ub2Rl
X2J5X25hbWUoTlVMTCwgInRoZXJtYWwtem9uZXMiKTsNCj4gPiArCWlmICghbnApDQo+ID4gKwkJ
cmV0dXJuIC1FTk9ERVY7DQo+ID4gKw0KPiA+ICsJc2Vuc29yX25wID0gb2ZfZ2V0X25leHRfY2hp
bGQobnAsIE5VTEwpOw0KPiA+ICsJcmV0ID0gb2ZfcGFyc2VfcGhhbmRsZV93aXRoX2FyZ3Moc2Vu
c29yX25wLCAidGhlcm1hbC0NCj4gPiBzZW5zb3JzIiwNCj4gPiArCQkJIiN0aGVybWFsLXNlbnNv
ci1jZWxscyIsDQo+ID4gKwkJCTAsICZzZW5zb3Jfc3BlY3MpOw0KPiA+ICsJaWYgKHJldCkgew0K
PiA+ICsJCW9mX25vZGVfcHV0KG5wKTsNCj4gPiArCQlvZl9ub2RlX3B1dChzZW5zb3JfbnApOw0K
PiA+ICsJCXJldHVybiByZXQ7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJaWYgKHNlbnNvcl9zcGVj
cy5hcmdzX2NvdW50ID49IDEpIHsNCj4gPiArCQlpZCA9IHNlbnNvcl9zcGVjcy5hcmdzWzBdOw0K
PiA+ICsJCVdBUk4oc2Vuc29yX3NwZWNzLmFyZ3NfY291bnQgPiAxLA0KPiA+ICsJCQkJIiVzOiB0
b28gbWFueSBjZWxscyBpbiBzZW5zb3INCj4gPiBzcGVjaWZpZXIgJWRcbiIsDQo+ID4gKwkJCQlz
ZW5zb3Jfc3BlY3MubnAtPm5hbWUsDQo+ID4gc2Vuc29yX3NwZWNzLmFyZ3NfY291bnQpOw0KPiA+
ICsJfSBlbHNlIHsNCj4gPiArCQlpZCA9IDA7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJb2Zfbm9k
ZV9wdXQobnApOw0KPiA+ICsJb2Zfbm9kZV9wdXQoc2Vuc29yX25wKTsNCj4gPiArDQo+ID4gKwly
ZXR1cm4gaWQ7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgcW9yaXFfdG11X2NhbGli
cmF0aW9uKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpDQo+ID4gK3sNCj4gPiArCWludCBp
LCB2YWwsIGxlbjsNCj4gPiArCXUzMiByYW5nZVs0XTsNCj4gPiArCWNvbnN0IHUzMiAqY2FsaWJy
YXRpb247DQo+ID4gKwlzdHJ1Y3QgZGV2aWNlX25vZGUgKm5wID0gcGRldi0+ZGV2Lm9mX25vZGU7
DQo+ID4gKwlzdHJ1Y3QgcW9yaXFfdG11X2RhdGEgKmRhdGEgPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0
YShwZGV2KTsNCj4gPiArDQo+ID4gKwlpZiAob2ZfcHJvcGVydHlfcmVhZF91MzJfYXJyYXkobnAs
ICJmc2wsdG11LXJhbmdlIiwgcmFuZ2UsDQo+ID4gNCkpIHsNCj4gPiArCQlkZXZfZXJyKCZwZGV2
LT5kZXYsICJtaXNzaW5nIGNhbGlicmF0aW9uIHJhbmdlLlxuIik7DQo+ID4gKwkJcmV0dXJuIC1F
Tk9ERVY7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJLyogSW5pdCB0ZW1wZXJhdHVyZSByYW5nZSBy
ZWdpc3RlcnMgKi8NCj4gPiArCXRtdV93cml0ZShkYXRhLCByYW5nZVswXSwgJmRhdGEtPnJlZ3Mt
PnR0cjBjcik7DQo+ID4gKwl0bXVfd3JpdGUoZGF0YSwgcmFuZ2VbMV0sICZkYXRhLT5yZWdzLT50
dHIxY3IpOw0KPiA+ICsJdG11X3dyaXRlKGRhdGEsIHJhbmdlWzJdLCAmZGF0YS0+cmVncy0+dHRy
MmNyKTsNCj4gPiArCXRtdV93cml0ZShkYXRhLCByYW5nZVszXSwgJmRhdGEtPnJlZ3MtPnR0cjNj
cik7DQo+ID4gKw0KPiA+ICsJY2FsaWJyYXRpb24gPSBvZl9nZXRfcHJvcGVydHkobnAsICJmc2ws
dG11LWNhbGlicmF0aW9uIiwNCj4gPiAmbGVuKTsNCj4gPiArCWlmIChjYWxpYnJhdGlvbiA9PSBO
VUxMIHx8IGxlbiAlIDgpIHsNCj4gPiArCQlkZXZfZXJyKCZwZGV2LT5kZXYsICJpbnZhbGlkIGNh
bGlicmF0aW9uIGRhdGEuXG4iKTsNCj4gPiArCQlyZXR1cm4gLUVOT0RFVjsNCj4gPiArCX0NCj4g
PiArDQo+ID4gKwlmb3IgKGkgPSAwOyBpIDwgbGVuOyBpICs9IDgsIGNhbGlicmF0aW9uICs9IDIp
IHsNCj4gPiArCQl2YWwgPSBvZl9yZWFkX251bWJlcihjYWxpYnJhdGlvbiwgMSk7DQo+ID4gKwkJ
dG11X3dyaXRlKGRhdGEsIHZhbCwgJmRhdGEtPnJlZ3MtPnR0Y2Zncik7DQo+ID4gKwkJdmFsID0g
b2ZfcmVhZF9udW1iZXIoY2FsaWJyYXRpb24gKyAxLCAxKTsNCj4gPiArCQl0bXVfd3JpdGUoZGF0
YSwgdmFsLCAmZGF0YS0+cmVncy0+dHNjZmdyKTsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXR1
cm4gMDsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIHZvaWQgcW9yaXFfdG11X2luaXRfZGV2
aWNlKHN0cnVjdCBxb3JpcV90bXVfZGF0YSAqZGF0YSkNCj4gPiArew0KPiA+ICsJLyogRGlzYWJs
ZSBpbnRlcnJ1cHQsIHVzaW5nIHBvbGxpbmcgaW5zdGVhZCAqLw0KPiA+ICsJdG11X3dyaXRlKGRh
dGEsIFRJRVJfRElTQUJMRSwgJmRhdGEtPnJlZ3MtPnRpZXIpOw0KPiA+ICsNCj4gPiArCS8qIFNl
dCB1cGRhdGVfaW50ZXJ2YWwgKi8NCj4gPiArCXRtdV93cml0ZShkYXRhLCBUTVRNSVJfREVGQVVM
VCwgJmRhdGEtPnJlZ3MtPnRtdG1pcik7DQo+ID4gKw0KPiA+ICsJLyogRGlzYWJsZSBtb25pdG9y
aW5nICovDQo+ID4gKwl0bXVfd3JpdGUoZGF0YSwgVE1SX0RJU0FCTEUsICZkYXRhLT5yZWdzLT50
bXIpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgc3RydWN0IHRoZXJtYWxfem9uZV9vZl9k
ZXZpY2Vfb3BzIHRtdV90el9vcHMgPSB7DQo+ID4gKwkuZ2V0X3RlbXAgPSB0bXVfZ2V0X3RlbXAs
DQo+ID4gK307DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IHFvcmlxX3RtdV9wcm9iZShzdHJ1Y3Qg
cGxhdGZvcm1fZGV2aWNlICpwZGV2KQ0KPiA+ICt7DQo+ID4gKwlpbnQgcmV0Ow0KPiA+ICsJY29u
c3Qgc3RydWN0IHRoZXJtYWxfdHJpcCAqdHJpcDsNCj4gPiArCXN0cnVjdCBxb3JpcV90bXVfZGF0
YSAqZGF0YTsNCj4gPiArCXN0cnVjdCBkZXZpY2Vfbm9kZSAqbnAgPSBwZGV2LT5kZXYub2Zfbm9k
ZTsNCj4gPiArCXUzMiBzaXRlID0gMDsNCj4gPiArDQo+ID4gKwlpZiAoIW5wKSB7DQo+ID4gKwkJ
ZGV2X2VycigmcGRldi0+ZGV2LCAiRGV2aWNlIE9GLU5vZGUgaXMgTlVMTCIpOw0KPiA+ICsJCXJl
dHVybiAtRU5PREVWOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCWRhdGEgPSBkZXZtX2t6YWxsb2Mo
JnBkZXYtPmRldiwgc2l6ZW9mKHN0cnVjdA0KPiA+IHFvcmlxX3RtdV9kYXRhKSwNCj4gPiArCQkJ
wqDCoMKgwqBHRlBfS0VSTkVMKTsNCj4gPiArCWlmICghZGF0YSkNCj4gPiArCQlyZXR1cm4gLUVO
T01FTTsNCj4gPiArDQo+ID4gKwlwbGF0Zm9ybV9zZXRfZHJ2ZGF0YShwZGV2LCBkYXRhKTsNCj4g
PiArDQo+ID4gKwlkYXRhLT5saXR0bGVfZW5kaWFuID0gb2ZfcHJvcGVydHlfcmVhZF9ib29sKG5w
LCAibGl0dGxlLQ0KPiA+IGVuZGlhbiIpOw0KPiA+ICsNCj4gPiArCWRhdGEtPnNlbnNvcl9pZCA9
IHFvcmlxX3RtdV9nZXRfc2Vuc29yX2lkKCk7DQo+ID4gKwlpZiAoZGF0YS0+c2Vuc29yX2lkIDwg
MCkgew0KPiA+ICsJCWRldl9lcnIoJnBkZXYtPmRldiwgIkZhaWxlZCB0byBnZXQgc2Vuc29yIGlk
XG4iKTsNCj4gPiArCQlyZXQgPSAtRU5PREVWOw0KPiA+ICsJCWdvdG8gZXJyX2lvbWFwOw0KPiA+
ICsJfQ0KPiA+ICsNCj4gPiArCWRhdGEtPnJlZ3MgPSBvZl9pb21hcChucCwgMCk7DQo+ID4gKwlp
ZiAoIWRhdGEtPnJlZ3MpIHsNCj4gPiArCQlkZXZfZXJyKCZwZGV2LT5kZXYsICJGYWlsZWQgdG8g
Z2V0IG1lbW9yeQ0KPiA+IHJlZ2lvblxuIik7DQo+ID4gKwkJcmV0ID0gLUVOT0RFVjsNCj4gPiAr
CQlnb3RvIGVycl9pb21hcDsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlxb3JpcV90bXVfaW5pdF9k
ZXZpY2UoZGF0YSk7CS8qIFRNVSBpbml0aWFsaXphdGlvbiAqLw0KPiA+ICsNCj4gPiArCXJldCA9
IHFvcmlxX3RtdV9jYWxpYnJhdGlvbihwZGV2KTsJLyogVE1VIGNhbGlicmF0aW9uDQo+ID4gKi8N
Cj4gPiArCWlmIChyZXQgPCAwKQ0KPiA+ICsJCWdvdG8gZXJyX3RtdTsNCj4gPiArDQo+ID4gKwlk
YXRhLT50eiA9IHRoZXJtYWxfem9uZV9vZl9zZW5zb3JfcmVnaXN0ZXIoJnBkZXYtPmRldiwgZGF0
YS0NCj4gPiA+c2Vuc29yX2lkLA0KPiA+ICsJCQkJZGF0YSwgJnRtdV90el9vcHMpOw0KPiA+ICsJ
aWYgKElTX0VSUihkYXRhLT50eikpIHsNCj4gPiArCQlyZXQgPSBQVFJfRVJSKGRhdGEtPnR6KTsN
Cj4gPiArCQlkZXZfZXJyKCZwZGV2LT5kZXYsDQo+ID4gKwkJCSJGYWlsZWQgdG8gcmVnaXN0ZXIg
dGhlcm1hbCB6b25lIGRldmljZQ0KPiA+ICVkXG4iLCByZXQpOw0KPiA+ICsJCWdvdG8gZXJyX3Rt
dTsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwl0cmlwID0gb2ZfdGhlcm1hbF9nZXRfdHJpcF9wb2lu
dHMoZGF0YS0+dHopOw0KPiA+ICsNCj4gPiArCS8qIEVuYWJsZSBtb25pdG9yaW5nICovDQo+ID4g
KwlzaXRlIHw9IDB4MSA8PCAoMTUgLSBkYXRhLT5zZW5zb3JfaWQpOw0KPiA+ICsJdG11X3dyaXRl
KGRhdGEsIHNpdGUgfCBUTVJfTUUgfCBUTVJfQUxQRiwgJmRhdGEtPnJlZ3MtPnRtcik7DQo+ID4g
Kw0KPiA+ICsJcmV0dXJuIDA7DQo+ID4gKw0KPiA+ICtlcnJfdG11Og0KPiA+ICsJaW91bm1hcChk
YXRhLT5yZWdzKTsNCj4gPiArDQo+ID4gK2Vycl9pb21hcDoNCj4gPiArCXBsYXRmb3JtX3NldF9k
cnZkYXRhKHBkZXYsIE5VTEwpOw0KPiA+ICsNCj4gPiArCXJldHVybiByZXQ7DQo+ID4gK30NCj4g
PiArDQo+ID4gK3N0YXRpYyBpbnQgcW9yaXFfdG11X3JlbW92ZShzdHJ1Y3QgcGxhdGZvcm1fZGV2
aWNlICpwZGV2KQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3QgcW9yaXFfdG11X2RhdGEgKmRhdGEgPSBw
bGF0Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2KTsNCj4gPiArDQo+ID4gKwl0aGVybWFsX3pvbmVfb2Zf
c2Vuc29yX3VucmVnaXN0ZXIoJnBkZXYtPmRldiwgZGF0YS0+dHopOw0KPiA+ICsNCj4gPiArCS8q
IERpc2FibGUgbW9uaXRvcmluZyAqLw0KPiA+ICsJdG11X3dyaXRlKGRhdGEsIFRNUl9ESVNBQkxF
LCAmZGF0YS0+cmVncy0+dG1yKTsNCj4gPiArDQo+ID4gKwlpb3VubWFwKGRhdGEtPnJlZ3MpOw0K
PiA+ICsJcGxhdGZvcm1fc2V0X2RydmRhdGEocGRldiwgTlVMTCk7DQo+ID4gKw0KPiA+ICsJcmV0
dXJuIDA7DQo+ID4gK30NCj4gPiArDQo+ID4gKyNpZmRlZiBDT05GSUdfUE1fU0xFRVANCj4gPiAr
c3RhdGljIGludCBxb3JpcV90bXVfc3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpkZXYpDQo+ID4gK3sN
Cj4gPiArCXUzMiB0bXI7DQo+ID4gKwlzdHJ1Y3QgcW9yaXFfdG11X2RhdGEgKmRhdGEgPSBkZXZf
Z2V0X2RydmRhdGEoZGV2KTsNCj4gPiArDQo+ID4gKwkvKiBEaXNhYmxlIG1vbml0b3JpbmcgKi8N
Cj4gPiArCXRtciA9IHRtdV9yZWFkKGRhdGEsICZkYXRhLT5yZWdzLT50bXIpOw0KPiA+ICsJdG1y
ICY9IH5UTVJfTUU7DQo+ID4gKwl0bXVfd3JpdGUoZGF0YSwgdG1yLCAmZGF0YS0+cmVncy0+dG1y
KTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gMDsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIGlu
dCBxb3JpcV90bXVfcmVzdW1lKHN0cnVjdCBkZXZpY2UgKmRldikNCj4gPiArew0KPiA+ICsJdTMy
IHRtcjsNCj4gPiArCXN0cnVjdCBxb3JpcV90bXVfZGF0YSAqZGF0YSA9IGRldl9nZXRfZHJ2ZGF0
YShkZXYpOw0KPiA+ICsNCj4gPiArCS8qIEVuYWJsZSBtb25pdG9yaW5nICovDQo+ID4gKwl0bXIg
PSB0bXVfcmVhZChkYXRhLCAmZGF0YS0+cmVncy0+dG1yKTsNCj4gPiArCXRtciB8PSBUTVJfTUU7
DQo+ID4gKwl0bXVfd3JpdGUoZGF0YSwgdG1yLCAmZGF0YS0+cmVncy0+dG1yKTsNCj4gPiArDQo+
ID4gKwlyZXR1cm4gMDsNCj4gPiArfQ0KPiA+ICsjZW5kaWYNCj4gPiArDQo+ID4gK3N0YXRpYyBT
SU1QTEVfREVWX1BNX09QUyhxb3JpcV90bXVfcG1fb3BzLA0KPiA+ICsJCQnCoHFvcmlxX3RtdV9z
dXNwZW5kLCBxb3JpcV90bXVfcmVzdW1lKTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1
Y3Qgb2ZfZGV2aWNlX2lkIHFvcmlxX3RtdV9tYXRjaFtdID0gew0KPiA+ICsJeyAuY29tcGF0aWJs
ZSA9ICJmc2wscW9yaXEtdG11IiwgfSwNCj4gPiArCXt9LA0KPiA+ICt9Ow0KPiA+ICtNT0RVTEVf
REVWSUNFX1RBQkxFKG9mLCBxb3JpcV90bXVfbWF0Y2gpOw0KPiA+ICsNCj4gPiArc3RhdGljIHN0
cnVjdCBwbGF0Zm9ybV9kcml2ZXIgcW9yaXFfdG11ID0gew0KPiA+ICsJLmRyaXZlcgk9IHsNCj4g
PiArCQkubmFtZQkJPSAicW9yaXFfdGhlcm1hbCIsDQo+ID4gKwkJLnBtCQk9ICZxb3JpcV90bXVf
cG1fb3BzLA0KPiA+ICsJCS5vZl9tYXRjaF90YWJsZQk9IHFvcmlxX3RtdV9tYXRjaCwNCj4gPiAr
CX0sDQo+ID4gKwkucHJvYmUJPSBxb3JpcV90bXVfcHJvYmUsDQo+ID4gKwkucmVtb3ZlCT0gcW9y
aXFfdG11X3JlbW92ZSwNCj4gPiArfTsNCj4gPiArbW9kdWxlX3BsYXRmb3JtX2RyaXZlcihxb3Jp
cV90bXUpOw0KPiA+ICsNCj4gPiArTU9EVUxFX0FVVEhPUigiSmlhIEhvbmd0YW8gPGhvbmd0YW8u
amlhQG54cC5jb20+Iik7DQo+ID4gK01PRFVMRV9ERVNDUklQVElPTigiUW9ySVEgVGhlcm1hbCBN
b25pdG9yaW5nIFVuaXQgZHJpdmVyIik7DQo+ID4gK01PRFVMRV9MSUNFTlNFKCJHUEwgdjIiKTsN
Cj4gPiAtLQ0KPiA+IDIuMS4wLjI3Lmc5NmRiMzI0DQo+ID4NCg==
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH V2 7/7] thermal: qoriq: Add thermal management support
@ 2016-09-08 7:11 ` Troy Jia
0 siblings, 0 replies; 18+ messages in thread
From: Troy Jia @ 2016-09-08 7:11 UTC (permalink / raw)
To: linux-arm-kernel
> -----Original Message-----
> From: Zhang Rui [mailto:rui.zhang at intel.com]
> Sent: Friday, August 19, 2016 8:40 PM
> To: Hongtao Jia <hongtao.jia@nxp.com>; edubezval at gmail.com;
> robh+dt at kernel.org; galak at codeaurora.org; Scott Wood
> <scott.wood@nxp.com>; shawnguo at kernel.org
> Cc: linux-pm at vger.kernel.org; devicetree at vger.kernel.org; linux-
> kernel at vger.kernel.org; linuxppc-dev at lists.ozlabs.org; linux-arm-
> kernel at lists.infradead.org
> Subject: Re: [PATCH V2 7/7] thermal: qoriq: Add thermal management support
>
> On ?, 2016-06-30 at 11:08 +0800, Jia Hongtao wrote:
> > This driver add thermal management support by enabling TMU (Thermal
> > Monitoring Unit) on QorIQ platform.
> >
> > It's based on thermal of framework:
> > - Trip points defined in device tree.
> > - Cpufreq as cooling device registered in qoriq cpufreq driver.
> >
> > Signed-off-by: Jia Hongtao <hongtao.jia@nxp.com>
>
> The patch looks good to me.
> I only need to take this patch, right?
>
> thanks,
> rui
Yes. You just need to take this patch.
Sorry for the late response. I was on my long vacation back then.
Thanks,
Hongtao.
> > ---
> > Changes of V2:
> > * Add HAS_IOMEM dependency to fix build error on UM
> >
> > ?drivers/thermal/Kconfig?????????|??10 ++
> > ?drivers/thermal/Makefile????????|???1 +
> > ?drivers/thermal/qoriq_thermal.c | 328
> > ++++++++++++++++++++++++++++++++++++++++
> > ?3 files changed, 339 insertions(+)
> > ?create mode 100644 drivers/thermal/qoriq_thermal.c
> >
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > index 2d702ca..56ef30d 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -195,6 +195,16 @@ config IMX_THERMAL
> > ? ??cpufreq is used as the cooling device to throttle CPUs
> > when the
> > ? ??passive trip is crossed.
> >
> > +config QORIQ_THERMAL
> > + tristate "QorIQ Thermal Monitoring Unit"
> > + depends on THERMAL_OF
> > + depends on HAS_IOMEM
> > + help
> > + ??Support for Thermal Monitoring Unit (TMU) found on QorIQ
> > platforms.
> > + ??It supports one critical trip point and one passive trip
> > point. The
> > + ??cpufreq is used as the cooling device to throttle CPUs
> > when the
> > + ??passive trip is crossed.
> > +
> > ?config SPEAR_THERMAL
> > ? tristate "SPEAr thermal sensor driver"
> > ? depends on PLAT_SPEAR || COMPILE_TEST
> > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > index 10b07c1..6662232 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -37,6 +37,7 @@ obj-$(CONFIG_DB8500_THERMAL) +=
> > db8500_thermal.o
> > ?obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
> > ?obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
> > ?obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
> > +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
> > ?obj-$(CONFIG_DB8500_CPUFREQ_COOLING) +=
> > db8500_cpufreq_cooling.o
> > ?obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
> > ?obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
> > diff --git a/drivers/thermal/qoriq_thermal.c
> > b/drivers/thermal/qoriq_thermal.c
> > new file mode 100644
> > index 0000000..644ba52
> > --- /dev/null
> > +++ b/drivers/thermal/qoriq_thermal.c
> > @@ -0,0 +1,328 @@
> > +/*
> > + * Copyright 2016 Freescale Semiconductor, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but
> > WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.??See the GNU General Public
> > License for
> > + * more details.
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/err.h>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/thermal.h>
> > +
> > +#include "thermal_core.h"
> > +
> > +#define SITES_MAX 16
> > +
> > +/*
> > + * QorIQ TMU Registers
> > + */
> > +struct qoriq_tmu_site_regs {
> > + u32 tritsr; /* Immediate Temperature Site
> > Register */
> > + u32 tratsr; /* Average Temperature Site
> > Register */
> > + u8 res0[0x8];
> > +};
> > +
> > +struct qoriq_tmu_regs {
> > + u32 tmr; /* Mode Register */
> > +#define TMR_DISABLE 0x0
> > +#define TMR_ME 0x80000000
> > +#define TMR_ALPF 0x0c000000
> > + u32 tsr; /* Status Register */
> > + u32 tmtmir; /* Temperature measurement
> > interval Register */
> > +#define TMTMIR_DEFAULT 0x0000000f
> > + u8 res0[0x14];
> > + u32 tier; /* Interrupt Enable Register */
> > +#define TIER_DISABLE 0x0
> > + u32 tidr; /* Interrupt Detect Register */
> > + u32 tiscr; /* Interrupt Site Capture Register
> > */
> > + u32 ticscr; /* Interrupt Critical Site
> > Capture Register */
> > + u8 res1[0x10];
> > + u32 tmhtcrh; /* High Temperature Capture
> > Register */
> > + u32 tmhtcrl; /* Low Temperature Capture
> > Register */
> > + u8 res2[0x8];
> > + u32 tmhtitr; /* High Temperature Immediate
> > Threshold */
> > + u32 tmhtatr; /* High Temperature Average
> > Threshold */
> > + u32 tmhtactr; /* High Temperature Average Crit
> > Threshold */
> > + u8 res3[0x24];
> > + u32 ttcfgr; /* Temperature Configuration
> > Register */
> > + u32 tscfgr; /* Sensor Configuration Register
> > */
> > + u8 res4[0x78];
> > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > + u8 res5[0x9f8];
> > + u32 ipbrr0; /* IP Block Revision Register 0
> > */
> > + u32 ipbrr1; /* IP Block Revision Register 1
> > */
> > + u8 res6[0x310];
> > + u32 ttr0cr; /* Temperature Range 0 Control
> > Register */
> > + u32 ttr1cr; /* Temperature Range 1 Control
> > Register */
> > + u32 ttr2cr; /* Temperature Range 2 Control
> > Register */
> > + u32 ttr3cr; /* Temperature Range 3 Control
> > Register */
> > +};
> > +
> > +/*
> > + * Thermal zone data
> > + */
> > +struct qoriq_tmu_data {
> > + struct thermal_zone_device *tz;
> > + struct qoriq_tmu_regs __iomem *regs;
> > + int sensor_id;
> > + bool little_endian;
> > +};
> > +
> > +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void
> > __iomem *addr)
> > +{
> > + if (p->little_endian)
> > + iowrite32(val, addr);
> > + else
> > + iowrite32be(val, addr);
> > +}
> > +
> > +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
> > +{
> > + if (p->little_endian)
> > + return ioread32(addr);
> > + else
> > + return ioread32be(addr);
> > +}
> > +
> > +static int tmu_get_temp(void *p, int *temp)
> > +{
> > + u32 val;
> > + struct qoriq_tmu_data *data = p;
> > +
> > + val = tmu_read(data, &data->regs->site[data-
> > >sensor_id].tritsr);
> > + *temp = (val & 0xff) * 1000;
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_get_sensor_id(void)
> > +{
> > + int ret, id;
> > + struct of_phandle_args sensor_specs;
> > + struct device_node *np, *sensor_np;
> > +
> > + np = of_find_node_by_name(NULL, "thermal-zones");
> > + if (!np)
> > + return -ENODEV;
> > +
> > + sensor_np = of_get_next_child(np, NULL);
> > + ret = of_parse_phandle_with_args(sensor_np, "thermal-
> > sensors",
> > + "#thermal-sensor-cells",
> > + 0, &sensor_specs);
> > + if (ret) {
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > + return ret;
> > + }
> > +
> > + if (sensor_specs.args_count >= 1) {
> > + id = sensor_specs.args[0];
> > + WARN(sensor_specs.args_count > 1,
> > + "%s: too many cells in sensor
> > specifier %d\n",
> > + sensor_specs.np->name,
> > sensor_specs.args_count);
> > + } else {
> > + id = 0;
> > + }
> > +
> > + of_node_put(np);
> > + of_node_put(sensor_np);
> > +
> > + return id;
> > +}
> > +
> > +static int qoriq_tmu_calibration(struct platform_device *pdev)
> > +{
> > + int i, val, len;
> > + u32 range[4];
> > + const u32 *calibration;
> > + struct device_node *np = pdev->dev.of_node;
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + if (of_property_read_u32_array(np, "fsl,tmu-range", range,
> > 4)) {
> > + dev_err(&pdev->dev, "missing calibration range.\n");
> > + return -ENODEV;
> > + }
> > +
> > + /* Init temperature range registers */
> > + tmu_write(data, range[0], &data->regs->ttr0cr);
> > + tmu_write(data, range[1], &data->regs->ttr1cr);
> > + tmu_write(data, range[2], &data->regs->ttr2cr);
> > + tmu_write(data, range[3], &data->regs->ttr3cr);
> > +
> > + calibration = of_get_property(np, "fsl,tmu-calibration",
> > &len);
> > + if (calibration == NULL || len % 8) {
> > + dev_err(&pdev->dev, "invalid calibration data.\n");
> > + return -ENODEV;
> > + }
> > +
> > + for (i = 0; i < len; i += 8, calibration += 2) {
> > + val = of_read_number(calibration, 1);
> > + tmu_write(data, val, &data->regs->ttcfgr);
> > + val = of_read_number(calibration + 1, 1);
> > + tmu_write(data, val, &data->regs->tscfgr);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
> > +{
> > + /* Disable interrupt, using polling instead */
> > + tmu_write(data, TIER_DISABLE, &data->regs->tier);
> > +
> > + /* Set update_interval */
> > + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +}
> > +
> > +static struct thermal_zone_of_device_ops tmu_tz_ops = {
> > + .get_temp = tmu_get_temp,
> > +};
> > +
> > +static int qoriq_tmu_probe(struct platform_device *pdev)
> > +{
> > + int ret;
> > + const struct thermal_trip *trip;
> > + struct qoriq_tmu_data *data;
> > + struct device_node *np = pdev->dev.of_node;
> > + u32 site = 0;
> > +
> > + if (!np) {
> > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > + return -ENODEV;
> > + }
> > +
> > + data = devm_kzalloc(&pdev->dev, sizeof(struct
> > qoriq_tmu_data),
> > + ????GFP_KERNEL);
> > + if (!data)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, data);
> > +
> > + data->little_endian = of_property_read_bool(np, "little-
> > endian");
> > +
> > + data->sensor_id = qoriq_tmu_get_sensor_id();
> > + if (data->sensor_id < 0) {
> > + dev_err(&pdev->dev, "Failed to get sensor id\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + data->regs = of_iomap(np, 0);
> > + if (!data->regs) {
> > + dev_err(&pdev->dev, "Failed to get memory
> > region\n");
> > + ret = -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + qoriq_tmu_init_device(data); /* TMU initialization */
> > +
> > + ret = qoriq_tmu_calibration(pdev); /* TMU calibration
> > */
> > + if (ret < 0)
> > + goto err_tmu;
> > +
> > + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data-
> > >sensor_id,
> > + data, &tmu_tz_ops);
> > + if (IS_ERR(data->tz)) {
> > + ret = PTR_ERR(data->tz);
> > + dev_err(&pdev->dev,
> > + "Failed to register thermal zone device
> > %d\n", ret);
> > + goto err_tmu;
> > + }
> > +
> > + trip = of_thermal_get_trip_points(data->tz);
> > +
> > + /* Enable monitoring */
> > + site |= 0x1 << (15 - data->sensor_id);
> > + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
> > +
> > + return 0;
> > +
> > +err_tmu:
> > + iounmap(data->regs);
> > +
> > +err_iomap:
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return ret;
> > +}
> > +
> > +static int qoriq_tmu_remove(struct platform_device *pdev)
> > +{
> > + struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
> > +
> > + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
> > +
> > + /* Disable monitoring */
> > + tmu_write(data, TMR_DISABLE, &data->regs->tmr);
> > +
> > + iounmap(data->regs);
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int qoriq_tmu_suspend(struct device *dev)
> > +{
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Disable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr &= ~TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_resume(struct device *dev)
> > +{
> > + u32 tmr;
> > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > +
> > + /* Enable monitoring */
> > + tmr = tmu_read(data, &data->regs->tmr);
> > + tmr |= TMR_ME;
> > + tmu_write(data, tmr, &data->regs->tmr);
> > +
> > + return 0;
> > +}
> > +#endif
> > +
> > +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
> > + ?qoriq_tmu_suspend, qoriq_tmu_resume);
> > +
> > +static const struct of_device_id qoriq_tmu_match[] = {
> > + { .compatible = "fsl,qoriq-tmu", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
> > +
> > +static struct platform_driver qoriq_tmu = {
> > + .driver = {
> > + .name = "qoriq_thermal",
> > + .pm = &qoriq_tmu_pm_ops,
> > + .of_match_table = qoriq_tmu_match,
> > + },
> > + .probe = qoriq_tmu_probe,
> > + .remove = qoriq_tmu_remove,
> > +};
> > +module_platform_driver(qoriq_tmu);
> > +
> > +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
> > +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.1.0.27.g96db324
> >
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2016-09-08 7:27 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-30 3:08 [PATCH V2 7/7] thermal: qoriq: Add thermal management support Jia Hongtao
2016-06-30 3:08 ` Jia Hongtao
2016-06-30 3:08 ` Jia Hongtao
2016-07-19 6:54 ` Hongtao Jia
2016-07-19 6:54 ` Hongtao Jia
2016-07-19 6:54 ` Hongtao Jia
2016-07-19 6:54 ` Hongtao Jia
2016-08-05 6:26 ` Hongtao Jia
2016-08-05 6:26 ` Hongtao Jia
2016-08-05 6:26 ` Hongtao Jia
2016-08-05 6:26 ` Hongtao Jia
2016-08-19 12:39 ` Zhang Rui
2016-08-19 12:39 ` Zhang Rui
2016-08-19 12:39 ` Zhang Rui
2016-09-08 7:11 ` Troy Jia
2016-09-08 7:11 ` Troy Jia
2016-09-08 7:11 ` Troy Jia
2016-09-08 7:11 ` Troy Jia
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.