All of lore.kernel.org
 help / color / mirror / Atom feed
* [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.