* [PATCH V2] QorIQ/TMU: add thermal management support based on TMU
@ 2015-07-29 6:19 Jia Hongtao
2015-07-30 6:55 ` Eduardo Valentin
0 siblings, 1 reply; 8+ messages in thread
From: Jia Hongtao @ 2015-07-29 6:19 UTC (permalink / raw)
To: edubezval; +Cc: linux-pm, linuxppc-dev, scottwood, hongtao.jia
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.
Signed-off-by: Jia Hongtao <hongtao.jia@freescale.com>
---
This patch based on:
http://patchwork.ozlabs.org/patch/482987/
Changes for V2:
* Add tmu-range parse.
* Use default trend hook.
* Using latest thermal_zone_bind_cooling_device API.
* Add calibration check during initialization.
* Disable/enalbe device when suspend/resume.
drivers/thermal/Kconfig | 11 ++
drivers/thermal/Makefile | 1 +
drivers/thermal/qoriq_thermal.c | 406 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 418 insertions(+)
create mode 100644 drivers/thermal/qoriq_thermal.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 118938e..a200745 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -180,6 +180,17 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed.
+config QORIQ_THERMAL
+ tristate "Freescale QorIQ Thermal Monitoring Unit"
+ depends on CPU_THERMAL
+ depends on OF
+ default n
+ help
+ Enable thermal management based on Freescale QorIQ Thermal Monitoring
+ Unit (TMU). 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
bool "SPEAr thermal sensor driver"
depends on PLAT_SPEAR
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 535dfee..8c25859 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_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..0694f42
--- /dev/null
+++ b/drivers/thermal/qoriq_thermal.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2015 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.
+ *
+ */
+
+/*
+ * Based on Freescale QorIQ Thermal Monitoring Unit (TMU)
+ */
+#include <linux/cpufreq.h>
+#include <linux/cpu_cooling.h>
+#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>
+
+#define SITES_MAX 16
+
+#define TMU_TEMP_PASSIVE 85000
+#define TMU_TEMP_CRITICAL 95000
+
+#define TMU_PASSIVE_DELAY 1000 /* Milliseconds */
+#define TMU_POLLING_DELAY 5000
+
+/* The driver supports 1 passive trip point and 1 critical trip point */
+enum tmu_thermal_trip {
+ TMU_TRIP_PASSIVE,
+ TMU_TRIP_CRITICAL,
+ TMU_TRIP_NUM,
+};
+
+/*
+ * QorIQ TMU Registers
+ */
+struct qoriq_tmu_site_regs {
+ __be32 tritsr; /* Immediate Temperature Site Register */
+ __be32 tratsr; /* Average Temperature Site Register */
+ u8 res0[0x8];
+} __packed;
+
+struct qoriq_tmu_regs {
+ __be32 tmr; /* Mode Register */
+#define TMR_DISABLE 0x0
+#define TMR_ME 0x80000000
+#define TMR_ALPF 0x0c000000
+#define TMR_MSITE 0x00008000
+#define TMR_ALL (TMR_ME | TMR_ALPF | TMR_MSITE)
+ __be32 tsr; /* Status Register */
+ __be32 tmtmir; /* Temperature measurement interval Register */
+#define TMTMIR_DEFAULT 0x00000007
+ u8 res0[0x14];
+ __be32 tier; /* Interrupt Enable Register */
+#define TIER_DISABLE 0x0
+ __be32 tidr; /* Interrupt Detect Register */
+ __be32 tiscr; /* Interrupt Site Capture Register */
+ __be32 ticscr; /* Interrupt Critical Site Capture Register */
+ u8 res1[0x10];
+ __be32 tmhtcrh; /* High Temperature Capture Register */
+ __be32 tmhtcrl; /* Low Temperature Capture Register */
+ u8 res2[0x8];
+ __be32 tmhtitr; /* High Temperature Immediate Threshold */
+ __be32 tmhtatr; /* High Temperature Average Threshold */
+ __be32 tmhtactr; /* High Temperature Average Crit Threshold */
+ u8 res3[0x24];
+ __be32 ttcfgr; /* Temperature Configuration Register */
+ __be32 tscfgr; /* Sensor Configuration Register */
+ u8 res4[0x78];
+ struct qoriq_tmu_site_regs site[SITES_MAX];
+ u8 res5[0x9f8];
+ __be32 ipbrr0; /* IP Block Revision Register 0 */
+ __be32 ipbrr1; /* IP Block Revision Register 1 */
+ u8 res6[0x310];
+ __be32 ttr0cr; /* Temperature Range 0 Control Register */
+ __be32 ttr1cr; /* Temperature Range 1 Control Register */
+ __be32 ttr2cr; /* Temperature Range 2 Control Register */
+ __be32 ttr3cr; /* Temperature Range 3 Control Register */
+};
+
+/*
+ * Thermal zone data
+ */
+struct qoriq_tmu_data {
+ struct thermal_zone_device *tz;
+ struct thermal_cooling_device *cdev;
+ enum thermal_device_mode mode;
+ unsigned long temp_passive;
+ unsigned long temp_critical;
+ struct qoriq_tmu_regs __iomem *regs;
+};
+
+static int tmu_get_mode(struct thermal_zone_device *tz,
+ enum thermal_device_mode *mode)
+{
+ struct qoriq_tmu_data *data = tz->devdata;
+
+ *mode = data->mode;
+
+ return 0;
+}
+
+static int tmu_set_mode(struct thermal_zone_device *tz,
+ enum thermal_device_mode mode)
+{
+ struct qoriq_tmu_data *data = tz->devdata;
+
+ if (mode == THERMAL_DEVICE_ENABLED) {
+ tz->polling_delay = TMU_POLLING_DELAY;
+ tz->passive_delay = TMU_PASSIVE_DELAY;
+ thermal_zone_device_update(tz);
+ } else {
+ tz->polling_delay = 0;
+ tz->passive_delay = 0;
+ }
+
+ data->mode = mode;
+
+ return 0;
+}
+
+static int tmu_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
+{
+ u8 val;
+ struct qoriq_tmu_data *data = tz->devdata;
+
+ val = ioread32be(&data->regs->site[0].tritsr);
+ *temp = (unsigned long)val * 1000;
+
+ return 0;
+}
+
+static int tmu_get_trip_type(struct thermal_zone_device *tz, int trip,
+ enum thermal_trip_type *type)
+{
+ *type = (trip == TMU_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
+ THERMAL_TRIP_CRITICAL;
+
+ return 0;
+}
+
+static int tmu_get_trip_temp(struct thermal_zone_device *tz, int trip,
+ unsigned long *temp)
+{
+ struct qoriq_tmu_data *data = tz->devdata;
+
+ *temp = (trip == TMU_TRIP_PASSIVE) ? data->temp_passive :
+ data->temp_critical;
+
+ return 0;
+}
+
+static int tmu_get_crit_temp(struct thermal_zone_device *tz,
+ unsigned long *temp)
+{
+ struct qoriq_tmu_data *data = tz->devdata;
+
+ *temp = data->temp_critical;
+
+ return 0;
+}
+
+static int tmu_bind(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev)
+{
+ int ret;
+
+ ret = thermal_zone_bind_cooling_device(tz, TMU_TRIP_PASSIVE, cdev,
+ THERMAL_NO_LIMIT,
+ THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT);
+ if (ret) {
+ dev_err(&tz->device,
+ "Binding zone %s with cdev %s failed:%d\n",
+ tz->type, cdev->type, ret);
+ }
+
+ return ret;
+}
+
+static int tmu_unbind(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev)
+{
+ int ret;
+
+ ret = thermal_zone_unbind_cooling_device(tz, TMU_TRIP_PASSIVE, cdev);
+ if (ret) {
+ dev_err(&tz->device,
+ "Unbinding zone %s with cdev %s failed:%d\n",
+ tz->type, cdev->type, ret);
+ }
+
+ return ret;
+}
+
+static int qoriq_tmu_calibration(struct platform_device *pdev)
+{
+ int i, val, len;
+ u32 range[4];
+ const __be32 *calibration;
+ struct device_node *node = pdev->dev.of_node;
+ struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev);
+
+ /* Disable monitoring before calibration */
+ iowrite32be(TMR_DISABLE, &data->regs->tmr);
+
+ if (of_property_read_u32_array(node, "fsl,tmu-range", range, 4))
+ return -1;
+
+ /* Init temperature range registers */
+ iowrite32be(range[0], &data->regs->ttr0cr);
+ iowrite32be(range[1], &data->regs->ttr1cr);
+ iowrite32be(range[2], &data->regs->ttr2cr);
+ iowrite32be(range[3], &data->regs->ttr3cr);
+
+ calibration = of_get_property(node, "fsl,tmu-calibration", &len);
+ if (calibration == NULL)
+ return -1;
+
+ for (i = 0; i < len; i += 8, calibration += 2) {
+ val = (int)of_read_number(calibration, 1);
+ iowrite32be(val, &data->regs->ttcfgr);
+ val = (int)of_read_number(calibration + 1, 1);
+ iowrite32be(val, &data->regs->tscfgr);
+ }
+
+ return 0;
+}
+
+static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
+{
+ /* Disable interrupt, using polling instead */
+ iowrite32be(TIER_DISABLE, &data->regs->tier);
+
+ /* Set update_interval */
+ iowrite32be(TMTMIR_DEFAULT, &data->regs->tmtmir);
+
+ /* Enable monitoring */
+ iowrite32be(TMR_ALL, &data->regs->tmr);
+}
+
+static struct thermal_zone_device_ops tmu_tz_ops = {
+ .bind = tmu_bind,
+ .unbind = tmu_unbind,
+ .get_temp = tmu_get_temp,
+ .get_mode = tmu_get_mode,
+ .set_mode = tmu_set_mode,
+ .get_trip_type = tmu_get_trip_type,
+ .get_trip_temp = tmu_get_trip_temp,
+ .get_crit_temp = tmu_get_crit_temp,
+};
+
+static int qoriq_tmu_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct cpumask clip_cpus;
+ struct qoriq_tmu_data *data;
+
+ if (!cpufreq_get_current_driver()) {
+ dev_dbg(&pdev->dev, "No cpufreq driver yet\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "Device OF-Node is NULL");
+ return -EFAULT;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, data);
+ data->regs = of_iomap(pdev->dev.of_node, 0);
+
+ if (!data->regs) {
+ dev_err(&pdev->dev, "Failed to get memory region\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
+ if (ret < 0) {
+ dev_err(&pdev->dev, "TMU calibration failed.\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ qoriq_tmu_init_device(data); /* TMU initialization */
+
+ cpumask_setall(&clip_cpus);
+ data->cdev = cpufreq_cooling_register(&clip_cpus);
+ if (IS_ERR(data->cdev)) {
+ ret = PTR_ERR(data->cdev);
+ dev_err(&data->cdev->device,
+ "Failed to register cpufreq cooling device: %d\n", ret);
+ goto err_cooling;
+ }
+
+ data->temp_passive = TMU_TEMP_PASSIVE;
+ data->temp_critical = TMU_TEMP_CRITICAL;
+ data->tz = thermal_zone_device_register("tmu_thermal_zone",
+ TMU_TRIP_NUM,
+ 0, data,
+ &tmu_tz_ops, NULL,
+ TMU_PASSIVE_DELAY,
+ TMU_POLLING_DELAY);
+
+ 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_thermal;
+ }
+
+ data->mode = THERMAL_DEVICE_ENABLED;
+
+ return 0;
+
+err_thermal:
+ cpufreq_cooling_unregister(data->cdev);
+
+err_cooling:
+ iounmap(data->regs);
+
+err_iomap:
+ dev_set_drvdata(&pdev->dev, NULL);
+ devm_kfree(&pdev->dev, data);
+
+ return ret;
+}
+
+static int qoriq_tmu_remove(struct platform_device *pdev)
+{
+ struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev);
+
+ /* Disable monitoring */
+ iowrite32be(TMR_DISABLE, &data->regs->tmr);
+
+ thermal_zone_device_unregister(data->tz);
+ cpufreq_cooling_unregister(data->cdev);
+ iounmap(data->regs);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ devm_kfree(&pdev->dev, data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qoriq_tmu_suspend(struct device *dev)
+{
+ struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+ /* Disable monitoring */
+ iowrite32be(TMR_DISABLE, &data->regs->tmr);
+ data->mode = THERMAL_DEVICE_DISABLED;
+
+ return 0;
+}
+
+static int qoriq_tmu_resume(struct device *dev)
+{
+ struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+ /* Enable monitoring */
+ iowrite32be(TMR_ALL, &data->regs->tmr);
+ data->mode = THERMAL_DEVICE_ENABLED;
+
+ 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", },
+ {},
+};
+
+static struct platform_driver qoriq_tmu = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .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@freescale.com>");
+MODULE_DESCRIPTION("Freescale QorIQ Thermal Monitoring Unit driver");
+MODULE_LICENSE("GPL v2");
--
2.1.0.27.g96db324
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH V2] QorIQ/TMU: add thermal management support based on TMU
2015-07-29 6:19 [PATCH V2] QorIQ/TMU: add thermal management support based on TMU Jia Hongtao
@ 2015-07-30 6:55 ` Eduardo Valentin
2015-07-30 8:13 ` Hongtao Jia
0 siblings, 1 reply; 8+ messages in thread
From: Eduardo Valentin @ 2015-07-30 6:55 UTC (permalink / raw)
To: Jia Hongtao; +Cc: linux-pm, linuxppc-dev, scottwood
[-- Attachment #1: Type: text/plain, Size: 14364 bytes --]
On Wed, Jul 29, 2015 at 02:19:39PM +0800, Jia Hongtao wrote:
> 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.
>
> Signed-off-by: Jia Hongtao <hongtao.jia@freescale.com>
> ---
> This patch based on:
> http://patchwork.ozlabs.org/patch/482987/
>
> Changes for V2:
> * Add tmu-range parse.
> * Use default trend hook.
> * Using latest thermal_zone_bind_cooling_device API.
> * Add calibration check during initialization.
> * Disable/enalbe device when suspend/resume.
>
> drivers/thermal/Kconfig | 11 ++
> drivers/thermal/Makefile | 1 +
> drivers/thermal/qoriq_thermal.c | 406 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 418 insertions(+)
> create mode 100644 drivers/thermal/qoriq_thermal.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 118938e..a200745 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -180,6 +180,17 @@ config IMX_THERMAL
> cpufreq is used as the cooling device to throttle CPUs when the
> passive trip is crossed.
>
> +config QORIQ_THERMAL
> + tristate "Freescale QorIQ Thermal Monitoring Unit"
> + depends on CPU_THERMAL
> + depends on OF
> + default n
> + help
> + Enable thermal management based on Freescale QorIQ Thermal Monitoring
> + Unit (TMU). 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
> bool "SPEAr thermal sensor driver"
> depends on PLAT_SPEAR
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 535dfee..8c25859 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -33,6 +33,7 @@ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
> obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
> obj-$(CONFIG_ARMADA_THERMAL) += armada_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..0694f42
> --- /dev/null
> +++ b/drivers/thermal/qoriq_thermal.c
> @@ -0,0 +1,406 @@
> +/*
> + * Copyright 2015 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.
> + *
> + */
> +
> +/*
> + * Based on Freescale QorIQ Thermal Monitoring Unit (TMU)
> + */
> +#include <linux/cpufreq.h>
> +#include <linux/cpu_cooling.h>
> +#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>
> +
> +#define SITES_MAX 16
> +
> +#define TMU_TEMP_PASSIVE 85000
> +#define TMU_TEMP_CRITICAL 95000
> +
> +#define TMU_PASSIVE_DELAY 1000 /* Milliseconds */
> +#define TMU_POLLING_DELAY 5000
> +
> +/* The driver supports 1 passive trip point and 1 critical trip point */
> +enum tmu_thermal_trip {
> + TMU_TRIP_PASSIVE,
> + TMU_TRIP_CRITICAL,
> + TMU_TRIP_NUM,
> +};
> +
> +/*
> + * QorIQ TMU Registers
> + */
> +struct qoriq_tmu_site_regs {
> + __be32 tritsr; /* Immediate Temperature Site Register */
> + __be32 tratsr; /* Average Temperature Site Register */
> + u8 res0[0x8];
> +} __packed;
> +
> +struct qoriq_tmu_regs {
> + __be32 tmr; /* Mode Register */
> +#define TMR_DISABLE 0x0
> +#define TMR_ME 0x80000000
> +#define TMR_ALPF 0x0c000000
> +#define TMR_MSITE 0x00008000
> +#define TMR_ALL (TMR_ME | TMR_ALPF | TMR_MSITE)
> + __be32 tsr; /* Status Register */
> + __be32 tmtmir; /* Temperature measurement interval Register */
> +#define TMTMIR_DEFAULT 0x00000007
> + u8 res0[0x14];
> + __be32 tier; /* Interrupt Enable Register */
> +#define TIER_DISABLE 0x0
> + __be32 tidr; /* Interrupt Detect Register */
> + __be32 tiscr; /* Interrupt Site Capture Register */
> + __be32 ticscr; /* Interrupt Critical Site Capture Register */
> + u8 res1[0x10];
> + __be32 tmhtcrh; /* High Temperature Capture Register */
> + __be32 tmhtcrl; /* Low Temperature Capture Register */
> + u8 res2[0x8];
> + __be32 tmhtitr; /* High Temperature Immediate Threshold */
> + __be32 tmhtatr; /* High Temperature Average Threshold */
> + __be32 tmhtactr; /* High Temperature Average Crit Threshold */
> + u8 res3[0x24];
> + __be32 ttcfgr; /* Temperature Configuration Register */
> + __be32 tscfgr; /* Sensor Configuration Register */
> + u8 res4[0x78];
> + struct qoriq_tmu_site_regs site[SITES_MAX];
> + u8 res5[0x9f8];
> + __be32 ipbrr0; /* IP Block Revision Register 0 */
> + __be32 ipbrr1; /* IP Block Revision Register 1 */
> + u8 res6[0x310];
> + __be32 ttr0cr; /* Temperature Range 0 Control Register */
> + __be32 ttr1cr; /* Temperature Range 1 Control Register */
> + __be32 ttr2cr; /* Temperature Range 2 Control Register */
> + __be32 ttr3cr; /* Temperature Range 3 Control Register */
> +};
> +
> +/*
> + * Thermal zone data
> + */
> +struct qoriq_tmu_data {
> + struct thermal_zone_device *tz;
> + struct thermal_cooling_device *cdev;
> + enum thermal_device_mode mode;
> + unsigned long temp_passive;
> + unsigned long temp_critical;
> + struct qoriq_tmu_regs __iomem *regs;
> +};
> +
> +static int tmu_get_mode(struct thermal_zone_device *tz,
> + enum thermal_device_mode *mode)
> +{
> + struct qoriq_tmu_data *data = tz->devdata;
> +
> + *mode = data->mode;
> +
> + return 0;
> +}
> +
> +static int tmu_set_mode(struct thermal_zone_device *tz,
> + enum thermal_device_mode mode)
> +{
> + struct qoriq_tmu_data *data = tz->devdata;
> +
> + if (mode == THERMAL_DEVICE_ENABLED) {
> + tz->polling_delay = TMU_POLLING_DELAY;
> + tz->passive_delay = TMU_PASSIVE_DELAY;
> + thermal_zone_device_update(tz);
> + } else {
> + tz->polling_delay = 0;
> + tz->passive_delay = 0;
> + }
> +
> + data->mode = mode;
> +
> + return 0;
> +}
> +
> +static int tmu_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
> +{
> + u8 val;
> + struct qoriq_tmu_data *data = tz->devdata;
> +
> + val = ioread32be(&data->regs->site[0].tritsr);
> + *temp = (unsigned long)val * 1000;
> +
> + return 0;
> +}
> +
> +static int tmu_get_trip_type(struct thermal_zone_device *tz, int trip,
> + enum thermal_trip_type *type)
> +{
> + *type = (trip == TMU_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
> + THERMAL_TRIP_CRITICAL;
> +
> + return 0;
> +}
> +
> +static int tmu_get_trip_temp(struct thermal_zone_device *tz, int trip,
> + unsigned long *temp)
> +{
> + struct qoriq_tmu_data *data = tz->devdata;
> +
> + *temp = (trip == TMU_TRIP_PASSIVE) ? data->temp_passive :
> + data->temp_critical;
> +
> + return 0;
> +}
> +
> +static int tmu_get_crit_temp(struct thermal_zone_device *tz,
> + unsigned long *temp)
> +{
> + struct qoriq_tmu_data *data = tz->devdata;
> +
> + *temp = data->temp_critical;
> +
> + return 0;
> +}
> +
> +static int tmu_bind(struct thermal_zone_device *tz,
> + struct thermal_cooling_device *cdev)
> +{
> + int ret;
> +
> + ret = thermal_zone_bind_cooling_device(tz, TMU_TRIP_PASSIVE, cdev,
> + THERMAL_NO_LIMIT,
> + THERMAL_NO_LIMIT,
> + THERMAL_WEIGHT_DEFAULT);
> + if (ret) {
> + dev_err(&tz->device,
> + "Binding zone %s with cdev %s failed:%d\n",
> + tz->type, cdev->type, ret);
> + }
> +
> + return ret;
> +}
> +
> +static int tmu_unbind(struct thermal_zone_device *tz,
> + struct thermal_cooling_device *cdev)
> +{
> + int ret;
> +
> + ret = thermal_zone_unbind_cooling_device(tz, TMU_TRIP_PASSIVE, cdev);
> + if (ret) {
> + dev_err(&tz->device,
> + "Unbinding zone %s with cdev %s failed:%d\n",
> + tz->type, cdev->type, ret);
> + }
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_calibration(struct platform_device *pdev)
> +{
> + int i, val, len;
> + u32 range[4];
> + const __be32 *calibration;
> + struct device_node *node = pdev->dev.of_node;
> + struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev);
> +
> + /* Disable monitoring before calibration */
> + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> +
> + if (of_property_read_u32_array(node, "fsl,tmu-range", range, 4))
> + return -1;
> +
> + /* Init temperature range registers */
> + iowrite32be(range[0], &data->regs->ttr0cr);
> + iowrite32be(range[1], &data->regs->ttr1cr);
> + iowrite32be(range[2], &data->regs->ttr2cr);
> + iowrite32be(range[3], &data->regs->ttr3cr);
> +
> + calibration = of_get_property(node, "fsl,tmu-calibration", &len);
> + if (calibration == NULL)
> + return -1;
> +
> + for (i = 0; i < len; i += 8, calibration += 2) {
> + val = (int)of_read_number(calibration, 1);
> + iowrite32be(val, &data->regs->ttcfgr);
> + val = (int)of_read_number(calibration + 1, 1);
> + iowrite32be(val, &data->regs->tscfgr);
> + }
> +
> + return 0;
> +}
> +
> +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
> +{
> + /* Disable interrupt, using polling instead */
> + iowrite32be(TIER_DISABLE, &data->regs->tier);
> +
> + /* Set update_interval */
> + iowrite32be(TMTMIR_DEFAULT, &data->regs->tmtmir);
> +
> + /* Enable monitoring */
> + iowrite32be(TMR_ALL, &data->regs->tmr);
> +}
> +
> +static struct thermal_zone_device_ops tmu_tz_ops = {
> + .bind = tmu_bind,
> + .unbind = tmu_unbind,
> + .get_temp = tmu_get_temp,
> + .get_mode = tmu_get_mode,
> + .set_mode = tmu_set_mode,
> + .get_trip_type = tmu_get_trip_type,
> + .get_trip_temp = tmu_get_trip_temp,
> + .get_crit_temp = tmu_get_crit_temp,
> +};
> +
> +static int qoriq_tmu_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct cpumask clip_cpus;
> + struct qoriq_tmu_data *data;
> +
> + if (!cpufreq_get_current_driver()) {
> + dev_dbg(&pdev->dev, "No cpufreq driver yet\n");
> + return -EPROBE_DEFER;
> + }
> +
> + if (!pdev->dev.of_node) {
> + dev_err(&pdev->dev, "Device OF-Node is NULL");
> + return -EFAULT;
> + }
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> + GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + dev_set_drvdata(&pdev->dev, data);
> + data->regs = of_iomap(pdev->dev.of_node, 0);
> +
> + if (!data->regs) {
> + dev_err(&pdev->dev, "Failed to get memory region\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> + if (ret < 0) {
> + dev_err(&pdev->dev, "TMU calibration failed.\n");
> + ret = -ENODEV;
> + goto err_iomap;
> + }
> +
> + qoriq_tmu_init_device(data); /* TMU initialization */
> +
> + cpumask_setall(&clip_cpus);
> + data->cdev = cpufreq_cooling_register(&clip_cpus);
> + if (IS_ERR(data->cdev)) {
> + ret = PTR_ERR(data->cdev);
> + dev_err(&data->cdev->device,
> + "Failed to register cpufreq cooling device: %d\n", ret);
> + goto err_cooling;
> + }
> +
> + data->temp_passive = TMU_TEMP_PASSIVE;
> + data->temp_critical = TMU_TEMP_CRITICAL;
> + data->tz = thermal_zone_device_register("tmu_thermal_zone",
Any specific reason why not using OF thermal?
> + TMU_TRIP_NUM,
> + 0, data,
> + &tmu_tz_ops, NULL,
> + TMU_PASSIVE_DELAY,
> + TMU_POLLING_DELAY);
> +
> + 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_thermal;
> + }
> +
> + data->mode = THERMAL_DEVICE_ENABLED;
> +
> + return 0;
> +
> +err_thermal:
> + cpufreq_cooling_unregister(data->cdev);
> +
> +err_cooling:
> + iounmap(data->regs);
> +
> +err_iomap:
> + dev_set_drvdata(&pdev->dev, NULL);
> + devm_kfree(&pdev->dev, data);
> +
> + return ret;
> +}
> +
> +static int qoriq_tmu_remove(struct platform_device *pdev)
> +{
> + struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev);
> +
> + /* Disable monitoring */
> + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> +
> + thermal_zone_device_unregister(data->tz);
> + cpufreq_cooling_unregister(data->cdev);
> + iounmap(data->regs);
> +
> + dev_set_drvdata(&pdev->dev, NULL);
> + devm_kfree(&pdev->dev, data);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qoriq_tmu_suspend(struct device *dev)
> +{
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Disable monitoring */
> + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> + data->mode = THERMAL_DEVICE_DISABLED;
> +
> + return 0;
> +}
> +
> +static int qoriq_tmu_resume(struct device *dev)
> +{
> + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> +
> + /* Enable monitoring */
> + iowrite32be(TMR_ALL, &data->regs->tmr);
> + data->mode = THERMAL_DEVICE_ENABLED;
> +
> + 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", },
> + {},
> +};
> +
> +static struct platform_driver qoriq_tmu = {
> + .driver = {
> + .owner = THIS_MODULE,
> + .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@freescale.com>");
> +MODULE_DESCRIPTION("Freescale QorIQ Thermal Monitoring Unit driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.1.0.27.g96db324
>
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH V2] QorIQ/TMU: add thermal management support based on TMU
2015-07-30 6:55 ` Eduardo Valentin
@ 2015-07-30 8:13 ` Hongtao Jia
2015-08-05 19:42 ` Eduardo Valentin
0 siblings, 1 reply; 8+ messages in thread
From: Hongtao Jia @ 2015-07-30 8:13 UTC (permalink / raw)
To: Eduardo Valentin
Cc: linux-pm@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
Scott Wood
- "Any specific reason why not using OF thermal?"
- No, actually.
I'd like to use OF thermal after some clarification.
Regarding to "cooling-maps". For some cases there should be more than one c=
pus
as cooling device and they are independent.
1. Let's say 4. So we need to provide 4 maps like map0-map3. Right?
2. "cooling-max-level" may vary depend on switch settings or firmware. Is t=
hat
OK if I do not provide "cooling-min-level" and "cooling-max-level" prope=
rty?
=20
Thanks.
-Hongtao
> -----Original Message-----
> From: Eduardo Valentin [mailto:edubezval@gmail.com]
> Sent: Thursday, July 30, 2015 2:56 PM
> To: Jia Hongtao-B38951
> Cc: linux-pm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Wood Scott-
> B07421
> Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management support based
> on TMU
>=20
> On Wed, Jul 29, 2015 at 02:19:39PM +0800, Jia Hongtao wrote:
> > 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.
> >
> > Signed-off-by: Jia Hongtao <hongtao.jia@freescale.com>
> > ---
> > This patch based on:
> > http://patchwork.ozlabs.org/patch/482987/
> >
> > Changes for V2:
> > * Add tmu-range parse.
> > * Use default trend hook.
> > * Using latest thermal_zone_bind_cooling_device API.
> > * Add calibration check during initialization.
> > * Disable/enalbe device when suspend/resume.
> >
> > drivers/thermal/Kconfig | 11 ++
> > drivers/thermal/Makefile | 1 +
> > drivers/thermal/qoriq_thermal.c | 406
> > ++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 418 insertions(+)
> > create mode 100644 drivers/thermal/qoriq_thermal.c
> >
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> > 118938e..a200745 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -180,6 +180,17 @@ config IMX_THERMAL
> > cpufreq is used as the cooling device to throttle CPUs when the
> > passive trip is crossed.
> >
> > +config QORIQ_THERMAL
> > + tristate "Freescale QorIQ Thermal Monitoring Unit"
> > + depends on CPU_THERMAL
> > + depends on OF
> > + default n
> > + help
> > + Enable thermal management based on Freescale QorIQ Thermal
> Monitoring
> > + Unit (TMU). 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
> > bool "SPEAr thermal sensor driver"
> > depends on PLAT_SPEAR
> > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> > 535dfee..8c25859 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -33,6 +33,7 @@ obj-$(CONFIG_DOVE_THERMAL) +=3D dove_thermal.o
> > obj-$(CONFIG_DB8500_THERMAL) +=3D db8500_thermal.o
> > obj-$(CONFIG_ARMADA_THERMAL) +=3D armada_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_thermal.c new file mode 100644 index
> > 0000000..0694f42
> > --- /dev/null
> > +++ b/drivers/thermal/qoriq_thermal.c
> > @@ -0,0 +1,406 @@
> > +/*
> > + * Copyright 2015 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.
> > + *
> > + */
> > +
> > +/*
> > + * Based on Freescale QorIQ Thermal Monitoring Unit (TMU) */
> > +#include <linux/cpufreq.h> #include <linux/cpu_cooling.h> #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>
> > +
> > +#define SITES_MAX 16
> > +
> > +#define TMU_TEMP_PASSIVE 85000
> > +#define TMU_TEMP_CRITICAL 95000
> > +
> > +#define TMU_PASSIVE_DELAY 1000 /* Milliseconds */
> > +#define TMU_POLLING_DELAY 5000
> > +
> > +/* The driver supports 1 passive trip point and 1 critical trip point
> > +*/ enum tmu_thermal_trip {
> > + TMU_TRIP_PASSIVE,
> > + TMU_TRIP_CRITICAL,
> > + TMU_TRIP_NUM,
> > +};
> > +
> > +/*
> > + * QorIQ TMU Registers
> > + */
> > +struct qoriq_tmu_site_regs {
> > + __be32 tritsr; /* Immediate Temperature Site Register */
> > + __be32 tratsr; /* Average Temperature Site Register */
> > + u8 res0[0x8];
> > +} __packed;
> > +
> > +struct qoriq_tmu_regs {
> > + __be32 tmr; /* Mode Register */
> > +#define TMR_DISABLE 0x0
> > +#define TMR_ME 0x80000000
> > +#define TMR_ALPF 0x0c000000
> > +#define TMR_MSITE 0x00008000
> > +#define TMR_ALL (TMR_ME | TMR_ALPF | TMR_MSITE)
> > + __be32 tsr; /* Status Register */
> > + __be32 tmtmir; /* Temperature measurement interval
> Register */
> > +#define TMTMIR_DEFAULT 0x00000007
> > + u8 res0[0x14];
> > + __be32 tier; /* Interrupt Enable Register */
> > +#define TIER_DISABLE 0x0
> > + __be32 tidr; /* Interrupt Detect Register */
> > + __be32 tiscr; /* Interrupt Site Capture Register */
> > + __be32 ticscr; /* Interrupt Critical Site Capture Register
> */
> > + u8 res1[0x10];
> > + __be32 tmhtcrh; /* High Temperature Capture Register */
> > + __be32 tmhtcrl; /* Low Temperature Capture Register */
> > + u8 res2[0x8];
> > + __be32 tmhtitr; /* High Temperature Immediate Threshold */
> > + __be32 tmhtatr; /* High Temperature Average Threshold */
> > + __be32 tmhtactr; /* High Temperature Average Crit Threshold */
> > + u8 res3[0x24];
> > + __be32 ttcfgr; /* Temperature Configuration Register */
> > + __be32 tscfgr; /* Sensor Configuration Register */
> > + u8 res4[0x78];
> > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > + u8 res5[0x9f8];
> > + __be32 ipbrr0; /* IP Block Revision Register 0 */
> > + __be32 ipbrr1; /* IP Block Revision Register 1 */
> > + u8 res6[0x310];
> > + __be32 ttr0cr; /* Temperature Range 0 Control Register */
> > + __be32 ttr1cr; /* Temperature Range 1 Control Register */
> > + __be32 ttr2cr; /* Temperature Range 2 Control Register */
> > + __be32 ttr3cr; /* Temperature Range 3 Control Register */
> > +};
> > +
> > +/*
> > + * Thermal zone data
> > + */
> > +struct qoriq_tmu_data {
> > + struct thermal_zone_device *tz;
> > + struct thermal_cooling_device *cdev;
> > + enum thermal_device_mode mode;
> > + unsigned long temp_passive;
> > + unsigned long temp_critical;
> > + struct qoriq_tmu_regs __iomem *regs; };
> > +
> > +static int tmu_get_mode(struct thermal_zone_device *tz,
> > + enum thermal_device_mode *mode)
> > +{
> > + struct qoriq_tmu_data *data =3D tz->devdata;
> > +
> > + *mode =3D data->mode;
> > +
> > + return 0;
> > +}
> > +
> > +static int tmu_set_mode(struct thermal_zone_device *tz,
> > + enum thermal_device_mode mode)
> > +{
> > + struct qoriq_tmu_data *data =3D tz->devdata;
> > +
> > + if (mode =3D=3D THERMAL_DEVICE_ENABLED) {
> > + tz->polling_delay =3D TMU_POLLING_DELAY;
> > + tz->passive_delay =3D TMU_PASSIVE_DELAY;
> > + thermal_zone_device_update(tz);
> > + } else {
> > + tz->polling_delay =3D 0;
> > + tz->passive_delay =3D 0;
> > + }
> > +
> > + data->mode =3D mode;
> > +
> > + return 0;
> > +}
> > +
> > +static int tmu_get_temp(struct thermal_zone_device *tz, unsigned long
> > +*temp) {
> > + u8 val;
> > + struct qoriq_tmu_data *data =3D tz->devdata;
> > +
> > + val =3D ioread32be(&data->regs->site[0].tritsr);
> > + *temp =3D (unsigned long)val * 1000;
> > +
> > + return 0;
> > +}
> > +
> > +static int tmu_get_trip_type(struct thermal_zone_device *tz, int trip,
> > + enum thermal_trip_type *type) {
> > + *type =3D (trip =3D=3D TMU_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
> > + THERMAL_TRIP_CRITICAL;
> > +
> > + return 0;
> > +}
> > +
> > +static int tmu_get_trip_temp(struct thermal_zone_device *tz, int trip,
> > + unsigned long *temp)
> > +{
> > + struct qoriq_tmu_data *data =3D tz->devdata;
> > +
> > + *temp =3D (trip =3D=3D TMU_TRIP_PASSIVE) ? data->temp_passive :
> > + data->temp_critical;
> > +
> > + return 0;
> > +}
> > +
> > +static int tmu_get_crit_temp(struct thermal_zone_device *tz,
> > + unsigned long *temp)
> > +{
> > + struct qoriq_tmu_data *data =3D tz->devdata;
> > +
> > + *temp =3D data->temp_critical;
> > +
> > + return 0;
> > +}
> > +
> > +static int tmu_bind(struct thermal_zone_device *tz,
> > + struct thermal_cooling_device *cdev) {
> > + int ret;
> > +
> > + ret =3D thermal_zone_bind_cooling_device(tz, TMU_TRIP_PASSIVE, cdev,
> > + THERMAL_NO_LIMIT,
> > + THERMAL_NO_LIMIT,
> > + THERMAL_WEIGHT_DEFAULT);
> > + if (ret) {
> > + dev_err(&tz->device,
> > + "Binding zone %s with cdev %s failed:%d\n",
> > + tz->type, cdev->type, ret);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int tmu_unbind(struct thermal_zone_device *tz,
> > + struct thermal_cooling_device *cdev) {
> > + int ret;
> > +
> > + ret =3D thermal_zone_unbind_cooling_device(tz, TMU_TRIP_PASSIVE,
> cdev);
> > + if (ret) {
> > + dev_err(&tz->device,
> > + "Unbinding zone %s with cdev %s failed:%d\n",
> > + tz->type, cdev->type, ret);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> > + int i, val, len;
> > + u32 range[4];
> > + const __be32 *calibration;
> > + struct device_node *node =3D pdev->dev.of_node;
> > + struct qoriq_tmu_data *data =3D dev_get_drvdata(&pdev->dev);
> > +
> > + /* Disable monitoring before calibration */
> > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > +
> > + if (of_property_read_u32_array(node, "fsl,tmu-range", range, 4))
> > + return -1;
> > +
> > + /* Init temperature range registers */
> > + iowrite32be(range[0], &data->regs->ttr0cr);
> > + iowrite32be(range[1], &data->regs->ttr1cr);
> > + iowrite32be(range[2], &data->regs->ttr2cr);
> > + iowrite32be(range[3], &data->regs->ttr3cr);
> > +
> > + calibration =3D of_get_property(node, "fsl,tmu-calibration", &len);
> > + if (calibration =3D=3D NULL)
> > + return -1;
> > +
> > + for (i =3D 0; i < len; i +=3D 8, calibration +=3D 2) {
> > + val =3D (int)of_read_number(calibration, 1);
> > + iowrite32be(val, &data->regs->ttcfgr);
> > + val =3D (int)of_read_number(calibration + 1, 1);
> > + iowrite32be(val, &data->regs->tscfgr);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> > + /* Disable interrupt, using polling instead */
> > + iowrite32be(TIER_DISABLE, &data->regs->tier);
> > +
> > + /* Set update_interval */
> > + iowrite32be(TMTMIR_DEFAULT, &data->regs->tmtmir);
> > +
> > + /* Enable monitoring */
> > + iowrite32be(TMR_ALL, &data->regs->tmr); }
> > +
> > +static struct thermal_zone_device_ops tmu_tz_ops =3D {
> > + .bind =3D tmu_bind,
> > + .unbind =3D tmu_unbind,
> > + .get_temp =3D tmu_get_temp,
> > + .get_mode =3D tmu_get_mode,
> > + .set_mode =3D tmu_set_mode,
> > + .get_trip_type =3D tmu_get_trip_type,
> > + .get_trip_temp =3D tmu_get_trip_temp,
> > + .get_crit_temp =3D tmu_get_crit_temp,
> > +};
> > +
> > +static int qoriq_tmu_probe(struct platform_device *pdev) {
> > + int ret;
> > + struct cpumask clip_cpus;
> > + struct qoriq_tmu_data *data;
> > +
> > + if (!cpufreq_get_current_driver()) {
> > + dev_dbg(&pdev->dev, "No cpufreq driver yet\n");
> > + return -EPROBE_DEFER;
> > + }
> > +
> > + if (!pdev->dev.of_node) {
> > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > + return -EFAULT;
> > + }
> > +
> > + data =3D devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> > + GFP_KERNEL);
> > + if (!data)
> > + return -ENOMEM;
> > +
> > + dev_set_drvdata(&pdev->dev, data);
> > + data->regs =3D of_iomap(pdev->dev.of_node, 0);
> > +
> > + if (!data->regs) {
> > + dev_err(&pdev->dev, "Failed to get memory region\n");
> > + ret =3D -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + ret =3D qoriq_tmu_calibration(pdev); /* TMU calibration */
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "TMU calibration failed.\n");
> > + ret =3D -ENODEV;
> > + goto err_iomap;
> > + }
> > +
> > + qoriq_tmu_init_device(data); /* TMU initialization */
> > +
> > + cpumask_setall(&clip_cpus);
> > + data->cdev =3D cpufreq_cooling_register(&clip_cpus);
> > + if (IS_ERR(data->cdev)) {
> > + ret =3D PTR_ERR(data->cdev);
> > + dev_err(&data->cdev->device,
> > + "Failed to register cpufreq cooling device: %d\n", ret);
> > + goto err_cooling;
> > + }
> > +
> > + data->temp_passive =3D TMU_TEMP_PASSIVE;
> > + data->temp_critical =3D TMU_TEMP_CRITICAL;
> > + data->tz =3D thermal_zone_device_register("tmu_thermal_zone",
>=20
> Any specific reason why not using OF thermal?
>=20
> > + TMU_TRIP_NUM,
> > + 0, data,
> > + &tmu_tz_ops, NULL,
> > + TMU_PASSIVE_DELAY,
> > + TMU_POLLING_DELAY);
> > +
> > + 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_thermal;
> > + }
> > +
> > + data->mode =3D THERMAL_DEVICE_ENABLED;
> > +
> > + return 0;
> > +
> > +err_thermal:
> > + cpufreq_cooling_unregister(data->cdev);
> > +
> > +err_cooling:
> > + iounmap(data->regs);
> > +
> > +err_iomap:
> > + dev_set_drvdata(&pdev->dev, NULL);
> > + devm_kfree(&pdev->dev, data);
> > +
> > + return ret;
> > +}
> > +
> > +static int qoriq_tmu_remove(struct platform_device *pdev) {
> > + struct qoriq_tmu_data *data =3D dev_get_drvdata(&pdev->dev);
> > +
> > + /* Disable monitoring */
> > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > +
> > + thermal_zone_device_unregister(data->tz);
> > + cpufreq_cooling_unregister(data->cdev);
> > + iounmap(data->regs);
> > +
> > + dev_set_drvdata(&pdev->dev, NULL);
> > + devm_kfree(&pdev->dev, data);
> > +
> > + return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int qoriq_tmu_suspend(struct device *dev) {
> > + struct qoriq_tmu_data *data =3D dev_get_drvdata(dev);
> > +
> > + /* Disable monitoring */
> > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > + data->mode =3D THERMAL_DEVICE_DISABLED;
> > +
> > + return 0;
> > +}
> > +
> > +static int qoriq_tmu_resume(struct device *dev) {
> > + struct qoriq_tmu_data *data =3D dev_get_drvdata(dev);
> > +
> > + /* Enable monitoring */
> > + iowrite32be(TMR_ALL, &data->regs->tmr);
> > + data->mode =3D THERMAL_DEVICE_ENABLED;
> > +
> > + 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", },
> > + {},
> > +};
> > +
> > +static struct platform_driver qoriq_tmu =3D {
> > + .driver =3D {
> > + .owner =3D THIS_MODULE,
> > + .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@freescale.com>");
> > +MODULE_DESCRIPTION("Freescale QorIQ Thermal Monitoring Unit driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.1.0.27.g96db324
> >
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH V2] QorIQ/TMU: add thermal management support based on TMU
2015-07-30 8:13 ` Hongtao Jia
@ 2015-08-05 19:42 ` Eduardo Valentin
2015-08-07 8:14 ` Hongtao Jia
0 siblings, 1 reply; 8+ messages in thread
From: Eduardo Valentin @ 2015-08-05 19:42 UTC (permalink / raw)
To: Hongtao Jia
Cc: linux-pm@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
Scott Wood
[-- Attachment #1: Type: text/plain, Size: 17738 bytes --]
On Thu, Jul 30, 2015 at 08:13:09AM +0000, Hongtao Jia wrote:
> - "Any specific reason why not using OF thermal?"
> - No, actually.
>
> I'd like to use OF thermal after some clarification.
>
> Regarding to "cooling-maps". For some cases there should be more than one cpus
> as cooling device and they are independent.
> 1. Let's say 4. So we need to provide 4 maps like map0-map3. Right?
That would depend on the amount of sensors you have. Do you have one
sensor per cpu? if the answer is yes, then you probably want to have
four different map entries, yes, but one on each thermal zone of each
cpu temperature sensor. if the answer is no, then you would need to have
all the maps in the same thermal zone.
> 2. "cooling-max-level" may vary depend on switch settings or firmware. Is that
> OK if I do not provide "cooling-min-level" and "cooling-max-level" property?
That is already achievable by using the cooling-device property of a
cooling map.
Please have a look in the example section of the
Documentation/devicetree/bindings/thermal/thermal.txt
Let me know if you need further clarification.
BR,
Eduardo Valentin
>
> Thanks.
> -Hongtao
>
>
> > -----Original Message-----
> > From: Eduardo Valentin [mailto:edubezval@gmail.com]
> > Sent: Thursday, July 30, 2015 2:56 PM
> > To: Jia Hongtao-B38951
> > Cc: linux-pm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Wood Scott-
> > B07421
> > Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management support based
> > on TMU
> >
> > On Wed, Jul 29, 2015 at 02:19:39PM +0800, Jia Hongtao wrote:
> > > 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.
> > >
> > > Signed-off-by: Jia Hongtao <hongtao.jia@freescale.com>
> > > ---
> > > This patch based on:
> > > http://patchwork.ozlabs.org/patch/482987/
> > >
> > > Changes for V2:
> > > * Add tmu-range parse.
> > > * Use default trend hook.
> > > * Using latest thermal_zone_bind_cooling_device API.
> > > * Add calibration check during initialization.
> > > * Disable/enalbe device when suspend/resume.
> > >
> > > drivers/thermal/Kconfig | 11 ++
> > > drivers/thermal/Makefile | 1 +
> > > drivers/thermal/qoriq_thermal.c | 406
> > > ++++++++++++++++++++++++++++++++++++++++
> > > 3 files changed, 418 insertions(+)
> > > create mode 100644 drivers/thermal/qoriq_thermal.c
> > >
> > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index
> > > 118938e..a200745 100644
> > > --- a/drivers/thermal/Kconfig
> > > +++ b/drivers/thermal/Kconfig
> > > @@ -180,6 +180,17 @@ config IMX_THERMAL
> > > cpufreq is used as the cooling device to throttle CPUs when the
> > > passive trip is crossed.
> > >
> > > +config QORIQ_THERMAL
> > > + tristate "Freescale QorIQ Thermal Monitoring Unit"
> > > + depends on CPU_THERMAL
> > > + depends on OF
> > > + default n
> > > + help
> > > + Enable thermal management based on Freescale QorIQ Thermal
> > Monitoring
> > > + Unit (TMU). 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
> > > bool "SPEAr thermal sensor driver"
> > > depends on PLAT_SPEAR
> > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index
> > > 535dfee..8c25859 100644
> > > --- a/drivers/thermal/Makefile
> > > +++ b/drivers/thermal/Makefile
> > > @@ -33,6 +33,7 @@ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
> > > obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
> > > obj-$(CONFIG_ARMADA_THERMAL) += armada_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..0694f42
> > > --- /dev/null
> > > +++ b/drivers/thermal/qoriq_thermal.c
> > > @@ -0,0 +1,406 @@
> > > +/*
> > > + * Copyright 2015 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.
> > > + *
> > > + */
> > > +
> > > +/*
> > > + * Based on Freescale QorIQ Thermal Monitoring Unit (TMU) */
> > > +#include <linux/cpufreq.h> #include <linux/cpu_cooling.h> #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>
> > > +
> > > +#define SITES_MAX 16
> > > +
> > > +#define TMU_TEMP_PASSIVE 85000
> > > +#define TMU_TEMP_CRITICAL 95000
> > > +
> > > +#define TMU_PASSIVE_DELAY 1000 /* Milliseconds */
> > > +#define TMU_POLLING_DELAY 5000
> > > +
> > > +/* The driver supports 1 passive trip point and 1 critical trip point
> > > +*/ enum tmu_thermal_trip {
> > > + TMU_TRIP_PASSIVE,
> > > + TMU_TRIP_CRITICAL,
> > > + TMU_TRIP_NUM,
> > > +};
> > > +
> > > +/*
> > > + * QorIQ TMU Registers
> > > + */
> > > +struct qoriq_tmu_site_regs {
> > > + __be32 tritsr; /* Immediate Temperature Site Register */
> > > + __be32 tratsr; /* Average Temperature Site Register */
> > > + u8 res0[0x8];
> > > +} __packed;
> > > +
> > > +struct qoriq_tmu_regs {
> > > + __be32 tmr; /* Mode Register */
> > > +#define TMR_DISABLE 0x0
> > > +#define TMR_ME 0x80000000
> > > +#define TMR_ALPF 0x0c000000
> > > +#define TMR_MSITE 0x00008000
> > > +#define TMR_ALL (TMR_ME | TMR_ALPF | TMR_MSITE)
> > > + __be32 tsr; /* Status Register */
> > > + __be32 tmtmir; /* Temperature measurement interval
> > Register */
> > > +#define TMTMIR_DEFAULT 0x00000007
> > > + u8 res0[0x14];
> > > + __be32 tier; /* Interrupt Enable Register */
> > > +#define TIER_DISABLE 0x0
> > > + __be32 tidr; /* Interrupt Detect Register */
> > > + __be32 tiscr; /* Interrupt Site Capture Register */
> > > + __be32 ticscr; /* Interrupt Critical Site Capture Register
> > */
> > > + u8 res1[0x10];
> > > + __be32 tmhtcrh; /* High Temperature Capture Register */
> > > + __be32 tmhtcrl; /* Low Temperature Capture Register */
> > > + u8 res2[0x8];
> > > + __be32 tmhtitr; /* High Temperature Immediate Threshold */
> > > + __be32 tmhtatr; /* High Temperature Average Threshold */
> > > + __be32 tmhtactr; /* High Temperature Average Crit Threshold */
> > > + u8 res3[0x24];
> > > + __be32 ttcfgr; /* Temperature Configuration Register */
> > > + __be32 tscfgr; /* Sensor Configuration Register */
> > > + u8 res4[0x78];
> > > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > > + u8 res5[0x9f8];
> > > + __be32 ipbrr0; /* IP Block Revision Register 0 */
> > > + __be32 ipbrr1; /* IP Block Revision Register 1 */
> > > + u8 res6[0x310];
> > > + __be32 ttr0cr; /* Temperature Range 0 Control Register */
> > > + __be32 ttr1cr; /* Temperature Range 1 Control Register */
> > > + __be32 ttr2cr; /* Temperature Range 2 Control Register */
> > > + __be32 ttr3cr; /* Temperature Range 3 Control Register */
> > > +};
> > > +
> > > +/*
> > > + * Thermal zone data
> > > + */
> > > +struct qoriq_tmu_data {
> > > + struct thermal_zone_device *tz;
> > > + struct thermal_cooling_device *cdev;
> > > + enum thermal_device_mode mode;
> > > + unsigned long temp_passive;
> > > + unsigned long temp_critical;
> > > + struct qoriq_tmu_regs __iomem *regs; };
> > > +
> > > +static int tmu_get_mode(struct thermal_zone_device *tz,
> > > + enum thermal_device_mode *mode)
> > > +{
> > > + struct qoriq_tmu_data *data = tz->devdata;
> > > +
> > > + *mode = data->mode;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int tmu_set_mode(struct thermal_zone_device *tz,
> > > + enum thermal_device_mode mode)
> > > +{
> > > + struct qoriq_tmu_data *data = tz->devdata;
> > > +
> > > + if (mode == THERMAL_DEVICE_ENABLED) {
> > > + tz->polling_delay = TMU_POLLING_DELAY;
> > > + tz->passive_delay = TMU_PASSIVE_DELAY;
> > > + thermal_zone_device_update(tz);
> > > + } else {
> > > + tz->polling_delay = 0;
> > > + tz->passive_delay = 0;
> > > + }
> > > +
> > > + data->mode = mode;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int tmu_get_temp(struct thermal_zone_device *tz, unsigned long
> > > +*temp) {
> > > + u8 val;
> > > + struct qoriq_tmu_data *data = tz->devdata;
> > > +
> > > + val = ioread32be(&data->regs->site[0].tritsr);
> > > + *temp = (unsigned long)val * 1000;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int tmu_get_trip_type(struct thermal_zone_device *tz, int trip,
> > > + enum thermal_trip_type *type) {
> > > + *type = (trip == TMU_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
> > > + THERMAL_TRIP_CRITICAL;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int tmu_get_trip_temp(struct thermal_zone_device *tz, int trip,
> > > + unsigned long *temp)
> > > +{
> > > + struct qoriq_tmu_data *data = tz->devdata;
> > > +
> > > + *temp = (trip == TMU_TRIP_PASSIVE) ? data->temp_passive :
> > > + data->temp_critical;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int tmu_get_crit_temp(struct thermal_zone_device *tz,
> > > + unsigned long *temp)
> > > +{
> > > + struct qoriq_tmu_data *data = tz->devdata;
> > > +
> > > + *temp = data->temp_critical;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int tmu_bind(struct thermal_zone_device *tz,
> > > + struct thermal_cooling_device *cdev) {
> > > + int ret;
> > > +
> > > + ret = thermal_zone_bind_cooling_device(tz, TMU_TRIP_PASSIVE, cdev,
> > > + THERMAL_NO_LIMIT,
> > > + THERMAL_NO_LIMIT,
> > > + THERMAL_WEIGHT_DEFAULT);
> > > + if (ret) {
> > > + dev_err(&tz->device,
> > > + "Binding zone %s with cdev %s failed:%d\n",
> > > + tz->type, cdev->type, ret);
> > > + }
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int tmu_unbind(struct thermal_zone_device *tz,
> > > + struct thermal_cooling_device *cdev) {
> > > + int ret;
> > > +
> > > + ret = thermal_zone_unbind_cooling_device(tz, TMU_TRIP_PASSIVE,
> > cdev);
> > > + if (ret) {
> > > + dev_err(&tz->device,
> > > + "Unbinding zone %s with cdev %s failed:%d\n",
> > > + tz->type, cdev->type, ret);
> > > + }
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> > > + int i, val, len;
> > > + u32 range[4];
> > > + const __be32 *calibration;
> > > + struct device_node *node = pdev->dev.of_node;
> > > + struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev);
> > > +
> > > + /* Disable monitoring before calibration */
> > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > +
> > > + if (of_property_read_u32_array(node, "fsl,tmu-range", range, 4))
> > > + return -1;
> > > +
> > > + /* Init temperature range registers */
> > > + iowrite32be(range[0], &data->regs->ttr0cr);
> > > + iowrite32be(range[1], &data->regs->ttr1cr);
> > > + iowrite32be(range[2], &data->regs->ttr2cr);
> > > + iowrite32be(range[3], &data->regs->ttr3cr);
> > > +
> > > + calibration = of_get_property(node, "fsl,tmu-calibration", &len);
> > > + if (calibration == NULL)
> > > + return -1;
> > > +
> > > + for (i = 0; i < len; i += 8, calibration += 2) {
> > > + val = (int)of_read_number(calibration, 1);
> > > + iowrite32be(val, &data->regs->ttcfgr);
> > > + val = (int)of_read_number(calibration + 1, 1);
> > > + iowrite32be(val, &data->regs->tscfgr);
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> > > + /* Disable interrupt, using polling instead */
> > > + iowrite32be(TIER_DISABLE, &data->regs->tier);
> > > +
> > > + /* Set update_interval */
> > > + iowrite32be(TMTMIR_DEFAULT, &data->regs->tmtmir);
> > > +
> > > + /* Enable monitoring */
> > > + iowrite32be(TMR_ALL, &data->regs->tmr); }
> > > +
> > > +static struct thermal_zone_device_ops tmu_tz_ops = {
> > > + .bind = tmu_bind,
> > > + .unbind = tmu_unbind,
> > > + .get_temp = tmu_get_temp,
> > > + .get_mode = tmu_get_mode,
> > > + .set_mode = tmu_set_mode,
> > > + .get_trip_type = tmu_get_trip_type,
> > > + .get_trip_temp = tmu_get_trip_temp,
> > > + .get_crit_temp = tmu_get_crit_temp,
> > > +};
> > > +
> > > +static int qoriq_tmu_probe(struct platform_device *pdev) {
> > > + int ret;
> > > + struct cpumask clip_cpus;
> > > + struct qoriq_tmu_data *data;
> > > +
> > > + if (!cpufreq_get_current_driver()) {
> > > + dev_dbg(&pdev->dev, "No cpufreq driver yet\n");
> > > + return -EPROBE_DEFER;
> > > + }
> > > +
> > > + if (!pdev->dev.of_node) {
> > > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > > + return -EFAULT;
> > > + }
> > > +
> > > + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> > > + GFP_KERNEL);
> > > + if (!data)
> > > + return -ENOMEM;
> > > +
> > > + dev_set_drvdata(&pdev->dev, data);
> > > + data->regs = of_iomap(pdev->dev.of_node, 0);
> > > +
> > > + if (!data->regs) {
> > > + dev_err(&pdev->dev, "Failed to get memory region\n");
> > > + ret = -ENODEV;
> > > + goto err_iomap;
> > > + }
> > > +
> > > + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> > > + if (ret < 0) {
> > > + dev_err(&pdev->dev, "TMU calibration failed.\n");
> > > + ret = -ENODEV;
> > > + goto err_iomap;
> > > + }
> > > +
> > > + qoriq_tmu_init_device(data); /* TMU initialization */
> > > +
> > > + cpumask_setall(&clip_cpus);
> > > + data->cdev = cpufreq_cooling_register(&clip_cpus);
> > > + if (IS_ERR(data->cdev)) {
> > > + ret = PTR_ERR(data->cdev);
> > > + dev_err(&data->cdev->device,
> > > + "Failed to register cpufreq cooling device: %d\n", ret);
> > > + goto err_cooling;
> > > + }
> > > +
> > > + data->temp_passive = TMU_TEMP_PASSIVE;
> > > + data->temp_critical = TMU_TEMP_CRITICAL;
> > > + data->tz = thermal_zone_device_register("tmu_thermal_zone",
> >
> > Any specific reason why not using OF thermal?
> >
> > > + TMU_TRIP_NUM,
> > > + 0, data,
> > > + &tmu_tz_ops, NULL,
> > > + TMU_PASSIVE_DELAY,
> > > + TMU_POLLING_DELAY);
> > > +
> > > + 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_thermal;
> > > + }
> > > +
> > > + data->mode = THERMAL_DEVICE_ENABLED;
> > > +
> > > + return 0;
> > > +
> > > +err_thermal:
> > > + cpufreq_cooling_unregister(data->cdev);
> > > +
> > > +err_cooling:
> > > + iounmap(data->regs);
> > > +
> > > +err_iomap:
> > > + dev_set_drvdata(&pdev->dev, NULL);
> > > + devm_kfree(&pdev->dev, data);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int qoriq_tmu_remove(struct platform_device *pdev) {
> > > + struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev);
> > > +
> > > + /* Disable monitoring */
> > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > +
> > > + thermal_zone_device_unregister(data->tz);
> > > + cpufreq_cooling_unregister(data->cdev);
> > > + iounmap(data->regs);
> > > +
> > > + dev_set_drvdata(&pdev->dev, NULL);
> > > + devm_kfree(&pdev->dev, data);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +#ifdef CONFIG_PM_SLEEP
> > > +static int qoriq_tmu_suspend(struct device *dev) {
> > > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > > +
> > > + /* Disable monitoring */
> > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > + data->mode = THERMAL_DEVICE_DISABLED;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int qoriq_tmu_resume(struct device *dev) {
> > > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > > +
> > > + /* Enable monitoring */
> > > + iowrite32be(TMR_ALL, &data->regs->tmr);
> > > + data->mode = THERMAL_DEVICE_ENABLED;
> > > +
> > > + 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", },
> > > + {},
> > > +};
> > > +
> > > +static struct platform_driver qoriq_tmu = {
> > > + .driver = {
> > > + .owner = THIS_MODULE,
> > > + .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@freescale.com>");
> > > +MODULE_DESCRIPTION("Freescale QorIQ Thermal Monitoring Unit driver");
> > > +MODULE_LICENSE("GPL v2");
> > > --
> > > 2.1.0.27.g96db324
> > >
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH V2] QorIQ/TMU: add thermal management support based on TMU
2015-08-05 19:42 ` Eduardo Valentin
@ 2015-08-07 8:14 ` Hongtao Jia
2015-08-14 3:15 ` Hongtao Jia
0 siblings, 1 reply; 8+ messages in thread
From: Hongtao Jia @ 2015-08-07 8:14 UTC (permalink / raw)
To: Eduardo Valentin
Cc: linux-pm@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
Scott Wood
Thanks for your comments.
Please see my questions inline.
Thanks.
---
Best Regards,
Hongtao
> -----Original Message-----
> From: Eduardo Valentin [mailto:edubezval@gmail.com]
> Sent: Thursday, August 06, 2015 3:43 AM
> To: Jia Hongtao-B38951
> Cc: linux-pm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Wood Scott-
> B07421
> Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management support based
> on TMU
>=20
> On Thu, Jul 30, 2015 at 08:13:09AM +0000, Hongtao Jia wrote:
> > - "Any specific reason why not using OF thermal?"
> > - No, actually.
> >
> > I'd like to use OF thermal after some clarification.
> >
> > Regarding to "cooling-maps". For some cases there should be more than
> > one cpus as cooling device and they are independent.
> > 1. Let's say 4. So we need to provide 4 maps like map0-map3. Right?
>=20
> That would depend on the amount of sensors you have. Do you have one
> sensor per cpu? if the answer is yes, then you probably want to have four
> different map entries, yes, but one on each thermal zone of each cpu
> temperature sensor. if the answer is no, then you would need to have all
> the maps in the same thermal zone.
>=20
> > 2. "cooling-max-level" may vary depend on switch settings or firmware.
> Is that
> > OK if I do not provide "cooling-min-level" and "cooling-max-level"
> property?
>=20
> That is already achievable by using the cooling-device property of a
> cooling map.
>=20
> Please have a look in the example section of the
> Documentation/devicetree/bindings/thermal/thermal.txt
Yes, I read this file.
So in my understanding:
There is no need to provide "cooling-min-level" and "cooling-max-level" pro=
perty.
THERMAL_NO_LIMIT value in cooling device node will indicate the driver to
automatically parse the min and max state, right?
Talking about THERMAL_NO_LIMIT, I need to #include <dt-bindings/thermal/the=
rmal.h>
to provide the definition. But I got compiling error when build dtb file.
I did some research and using "make t1040qds.dtb" in order to involve prepr=
ocessor.
But with simply adding "#include <dt-bindings/thermal/thermal.h>" to t1040s=
i-post.dtsi
at line 35 I still got error like this:
Error: arch/powerpc/boot/dts/fsl/t1040si-post.dtsi:35.1-9 syntax error
FATAL ERROR: Unable to parse input tree
Could you help me out here.
Thanks.
>=20
> Let me know if you need further clarification.
>=20
>=20
> BR,
>=20
> Eduardo Valentin
>=20
> >
> > Thanks.
> > -Hongtao
> >
> >
> > > -----Original Message-----
> > > From: Eduardo Valentin [mailto:edubezval@gmail.com]
> > > Sent: Thursday, July 30, 2015 2:56 PM
> > > To: Jia Hongtao-B38951
> > > Cc: linux-pm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Wood
> > > Scott-
> > > B07421
> > > Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management support
> > > based on TMU
> > >
> > > On Wed, Jul 29, 2015 at 02:19:39PM +0800, Jia Hongtao wrote:
> > > > 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.
> > > >
> > > > Signed-off-by: Jia Hongtao <hongtao.jia@freescale.com>
> > > > ---
> > > > This patch based on:
> > > > http://patchwork.ozlabs.org/patch/482987/
> > > >
> > > > Changes for V2:
> > > > * Add tmu-range parse.
> > > > * Use default trend hook.
> > > > * Using latest thermal_zone_bind_cooling_device API.
> > > > * Add calibration check during initialization.
> > > > * Disable/enalbe device when suspend/resume.
> > > >
> > > > drivers/thermal/Kconfig | 11 ++
> > > > drivers/thermal/Makefile | 1 +
> > > > drivers/thermal/qoriq_thermal.c | 406
> > > > ++++++++++++++++++++++++++++++++++++++++
> > > > 3 files changed, 418 insertions(+) create mode 100644
> > > > drivers/thermal/qoriq_thermal.c
> > > >
> > > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > > > index
> > > > 118938e..a200745 100644
> > > > --- a/drivers/thermal/Kconfig
> > > > +++ b/drivers/thermal/Kconfig
> > > > @@ -180,6 +180,17 @@ config IMX_THERMAL
> > > > cpufreq is used as the cooling device to throttle CPUs when
> the
> > > > passive trip is crossed.
> > > >
> > > > +config QORIQ_THERMAL
> > > > + tristate "Freescale QorIQ Thermal Monitoring Unit"
> > > > + depends on CPU_THERMAL
> > > > + depends on OF
> > > > + default n
> > > > + help
> > > > + Enable thermal management based on Freescale QorIQ Thermal
> > > Monitoring
> > > > + Unit (TMU). 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
> > > > bool "SPEAr thermal sensor driver"
> > > > depends on PLAT_SPEAR
> > > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > > > index
> > > > 535dfee..8c25859 100644
> > > > --- a/drivers/thermal/Makefile
> > > > +++ b/drivers/thermal/Makefile
> > > > @@ -33,6 +33,7 @@ obj-$(CONFIG_DOVE_THERMAL) +=3D dove_thermal.o
> > > > obj-$(CONFIG_DB8500_THERMAL) +=3D db8500_thermal.o
> > > > obj-$(CONFIG_ARMADA_THERMAL) +=3D armada_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_thermal.c new file mode 100644 index
> > > > 0000000..0694f42
> > > > --- /dev/null
> > > > +++ b/drivers/thermal/qoriq_thermal.c
> > > > @@ -0,0 +1,406 @@
> > > > +/*
> > > > + * Copyright 2015 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.
> > > > + *
> > > > + */
> > > > +
> > > > +/*
> > > > + * Based on Freescale QorIQ Thermal Monitoring Unit (TMU) */
> > > > +#include <linux/cpufreq.h> #include <linux/cpu_cooling.h>
> > > > +#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>
> > > > +
> > > > +#define SITES_MAX 16
> > > > +
> > > > +#define TMU_TEMP_PASSIVE 85000
> > > > +#define TMU_TEMP_CRITICAL 95000
> > > > +
> > > > +#define TMU_PASSIVE_DELAY 1000 /* Milliseconds */
> > > > +#define TMU_POLLING_DELAY 5000
> > > > +
> > > > +/* The driver supports 1 passive trip point and 1 critical trip
> > > > +point */ enum tmu_thermal_trip {
> > > > + TMU_TRIP_PASSIVE,
> > > > + TMU_TRIP_CRITICAL,
> > > > + TMU_TRIP_NUM,
> > > > +};
> > > > +
> > > > +/*
> > > > + * QorIQ TMU Registers
> > > > + */
> > > > +struct qoriq_tmu_site_regs {
> > > > + __be32 tritsr; /* Immediate Temperature Site
> Register */
> > > > + __be32 tratsr; /* Average Temperature Site Register
> */
> > > > + u8 res0[0x8];
> > > > +} __packed;
> > > > +
> > > > +struct qoriq_tmu_regs {
> > > > + __be32 tmr; /* Mode Register */
> > > > +#define TMR_DISABLE 0x0
> > > > +#define TMR_ME 0x80000000
> > > > +#define TMR_ALPF 0x0c000000
> > > > +#define TMR_MSITE 0x00008000
> > > > +#define TMR_ALL (TMR_ME | TMR_ALPF | TMR_MSITE)
> > > > + __be32 tsr; /* Status Register */
> > > > + __be32 tmtmir; /* Temperature measurement interval
> > > Register */
> > > > +#define TMTMIR_DEFAULT 0x00000007
> > > > + u8 res0[0x14];
> > > > + __be32 tier; /* Interrupt Enable Register */
> > > > +#define TIER_DISABLE 0x0
> > > > + __be32 tidr; /* Interrupt Detect Register */
> > > > + __be32 tiscr; /* Interrupt Site Capture Register */
> > > > + __be32 ticscr; /* Interrupt Critical Site Capture
> Register
> > > */
> > > > + u8 res1[0x10];
> > > > + __be32 tmhtcrh; /* High Temperature Capture Register
> */
> > > > + __be32 tmhtcrl; /* Low Temperature Capture Register
> */
> > > > + u8 res2[0x8];
> > > > + __be32 tmhtitr; /* High Temperature Immediate
> Threshold */
> > > > + __be32 tmhtatr; /* High Temperature Average Threshold
> */
> > > > + __be32 tmhtactr; /* High Temperature Average Crit Threshold
> */
> > > > + u8 res3[0x24];
> > > > + __be32 ttcfgr; /* Temperature Configuration Register
> */
> > > > + __be32 tscfgr; /* Sensor Configuration Register */
> > > > + u8 res4[0x78];
> > > > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > > > + u8 res5[0x9f8];
> > > > + __be32 ipbrr0; /* IP Block Revision Register 0 */
> > > > + __be32 ipbrr1; /* IP Block Revision Register 1 */
> > > > + u8 res6[0x310];
> > > > + __be32 ttr0cr; /* Temperature Range 0 Control
> Register */
> > > > + __be32 ttr1cr; /* Temperature Range 1 Control
> Register */
> > > > + __be32 ttr2cr; /* Temperature Range 2 Control
> Register */
> > > > + __be32 ttr3cr; /* Temperature Range 3 Control
> Register */
> > > > +};
> > > > +
> > > > +/*
> > > > + * Thermal zone data
> > > > + */
> > > > +struct qoriq_tmu_data {
> > > > + struct thermal_zone_device *tz;
> > > > + struct thermal_cooling_device *cdev;
> > > > + enum thermal_device_mode mode;
> > > > + unsigned long temp_passive;
> > > > + unsigned long temp_critical;
> > > > + struct qoriq_tmu_regs __iomem *regs; };
> > > > +
> > > > +static int tmu_get_mode(struct thermal_zone_device *tz,
> > > > + enum thermal_device_mode *mode) {
> > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > +
> > > > + *mode =3D data->mode;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int tmu_set_mode(struct thermal_zone_device *tz,
> > > > + enum thermal_device_mode mode) {
> > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > +
> > > > + if (mode =3D=3D THERMAL_DEVICE_ENABLED) {
> > > > + tz->polling_delay =3D TMU_POLLING_DELAY;
> > > > + tz->passive_delay =3D TMU_PASSIVE_DELAY;
> > > > + thermal_zone_device_update(tz);
> > > > + } else {
> > > > + tz->polling_delay =3D 0;
> > > > + tz->passive_delay =3D 0;
> > > > + }
> > > > +
> > > > + data->mode =3D mode;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int tmu_get_temp(struct thermal_zone_device *tz, unsigned
> > > > +long
> > > > +*temp) {
> > > > + u8 val;
> > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > +
> > > > + val =3D ioread32be(&data->regs->site[0].tritsr);
> > > > + *temp =3D (unsigned long)val * 1000;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int tmu_get_trip_type(struct thermal_zone_device *tz, int
> trip,
> > > > + enum thermal_trip_type *type) {
> > > > + *type =3D (trip =3D=3D TMU_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
> > > > + THERMAL_TRIP_CRITICAL;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int tmu_get_trip_temp(struct thermal_zone_device *tz, int
> trip,
> > > > + unsigned long *temp)
> > > > +{
> > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > +
> > > > + *temp =3D (trip =3D=3D TMU_TRIP_PASSIVE) ? data->temp_passive :
> > > > + data->temp_critical;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int tmu_get_crit_temp(struct thermal_zone_device *tz,
> > > > + unsigned long *temp)
> > > > +{
> > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > +
> > > > + *temp =3D data->temp_critical;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int tmu_bind(struct thermal_zone_device *tz,
> > > > + struct thermal_cooling_device *cdev) {
> > > > + int ret;
> > > > +
> > > > + ret =3D thermal_zone_bind_cooling_device(tz, TMU_TRIP_PASSIVE,
> cdev,
> > > > + THERMAL_NO_LIMIT,
> > > > + THERMAL_NO_LIMIT,
> > > > + THERMAL_WEIGHT_DEFAULT);
> > > > + if (ret) {
> > > > + dev_err(&tz->device,
> > > > + "Binding zone %s with cdev %s failed:%d\n",
> > > > + tz->type, cdev->type, ret);
> > > > + }
> > > > +
> > > > + return ret;
> > > > +}
> > > > +
> > > > +static int tmu_unbind(struct thermal_zone_device *tz,
> > > > + struct thermal_cooling_device *cdev) {
> > > > + int ret;
> > > > +
> > > > + ret =3D thermal_zone_unbind_cooling_device(tz, TMU_TRIP_PASSIVE,
> > > cdev);
> > > > + if (ret) {
> > > > + dev_err(&tz->device,
> > > > + "Unbinding zone %s with cdev %s failed:%d\n",
> > > > + tz->type, cdev->type, ret);
> > > > + }
> > > > +
> > > > + return ret;
> > > > +}
> > > > +
> > > > +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> > > > + int i, val, len;
> > > > + u32 range[4];
> > > > + const __be32 *calibration;
> > > > + struct device_node *node =3D pdev->dev.of_node;
> > > > + struct qoriq_tmu_data *data =3D dev_get_drvdata(&pdev->dev);
> > > > +
> > > > + /* Disable monitoring before calibration */
> > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > +
> > > > + if (of_property_read_u32_array(node, "fsl,tmu-range", range,
> 4))
> > > > + return -1;
> > > > +
> > > > + /* Init temperature range registers */
> > > > + iowrite32be(range[0], &data->regs->ttr0cr);
> > > > + iowrite32be(range[1], &data->regs->ttr1cr);
> > > > + iowrite32be(range[2], &data->regs->ttr2cr);
> > > > + iowrite32be(range[3], &data->regs->ttr3cr);
> > > > +
> > > > + calibration =3D of_get_property(node, "fsl,tmu-calibration",
> &len);
> > > > + if (calibration =3D=3D NULL)
> > > > + return -1;
> > > > +
> > > > + for (i =3D 0; i < len; i +=3D 8, calibration +=3D 2) {
> > > > + val =3D (int)of_read_number(calibration, 1);
> > > > + iowrite32be(val, &data->regs->ttcfgr);
> > > > + val =3D (int)of_read_number(calibration + 1, 1);
> > > > + iowrite32be(val, &data->regs->tscfgr);
> > > > + }
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> > > > + /* Disable interrupt, using polling instead */
> > > > + iowrite32be(TIER_DISABLE, &data->regs->tier);
> > > > +
> > > > + /* Set update_interval */
> > > > + iowrite32be(TMTMIR_DEFAULT, &data->regs->tmtmir);
> > > > +
> > > > + /* Enable monitoring */
> > > > + iowrite32be(TMR_ALL, &data->regs->tmr); }
> > > > +
> > > > +static struct thermal_zone_device_ops tmu_tz_ops =3D {
> > > > + .bind =3D tmu_bind,
> > > > + .unbind =3D tmu_unbind,
> > > > + .get_temp =3D tmu_get_temp,
> > > > + .get_mode =3D tmu_get_mode,
> > > > + .set_mode =3D tmu_set_mode,
> > > > + .get_trip_type =3D tmu_get_trip_type,
> > > > + .get_trip_temp =3D tmu_get_trip_temp,
> > > > + .get_crit_temp =3D tmu_get_crit_temp, };
> > > > +
> > > > +static int qoriq_tmu_probe(struct platform_device *pdev) {
> > > > + int ret;
> > > > + struct cpumask clip_cpus;
> > > > + struct qoriq_tmu_data *data;
> > > > +
> > > > + if (!cpufreq_get_current_driver()) {
> > > > + dev_dbg(&pdev->dev, "No cpufreq driver yet\n");
> > > > + return -EPROBE_DEFER;
> > > > + }
> > > > +
> > > > + if (!pdev->dev.of_node) {
> > > > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > > > + return -EFAULT;
> > > > + }
> > > > +
> > > > + data =3D devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> > > > + GFP_KERNEL);
> > > > + if (!data)
> > > > + return -ENOMEM;
> > > > +
> > > > + dev_set_drvdata(&pdev->dev, data);
> > > > + data->regs =3D of_iomap(pdev->dev.of_node, 0);
> > > > +
> > > > + if (!data->regs) {
> > > > + dev_err(&pdev->dev, "Failed to get memory region\n");
> > > > + ret =3D -ENODEV;
> > > > + goto err_iomap;
> > > > + }
> > > > +
> > > > + ret =3D qoriq_tmu_calibration(pdev); /* TMU calibration */
> > > > + if (ret < 0) {
> > > > + dev_err(&pdev->dev, "TMU calibration failed.\n");
> > > > + ret =3D -ENODEV;
> > > > + goto err_iomap;
> > > > + }
> > > > +
> > > > + qoriq_tmu_init_device(data); /* TMU initialization */
> > > > +
> > > > + cpumask_setall(&clip_cpus);
> > > > + data->cdev =3D cpufreq_cooling_register(&clip_cpus);
> > > > + if (IS_ERR(data->cdev)) {
> > > > + ret =3D PTR_ERR(data->cdev);
> > > > + dev_err(&data->cdev->device,
> > > > + "Failed to register cpufreq cooling device: %d\n",
> ret);
> > > > + goto err_cooling;
> > > > + }
> > > > +
> > > > + data->temp_passive =3D TMU_TEMP_PASSIVE;
> > > > + data->temp_critical =3D TMU_TEMP_CRITICAL;
> > > > + data->tz =3D thermal_zone_device_register("tmu_thermal_zone",
> > >
> > > Any specific reason why not using OF thermal?
> > >
> > > > + TMU_TRIP_NUM,
> > > > + 0, data,
> > > > + &tmu_tz_ops, NULL,
> > > > + TMU_PASSIVE_DELAY,
> > > > + TMU_POLLING_DELAY);
> > > > +
> > > > + 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_thermal;
> > > > + }
> > > > +
> > > > + data->mode =3D THERMAL_DEVICE_ENABLED;
> > > > +
> > > > + return 0;
> > > > +
> > > > +err_thermal:
> > > > + cpufreq_cooling_unregister(data->cdev);
> > > > +
> > > > +err_cooling:
> > > > + iounmap(data->regs);
> > > > +
> > > > +err_iomap:
> > > > + dev_set_drvdata(&pdev->dev, NULL);
> > > > + devm_kfree(&pdev->dev, data);
> > > > +
> > > > + return ret;
> > > > +}
> > > > +
> > > > +static int qoriq_tmu_remove(struct platform_device *pdev) {
> > > > + struct qoriq_tmu_data *data =3D dev_get_drvdata(&pdev->dev);
> > > > +
> > > > + /* Disable monitoring */
> > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > +
> > > > + thermal_zone_device_unregister(data->tz);
> > > > + cpufreq_cooling_unregister(data->cdev);
> > > > + iounmap(data->regs);
> > > > +
> > > > + dev_set_drvdata(&pdev->dev, NULL);
> > > > + devm_kfree(&pdev->dev, data);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +#ifdef CONFIG_PM_SLEEP
> > > > +static int qoriq_tmu_suspend(struct device *dev) {
> > > > + struct qoriq_tmu_data *data =3D dev_get_drvdata(dev);
> > > > +
> > > > + /* Disable monitoring */
> > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > + data->mode =3D THERMAL_DEVICE_DISABLED;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int qoriq_tmu_resume(struct device *dev) {
> > > > + struct qoriq_tmu_data *data =3D dev_get_drvdata(dev);
> > > > +
> > > > + /* Enable monitoring */
> > > > + iowrite32be(TMR_ALL, &data->regs->tmr);
> > > > + data->mode =3D THERMAL_DEVICE_ENABLED;
> > > > +
> > > > + 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", },
> > > > + {},
> > > > +};
> > > > +
> > > > +static struct platform_driver qoriq_tmu =3D {
> > > > + .driver =3D {
> > > > + .owner =3D THIS_MODULE,
> > > > + .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@freescale.com>");
> > > > +MODULE_DESCRIPTION("Freescale QorIQ Thermal Monitoring Unit
> > > > +driver"); MODULE_LICENSE("GPL v2");
> > > > --
> > > > 2.1.0.27.g96db324
> > > >
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH V2] QorIQ/TMU: add thermal management support based on TMU
2015-08-07 8:14 ` Hongtao Jia
@ 2015-08-14 3:15 ` Hongtao Jia
2015-08-14 4:29 ` Eduardo Valentin
0 siblings, 1 reply; 8+ messages in thread
From: Hongtao Jia @ 2015-08-14 3:15 UTC (permalink / raw)
To: Eduardo Valentin
Cc: Scott Wood, linuxppc-dev@lists.ozlabs.org,
linux-pm@vger.kernel.org, Hongtao Jia
SGkgRWR1YXJkbywNCg0KSW4gcHJldmlvdXMgbWFpbCBJIGFza2VkIHF1ZXN0aW9ucyBhYm91dCBp
bmNsdWRpbmcgaGVhZGVyIGZpbGVzIGluIGRldmljZSB0cmVlLg0KRG9uJ3QgYm90aGVyLCBJIGhh
dmUgYWxyZWFkeSBmaWd1cmVkIG91dCB0aGUgc29sdXRpb24uDQoNCkFub3RoZXIgcXVlc3Rpb25z
IGlzIGFib3V0IGNwdSBjb29saW5nOg0KSSBmb3VuZCBvdXQgdGhhdCB0aGVyZSBpcyBubyBleHBs
aWNpdCBjYWxsaW5nIGZvciByZWdpc3RlcmluZyBjcHUgY29vbGluZw0KZGV2aWNlIGluIHRoZSBv
Zi10aGVybWFsIHN0eWxlIGRyaXZlcnMuDQoNCkFuZCBTYW1zdW5nIGRpZCBpdCBpbiBjcHVmcmVx
IGRyaXZlcjogZHJpdmVycy9jcHVmcmVxL2V4eW5vcy1jcHVmcmVxLmMNCg0KU2hvdWxkIGFsbCB0
aGUgb2YtdGhlcm1hbCBkcml2ZXIgdXNlIHRoZSBzYW1lIHdheT8NCk9yIGlzIHRoZXJlIGFueSBy
ZWNvbW1lbmRhdGlvbiBmb3IgcmVnaXN0ZXJpbmcgY3B1IGNvb2xpbmcgZGV2aWNlPw0KKEkgZW5h
YmxlZCB0aGUgQ09ORklHX0NQVUZSRVFfRFQgYW5kIHN0aWxsIGdvdCBubyBjb29saW5nIGRldmlj
ZSByZWdpc3RlcmVkKQ0KDQpUaGFua3MuDQoNCi0tLQ0KQmVzdCBSZWdhcmRzLA0KSG9uZ3Rhbw0K
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogTGludXhwcGMtZGV2IFtt
YWlsdG86bGludXhwcGMtZGV2LQ0KPiBib3VuY2VzK2IzODk1MT1mcmVlc2NhbGUuY29tQGxpc3Rz
Lm96bGFicy5vcmddIE9uIEJlaGFsZiBPZiBIb25ndGFvIEppYQ0KPiBTZW50OiBGcmlkYXksIEF1
Z3VzdCAwNywgMjAxNSA0OjE1IFBNDQo+IFRvOiBFZHVhcmRvIFZhbGVudGluDQo+IENjOiBXb29k
IFNjb3R0LUIwNzQyMTsgbGludXhwcGMtZGV2QGxpc3RzLm96bGFicy5vcmc7IGxpbnV4LQ0KPiBw
bUB2Z2VyLmtlcm5lbC5vcmcNCj4gU3ViamVjdDogUkU6IFtQQVRDSCBWMl0gUW9ySVEvVE1VOiBh
ZGQgdGhlcm1hbCBtYW5hZ2VtZW50IHN1cHBvcnQgYmFzZWQNCj4gb24gVE1VDQo+IA0KPiBUaGFu
a3MgZm9yIHlvdXIgY29tbWVudHMuDQo+IFBsZWFzZSBzZWUgbXkgcXVlc3Rpb25zIGlubGluZS4N
Cj4gDQo+IFRoYW5rcy4NCj4gLS0tDQo+IEJlc3QgUmVnYXJkcywNCj4gSG9uZ3Rhbw0KPiANCj4g
DQo+ID4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gPiBGcm9tOiBFZHVhcmRvIFZhbGVu
dGluIFttYWlsdG86ZWR1YmV6dmFsQGdtYWlsLmNvbV0NCj4gPiBTZW50OiBUaHVyc2RheSwgQXVn
dXN0IDA2LCAyMDE1IDM6NDMgQU0NCj4gPiBUbzogSmlhIEhvbmd0YW8tQjM4OTUxDQo+ID4gQ2M6
IGxpbnV4LXBtQHZnZXIua2VybmVsLm9yZzsgbGludXhwcGMtZGV2QGxpc3RzLm96bGFicy5vcmc7
IFdvb2QNCj4gPiBTY290dC0NCj4gPiBCMDc0MjENCj4gPiBTdWJqZWN0OiBSZTogW1BBVENIIFYy
XSBRb3JJUS9UTVU6IGFkZCB0aGVybWFsIG1hbmFnZW1lbnQgc3VwcG9ydA0KPiA+IGJhc2VkIG9u
IFRNVQ0KPiA+DQo+ID4gT24gVGh1LCBKdWwgMzAsIDIwMTUgYXQgMDg6MTM6MDlBTSArMDAwMCwg
SG9uZ3RhbyBKaWEgd3JvdGU6DQo+ID4gPiAtICJBbnkgc3BlY2lmaWMgcmVhc29uIHdoeSBub3Qg
dXNpbmcgT0YgdGhlcm1hbD8iDQo+ID4gPiAtIE5vLCBhY3R1YWxseS4NCj4gPiA+DQo+ID4gPiBJ
J2QgbGlrZSB0byB1c2UgT0YgdGhlcm1hbCBhZnRlciBzb21lIGNsYXJpZmljYXRpb24uDQo+ID4g
Pg0KPiA+ID4gUmVnYXJkaW5nIHRvICJjb29saW5nLW1hcHMiLiBGb3Igc29tZSBjYXNlcyB0aGVy
ZSBzaG91bGQgYmUgbW9yZQ0KPiA+ID4gdGhhbiBvbmUgY3B1cyBhcyBjb29saW5nIGRldmljZSBh
bmQgdGhleSBhcmUgaW5kZXBlbmRlbnQuDQo+ID4gPiAxLiBMZXQncyBzYXkgNC4gU28gd2UgbmVl
ZCB0byBwcm92aWRlIDQgbWFwcyBsaWtlIG1hcDAtbWFwMy4gUmlnaHQ/DQo+ID4NCj4gPiBUaGF0
IHdvdWxkIGRlcGVuZCBvbiB0aGUgYW1vdW50IG9mIHNlbnNvcnMgeW91IGhhdmUuIERvIHlvdSBo
YXZlIG9uZQ0KPiA+IHNlbnNvciBwZXIgY3B1PyBpZiB0aGUgYW5zd2VyIGlzIHllcywgdGhlbiB5
b3UgcHJvYmFibHkgd2FudCB0byBoYXZlDQo+ID4gZm91ciBkaWZmZXJlbnQgbWFwIGVudHJpZXMs
IHllcywgYnV0IG9uZSBvbiBlYWNoIHRoZXJtYWwgem9uZSBvZiBlYWNoDQo+ID4gY3B1IHRlbXBl
cmF0dXJlIHNlbnNvci4gaWYgdGhlIGFuc3dlciBpcyBubywgdGhlbiB5b3Ugd291bGQgbmVlZCB0
bw0KPiA+IGhhdmUgYWxsIHRoZSBtYXBzIGluIHRoZSBzYW1lIHRoZXJtYWwgem9uZS4NCj4gPg0K
PiA+ID4gMi4gImNvb2xpbmctbWF4LWxldmVsIiBtYXkgdmFyeSBkZXBlbmQgb24gc3dpdGNoIHNl
dHRpbmdzIG9yIGZpcm13YXJlLg0KPiA+IElzIHRoYXQNCj4gPiA+ICAgIE9LIGlmIEkgZG8gbm90
IHByb3ZpZGUgImNvb2xpbmctbWluLWxldmVsIiBhbmQgImNvb2xpbmctbWF4LWxldmVsIg0KPiA+
IHByb3BlcnR5Pw0KPiA+DQo+ID4gVGhhdCBpcyBhbHJlYWR5IGFjaGlldmFibGUgYnkgdXNpbmcg
dGhlIGNvb2xpbmctZGV2aWNlIHByb3BlcnR5IG9mIGENCj4gPiBjb29saW5nIG1hcC4NCj4gPg0K
PiA+IFBsZWFzZSBoYXZlIGEgbG9vayBpbiB0aGUgZXhhbXBsZSBzZWN0aW9uIG9mIHRoZQ0KPiA+
IERvY3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy90aGVybWFsL3RoZXJtYWwudHh0DQo+
IA0KPiBZZXMsIEkgcmVhZCB0aGlzIGZpbGUuDQo+IFNvIGluIG15IHVuZGVyc3RhbmRpbmc6DQo+
IFRoZXJlIGlzIG5vIG5lZWQgdG8gcHJvdmlkZSAiY29vbGluZy1taW4tbGV2ZWwiIGFuZCAiY29v
bGluZy1tYXgtbGV2ZWwiDQo+IHByb3BlcnR5Lg0KPiBUSEVSTUFMX05PX0xJTUlUIHZhbHVlIGlu
IGNvb2xpbmcgZGV2aWNlIG5vZGUgd2lsbCBpbmRpY2F0ZSB0aGUgZHJpdmVyIHRvDQo+IGF1dG9t
YXRpY2FsbHkgcGFyc2UgdGhlIG1pbiBhbmQgbWF4IHN0YXRlLCByaWdodD8NCj4gDQo+IFRhbGtp
bmcgYWJvdXQgVEhFUk1BTF9OT19MSU1JVCwgSSBuZWVkIHRvICNpbmNsdWRlIDxkdC0NCj4gYmlu
ZGluZ3MvdGhlcm1hbC90aGVybWFsLmg+IHRvIHByb3ZpZGUgdGhlIGRlZmluaXRpb24uIEJ1dCBJ
IGdvdA0KPiBjb21waWxpbmcgZXJyb3Igd2hlbiBidWlsZCBkdGIgZmlsZS4NCj4gSSBkaWQgc29t
ZSByZXNlYXJjaCBhbmQgdXNpbmcgIm1ha2UgdDEwNDBxZHMuZHRiIiBpbiBvcmRlciB0byBpbnZv
bHZlDQo+IHByZXByb2Nlc3Nvci4NCj4gQnV0IHdpdGggc2ltcGx5IGFkZGluZyAiI2luY2x1ZGUg
PGR0LWJpbmRpbmdzL3RoZXJtYWwvdGhlcm1hbC5oPiIgdG8NCj4gdDEwNDBzaS1wb3N0LmR0c2kg
YXQgbGluZSAzNSBJIHN0aWxsIGdvdCBlcnJvciBsaWtlIHRoaXM6DQo+IEVycm9yOiBhcmNoL3Bv
d2VycGMvYm9vdC9kdHMvZnNsL3QxMDQwc2ktcG9zdC5kdHNpOjM1LjEtOSBzeW50YXggZXJyb3IN
Cj4gRkFUQUwgRVJST1I6IFVuYWJsZSB0byBwYXJzZSBpbnB1dCB0cmVlDQo+IA0KPiBDb3VsZCB5
b3UgaGVscCBtZSBvdXQgaGVyZS4NCj4gVGhhbmtzLg0KPiANCj4gPg0KPiA+IExldCBtZSBrbm93
IGlmIHlvdSBuZWVkIGZ1cnRoZXIgY2xhcmlmaWNhdGlvbi4NCj4gPg0KPiA+DQo+ID4gQlIsDQo+
ID4NCj4gPiBFZHVhcmRvIFZhbGVudGluDQo+ID4NCj4gPiA+DQo+ID4gPiBUaGFua3MuDQo+ID4g
PiAtSG9uZ3Rhbw0KPiA+ID4NCj4gPiA+DQo+ID4gPiA+IC0tLS0tT3JpZ2luYWwgTWVzc2FnZS0t
LS0tDQo+ID4gPiA+IEZyb206IEVkdWFyZG8gVmFsZW50aW4gW21haWx0bzplZHViZXp2YWxAZ21h
aWwuY29tXQ0KPiA+ID4gPiBTZW50OiBUaHVyc2RheSwgSnVseSAzMCwgMjAxNSAyOjU2IFBNDQo+
ID4gPiA+IFRvOiBKaWEgSG9uZ3Rhby1CMzg5NTENCj4gPiA+ID4gQ2M6IGxpbnV4LXBtQHZnZXIu
a2VybmVsLm9yZzsgbGludXhwcGMtZGV2QGxpc3RzLm96bGFicy5vcmc7IFdvb2QNCj4gPiA+ID4g
U2NvdHQtDQo+ID4gPiA+IEIwNzQyMQ0KPiA+ID4gPiBTdWJqZWN0OiBSZTogW1BBVENIIFYyXSBR
b3JJUS9UTVU6IGFkZCB0aGVybWFsIG1hbmFnZW1lbnQgc3VwcG9ydA0KPiA+ID4gPiBiYXNlZCBv
biBUTVUNCj4gPiA+ID4NCj4gPiA+ID4gT24gV2VkLCBKdWwgMjksIDIwMTUgYXQgMDI6MTk6MzlQ
TSArMDgwMCwgSmlhIEhvbmd0YW8gd3JvdGU6DQo+ID4gPiA+ID4gSXQgc3VwcG9ydHMgb25lIGNy
aXRpY2FsIHRyaXAgcG9pbnQgYW5kIG9uZSBwYXNzaXZlIHRyaXAgcG9pbnQuDQo+ID4gPiA+ID4g
VGhlIGNwdWZyZXEgaXMgdXNlZCBhcyB0aGUgY29vbGluZyBkZXZpY2UgdG8gdGhyb3R0bGUgQ1BV
cyB3aGVuDQo+ID4gPiA+ID4gdGhlIHBhc3NpdmUgdHJpcCBpcyBjcm9zc2VkLg0KPiA+ID4gPiA+
DQo+ID4gPiA+ID4gU2lnbmVkLW9mZi1ieTogSmlhIEhvbmd0YW8gPGhvbmd0YW8uamlhQGZyZWVz
Y2FsZS5jb20+DQo+ID4gPiA+ID4gLS0tDQo+ID4gPiA+ID4gVGhpcyBwYXRjaCBiYXNlZCBvbjoN
Cj4gPiA+ID4gPiBodHRwOi8vcGF0Y2h3b3JrLm96bGFicy5vcmcvcGF0Y2gvNDgyOTg3Lw0KPiA+
ID4gPiA+DQo+ID4gPiA+ID4gQ2hhbmdlcyBmb3IgVjI6DQo+ID4gPiA+ID4gKiBBZGQgdG11LXJh
bmdlIHBhcnNlLg0KPiA+ID4gPiA+ICogVXNlIGRlZmF1bHQgdHJlbmQgaG9vay4NCj4gPiA+ID4g
PiAqIFVzaW5nIGxhdGVzdCB0aGVybWFsX3pvbmVfYmluZF9jb29saW5nX2RldmljZSBBUEkuDQo+
ID4gPiA+ID4gKiBBZGQgY2FsaWJyYXRpb24gY2hlY2sgZHVyaW5nIGluaXRpYWxpemF0aW9uLg0K
PiA+ID4gPiA+ICogRGlzYWJsZS9lbmFsYmUgZGV2aWNlIHdoZW4gc3VzcGVuZC9yZXN1bWUuDQo+
ID4gPiA+ID4NCj4gPiA+ID4gPiAgZHJpdmVycy90aGVybWFsL0tjb25maWcgICAgICAgICB8ICAx
MSArKw0KPiA+ID4gPiA+ICBkcml2ZXJzL3RoZXJtYWwvTWFrZWZpbGUgICAgICAgIHwgICAxICsN
Cj4gPiA+ID4gPiAgZHJpdmVycy90aGVybWFsL3FvcmlxX3RoZXJtYWwuYyB8IDQwNg0KPiA+ID4g
PiA+ICsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysNCj4gPiA+ID4gPiAg
MyBmaWxlcyBjaGFuZ2VkLCA0MTggaW5zZXJ0aW9ucygrKSAgY3JlYXRlIG1vZGUgMTAwNjQ0DQo+
ID4gPiA+ID4gZHJpdmVycy90aGVybWFsL3FvcmlxX3RoZXJtYWwuYw0KPiA+ID4gPiA+DQo+ID4g
PiA+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvdGhlcm1hbC9LY29uZmlnIGIvZHJpdmVycy90aGVy
bWFsL0tjb25maWcNCj4gPiA+ID4gPiBpbmRleA0KPiA+ID4gPiA+IDExODkzOGUuLmEyMDA3NDUg
MTAwNjQ0DQo+ID4gPiA+ID4gLS0tIGEvZHJpdmVycy90aGVybWFsL0tjb25maWcNCj4gPiA+ID4g
PiArKysgYi9kcml2ZXJzL3RoZXJtYWwvS2NvbmZpZw0KPiA+ID4gPiA+IEBAIC0xODAsNiArMTgw
LDE3IEBAIGNvbmZpZyBJTVhfVEhFUk1BTA0KPiA+ID4gPiA+ICAJICBjcHVmcmVxIGlzIHVzZWQg
YXMgdGhlIGNvb2xpbmcgZGV2aWNlIHRvIHRocm90dGxlIENQVXMgd2hlbg0KPiA+IHRoZQ0KPiA+
ID4gPiA+ICAJICBwYXNzaXZlIHRyaXAgaXMgY3Jvc3NlZC4NCj4gPiA+ID4gPg0KPiA+ID4gPiA+
ICtjb25maWcgUU9SSVFfVEhFUk1BTA0KPiA+ID4gPiA+ICsJdHJpc3RhdGUgIkZyZWVzY2FsZSBR
b3JJUSBUaGVybWFsIE1vbml0b3JpbmcgVW5pdCINCj4gPiA+ID4gPiArCWRlcGVuZHMgb24gQ1BV
X1RIRVJNQUwNCj4gPiA+ID4gPiArCWRlcGVuZHMgb24gT0YNCj4gPiA+ID4gPiArCWRlZmF1bHQg
bg0KPiA+ID4gPiA+ICsJaGVscA0KPiA+ID4gPiA+ICsJICBFbmFibGUgdGhlcm1hbCBtYW5hZ2Vt
ZW50IGJhc2VkIG9uIEZyZWVzY2FsZSBRb3JJUSBUaGVybWFsDQo+ID4gPiA+IE1vbml0b3JpbmcN
Cj4gPiA+ID4gPiArCSAgVW5pdCAoVE1VKS4gSXQgc3VwcG9ydHMgb25lIGNyaXRpY2FsIHRyaXAg
cG9pbnQgYW5kIG9uZQ0KPiA+ID4gPiA+ICtwYXNzaXZlDQo+ID4gPiA+IHRyaXANCj4gPiA+ID4g
PiArCSAgcG9pbnQuIFRoZSBjcHVmcmVxIGlzIHVzZWQgYXMgdGhlIGNvb2xpbmcgZGV2aWNlIHRv
DQo+ID4gdGhyb3R0bGUNCj4gPiA+ID4gPiArQ1BVcw0KPiA+ID4gPiB3aGVuDQo+ID4gPiA+ID4g
KwkgIHRoZSBwYXNzaXZlIHRyaXAgaXMgY3Jvc3NlZC4NCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4g
IGNvbmZpZyBTUEVBUl9USEVSTUFMDQo+ID4gPiA+ID4gIAlib29sICJTUEVBciB0aGVybWFsIHNl
bnNvciBkcml2ZXIiDQo+ID4gPiA+ID4gIAlkZXBlbmRzIG9uIFBMQVRfU1BFQVINCj4gPiA+ID4g
PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy90aGVybWFsL01ha2VmaWxlIGIvZHJpdmVycy90aGVybWFs
L01ha2VmaWxlDQo+ID4gPiA+ID4gaW5kZXgNCj4gPiA+ID4gPiA1MzVkZmVlLi44YzI1ODU5IDEw
MDY0NA0KPiA+ID4gPiA+IC0tLSBhL2RyaXZlcnMvdGhlcm1hbC9NYWtlZmlsZQ0KPiA+ID4gPiA+
ICsrKyBiL2RyaXZlcnMvdGhlcm1hbC9NYWtlZmlsZQ0KPiA+ID4gPiA+IEBAIC0zMyw2ICszMyw3
IEBAIG9iai0kKENPTkZJR19ET1ZFX1RIRVJNQUwpICAJKz0NCj4gZG92ZV90aGVybWFsLm8NCj4g
PiA+ID4gPiAgb2JqLSQoQ09ORklHX0RCODUwMF9USEVSTUFMKQkrPSBkYjg1MDBfdGhlcm1hbC5v
DQo+ID4gPiA+ID4gIG9iai0kKENPTkZJR19BUk1BREFfVEhFUk1BTCkJKz0gYXJtYWRhX3RoZXJt
YWwubw0KPiA+ID4gPiA+ICBvYmotJChDT05GSUdfSU1YX1RIRVJNQUwpCSs9IGlteF90aGVybWFs
Lm8NCj4gPiA+ID4gPiArb2JqLSQoQ09ORklHX1FPUklRX1RIRVJNQUwpCSs9IHFvcmlxX3RoZXJt
YWwubw0KPiA+ID4gPiA+ICBvYmotJChDT05GSUdfREI4NTAwX0NQVUZSRVFfQ09PTElORykJKz0N
Cj4gPiBkYjg1MDBfY3B1ZnJlcV9jb29saW5nLm8NCj4gPiA+ID4gPiAgb2JqLSQoQ09ORklHX0lO
VEVMX1BPV0VSQ0xBTVApCSs9IGludGVsX3Bvd2VyY2xhbXAubw0KPiA+ID4gPiA+ICBvYmotJChD
T05GSUdfWDg2X1BLR19URU1QX1RIRVJNQUwpCSs9IHg4Nl9wa2dfdGVtcF90aGVybWFsLm8NCj4g
PiA+ID4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy90aGVybWFsL3FvcmlxX3RoZXJtYWwuYw0KPiA+
ID4gPiA+IGIvZHJpdmVycy90aGVybWFsL3FvcmlxX3RoZXJtYWwuYyBuZXcgZmlsZSBtb2RlIDEw
MDY0NCBpbmRleA0KPiA+ID4gPiA+IDAwMDAwMDAuLjA2OTRmNDINCj4gPiA+ID4gPiAtLS0gL2Rl
di9udWxsDQo+ID4gPiA+ID4gKysrIGIvZHJpdmVycy90aGVybWFsL3FvcmlxX3RoZXJtYWwuYw0K
PiA+ID4gPiA+IEBAIC0wLDAgKzEsNDA2IEBADQo+ID4gPiA+ID4gKy8qDQo+ID4gPiA+ID4gKyAq
IENvcHlyaWdodCAyMDE1IEZyZWVzY2FsZSBTZW1pY29uZHVjdG9yLCBJbmMuDQo+ID4gPiA+ID4g
KyAqDQo+ID4gPiA+ID4gKyAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2Fu
IHJlZGlzdHJpYnV0ZSBpdA0KPiA+ID4gPiA+ICthbmQvb3IgbW9kaWZ5IGl0DQo+ID4gPiA+ID4g
KyAqIHVuZGVyIHRoZSB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB0aGUgR05VIEdlbmVyYWwgUHVi
bGljDQo+ID4gPiA+ID4gK0xpY2Vuc2UsDQo+ID4gPiA+ID4gKyAqIHZlcnNpb24gMiwgYXMgcHVi
bGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24uDQo+ID4gPiA+ID4gKyAqDQo+
ID4gPiA+ID4gKyAqIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSBpdCB3
aWxsIGJlIHVzZWZ1bCwNCj4gPiA+ID4gPiArYnV0IFdJVEhPVVQNCj4gPiA+ID4gPiArICogQU5Z
IFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YNCj4gPiA+ID4g
PiArTUVSQ0hBTlRBQklMSVRZIG9yDQo+ID4gPiA+ID4gKyAqIEZJVE5FU1MgRk9SIEEgUEFSVElD
VUxBUiBQVVJQT1NFLiAgU2VlIHRoZSBHTlUgR2VuZXJhbA0KPiA+ID4gPiA+ICtQdWJsaWMgTGlj
ZW5zZSBmb3INCj4gPiA+ID4gPiArICogbW9yZSBkZXRhaWxzLg0KPiA+ID4gPiA+ICsgKg0KPiA+
ID4gPiA+ICsgKi8NCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKy8qDQo+ID4gPiA+ID4gKyAqIEJh
c2VkIG9uIEZyZWVzY2FsZSBRb3JJUSBUaGVybWFsIE1vbml0b3JpbmcgVW5pdCAoVE1VKSAgKi8N
Cj4gPiA+ID4gPiArI2luY2x1ZGUgPGxpbnV4L2NwdWZyZXEuaD4gI2luY2x1ZGUgPGxpbnV4L2Nw
dV9jb29saW5nLmg+DQo+ID4gPiA+ID4gKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4gI2luY2x1
ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPg0KPiA+ID4gPiA+ICsjaW5jbHVkZSA8bGludXgv
ZXJyLmg+ICNpbmNsdWRlIDxsaW51eC9pby5oPiAjaW5jbHVkZQ0KPiA+ID4gPiA+ICs8bGludXgv
b2YuaD4gI2luY2x1ZGUgPGxpbnV4L29mX2FkZHJlc3MuaD4gI2luY2x1ZGUNCj4gPiA+ID4gPiAr
PGxpbnV4L3RoZXJtYWwuaD4NCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKyNkZWZpbmUgU0lURVNf
TUFYCQkxNg0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArI2RlZmluZSBUTVVfVEVNUF9QQVNTSVZF
CTg1MDAwDQo+ID4gPiA+ID4gKyNkZWZpbmUgVE1VX1RFTVBfQ1JJVElDQUwJOTUwMDANCj4gPiA+
ID4gPiArDQo+ID4gPiA+ID4gKyNkZWZpbmUgVE1VX1BBU1NJVkVfREVMQVkJMTAwMAkvKiBNaWxs
aXNlY29uZHMgKi8NCj4gPiA+ID4gPiArI2RlZmluZSBUTVVfUE9MTElOR19ERUxBWQk1MDAwDQo+
ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsvKiBUaGUgZHJpdmVyIHN1cHBvcnRzIDEgcGFzc2l2ZSB0
cmlwIHBvaW50IGFuZCAxIGNyaXRpY2FsIHRyaXANCj4gPiA+ID4gPiArcG9pbnQgKi8gZW51bSB0
bXVfdGhlcm1hbF90cmlwIHsNCj4gPiA+ID4gPiArCVRNVV9UUklQX1BBU1NJVkUsDQo+ID4gPiA+
ID4gKwlUTVVfVFJJUF9DUklUSUNBTCwNCj4gPiA+ID4gPiArCVRNVV9UUklQX05VTSwNCj4gPiA+
ID4gPiArfTsNCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKy8qDQo+ID4gPiA+ID4gKyAqIFFvcklR
IFRNVSBSZWdpc3RlcnMNCj4gPiA+ID4gPiArICovDQo+ID4gPiA+ID4gK3N0cnVjdCBxb3JpcV90
bXVfc2l0ZV9yZWdzIHsNCj4gPiA+ID4gPiArCV9fYmUzMiB0cml0c3I7CQkvKiBJbW1lZGlhdGUg
VGVtcGVyYXR1cmUgU2l0ZQ0KPiA+IFJlZ2lzdGVyICovDQo+ID4gPiA+ID4gKwlfX2JlMzIgdHJh
dHNyOwkJLyogQXZlcmFnZSBUZW1wZXJhdHVyZSBTaXRlIFJlZ2lzdGVyDQo+ID4gKi8NCj4gPiA+
ID4gPiArCXU4IHJlczBbMHg4XTsNCj4gPiA+ID4gPiArfSBfX3BhY2tlZDsNCj4gPiA+ID4gPiAr
DQo+ID4gPiA+ID4gK3N0cnVjdCBxb3JpcV90bXVfcmVncyB7DQo+ID4gPiA+ID4gKwlfX2JlMzIg
dG1yOwkJLyogTW9kZSBSZWdpc3RlciAqLw0KPiA+ID4gPiA+ICsjZGVmaW5lIFRNUl9ESVNBQkxF
CTB4MA0KPiA+ID4gPiA+ICsjZGVmaW5lIFRNUl9NRQkJMHg4MDAwMDAwMA0KPiA+ID4gPiA+ICsj
ZGVmaW5lIFRNUl9BTFBGCTB4MGMwMDAwMDANCj4gPiA+ID4gPiArI2RlZmluZSBUTVJfTVNJVEUJ
MHgwMDAwODAwMA0KPiA+ID4gPiA+ICsjZGVmaW5lIFRNUl9BTEwJCShUTVJfTUUgfCBUTVJfQUxQ
RiB8IFRNUl9NU0lURSkNCj4gPiA+ID4gPiArCV9fYmUzMiB0c3I7CQkvKiBTdGF0dXMgUmVnaXN0
ZXIgKi8NCj4gPiA+ID4gPiArCV9fYmUzMiB0bXRtaXI7CQkvKiBUZW1wZXJhdHVyZSBtZWFzdXJl
bWVudCBpbnRlcnZhbA0KPiA+ID4gPiBSZWdpc3RlciAqLw0KPiA+ID4gPiA+ICsjZGVmaW5lIFRN
VE1JUl9ERUZBVUxUCTB4MDAwMDAwMDcNCj4gPiA+ID4gPiArCXU4IHJlczBbMHgxNF07DQo+ID4g
PiA+ID4gKwlfX2JlMzIgdGllcjsJCS8qIEludGVycnVwdCBFbmFibGUgUmVnaXN0ZXIgKi8NCj4g
PiA+ID4gPiArI2RlZmluZSBUSUVSX0RJU0FCTEUJMHgwDQo+ID4gPiA+ID4gKwlfX2JlMzIgdGlk
cjsJCS8qIEludGVycnVwdCBEZXRlY3QgUmVnaXN0ZXIgKi8NCj4gPiA+ID4gPiArCV9fYmUzMiB0
aXNjcjsJCS8qIEludGVycnVwdCBTaXRlIENhcHR1cmUgUmVnaXN0ZXIgKi8NCj4gPiA+ID4gPiAr
CV9fYmUzMiB0aWNzY3I7CQkvKiBJbnRlcnJ1cHQgQ3JpdGljYWwgU2l0ZSBDYXB0dXJlDQo+ID4g
UmVnaXN0ZXINCj4gPiA+ID4gKi8NCj4gPiA+ID4gPiArCXU4IHJlczFbMHgxMF07DQo+ID4gPiA+
ID4gKwlfX2JlMzIgdG1odGNyaDsJCS8qIEhpZ2ggVGVtcGVyYXR1cmUgQ2FwdHVyZSBSZWdpc3Rl
cg0KPiA+ICovDQo+ID4gPiA+ID4gKwlfX2JlMzIgdG1odGNybDsJCS8qIExvdyBUZW1wZXJhdHVy
ZSBDYXB0dXJlIFJlZ2lzdGVyDQo+ID4gKi8NCj4gPiA+ID4gPiArCXU4IHJlczJbMHg4XTsNCj4g
PiA+ID4gPiArCV9fYmUzMiB0bWh0aXRyOwkJLyogSGlnaCBUZW1wZXJhdHVyZSBJbW1lZGlhdGUN
Cj4gPiBUaHJlc2hvbGQgKi8NCj4gPiA+ID4gPiArCV9fYmUzMiB0bWh0YXRyOwkJLyogSGlnaCBU
ZW1wZXJhdHVyZSBBdmVyYWdlIFRocmVzaG9sZA0KPiA+ICovDQo+ID4gPiA+ID4gKwlfX2JlMzIg
dG1odGFjdHI7CS8qIEhpZ2ggVGVtcGVyYXR1cmUgQXZlcmFnZSBDcml0IFRocmVzaG9sZA0KPiA+
ICovDQo+ID4gPiA+ID4gKwl1OCByZXMzWzB4MjRdOw0KPiA+ID4gPiA+ICsJX19iZTMyIHR0Y2Zn
cjsJCS8qIFRlbXBlcmF0dXJlIENvbmZpZ3VyYXRpb24gUmVnaXN0ZXINCj4gPiAqLw0KPiA+ID4g
PiA+ICsJX19iZTMyIHRzY2ZncjsJCS8qIFNlbnNvciBDb25maWd1cmF0aW9uIFJlZ2lzdGVyICov
DQo+ID4gPiA+ID4gKwl1OCByZXM0WzB4NzhdOw0KPiA+ID4gPiA+ICsJc3RydWN0IHFvcmlxX3Rt
dV9zaXRlX3JlZ3Mgc2l0ZVtTSVRFU19NQVhdOw0KPiA+ID4gPiA+ICsJdTggcmVzNVsweDlmOF07
DQo+ID4gPiA+ID4gKwlfX2JlMzIgaXBicnIwOwkJLyogSVAgQmxvY2sgUmV2aXNpb24gUmVnaXN0
ZXIgMCAqLw0KPiA+ID4gPiA+ICsJX19iZTMyIGlwYnJyMTsJCS8qIElQIEJsb2NrIFJldmlzaW9u
IFJlZ2lzdGVyIDEgKi8NCj4gPiA+ID4gPiArCXU4IHJlczZbMHgzMTBdOw0KPiA+ID4gPiA+ICsJ
X19iZTMyIHR0cjBjcjsJCS8qIFRlbXBlcmF0dXJlIFJhbmdlIDAgQ29udHJvbA0KPiA+IFJlZ2lz
dGVyICovDQo+ID4gPiA+ID4gKwlfX2JlMzIgdHRyMWNyOwkJLyogVGVtcGVyYXR1cmUgUmFuZ2Ug
MSBDb250cm9sDQo+ID4gUmVnaXN0ZXIgKi8NCj4gPiA+ID4gPiArCV9fYmUzMiB0dHIyY3I7CQkv
KiBUZW1wZXJhdHVyZSBSYW5nZSAyIENvbnRyb2wNCj4gPiBSZWdpc3RlciAqLw0KPiA+ID4gPiA+
ICsJX19iZTMyIHR0cjNjcjsJCS8qIFRlbXBlcmF0dXJlIFJhbmdlIDMgQ29udHJvbA0KPiA+IFJl
Z2lzdGVyICovDQo+ID4gPiA+ID4gK307DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsvKg0KPiA+
ID4gPiA+ICsgKiBUaGVybWFsIHpvbmUgZGF0YQ0KPiA+ID4gPiA+ICsgKi8NCj4gPiA+ID4gPiAr
c3RydWN0IHFvcmlxX3RtdV9kYXRhIHsNCj4gPiA+ID4gPiArCXN0cnVjdCB0aGVybWFsX3pvbmVf
ZGV2aWNlICp0ejsNCj4gPiA+ID4gPiArCXN0cnVjdCB0aGVybWFsX2Nvb2xpbmdfZGV2aWNlICpj
ZGV2Ow0KPiA+ID4gPiA+ICsJZW51bSB0aGVybWFsX2RldmljZV9tb2RlIG1vZGU7DQo+ID4gPiA+
ID4gKwl1bnNpZ25lZCBsb25nIHRlbXBfcGFzc2l2ZTsNCj4gPiA+ID4gPiArCXVuc2lnbmVkIGxv
bmcgdGVtcF9jcml0aWNhbDsNCj4gPiA+ID4gPiArCXN0cnVjdCBxb3JpcV90bXVfcmVncyBfX2lv
bWVtICpyZWdzOyB9Ow0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArc3RhdGljIGludCB0bXVfZ2V0
X21vZGUoc3RydWN0IHRoZXJtYWxfem9uZV9kZXZpY2UgKnR6LA0KPiA+ID4gPiA+ICsJCQllbnVt
IHRoZXJtYWxfZGV2aWNlX21vZGUgKm1vZGUpIHsNCj4gPiA+ID4gPiArCXN0cnVjdCBxb3JpcV90
bXVfZGF0YSAqZGF0YSA9IHR6LT5kZXZkYXRhOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCSpt
b2RlID0gZGF0YS0+bW9kZTsNCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKwlyZXR1cm4gMDsNCj4g
PiA+ID4gPiArfQ0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArc3RhdGljIGludCB0bXVfc2V0X21v
ZGUoc3RydWN0IHRoZXJtYWxfem9uZV9kZXZpY2UgKnR6LA0KPiA+ID4gPiA+ICsJCQllbnVtIHRo
ZXJtYWxfZGV2aWNlX21vZGUgbW9kZSkgew0KPiA+ID4gPiA+ICsJc3RydWN0IHFvcmlxX3RtdV9k
YXRhICpkYXRhID0gdHotPmRldmRhdGE7DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJaWYgKG1v
ZGUgPT0gVEhFUk1BTF9ERVZJQ0VfRU5BQkxFRCkgew0KPiA+ID4gPiA+ICsJCXR6LT5wb2xsaW5n
X2RlbGF5ID0gVE1VX1BPTExJTkdfREVMQVk7DQo+ID4gPiA+ID4gKwkJdHotPnBhc3NpdmVfZGVs
YXkgPSBUTVVfUEFTU0lWRV9ERUxBWTsNCj4gPiA+ID4gPiArCQl0aGVybWFsX3pvbmVfZGV2aWNl
X3VwZGF0ZSh0eik7DQo+ID4gPiA+ID4gKwl9IGVsc2Ugew0KPiA+ID4gPiA+ICsJCXR6LT5wb2xs
aW5nX2RlbGF5ID0gMDsNCj4gPiA+ID4gPiArCQl0ei0+cGFzc2l2ZV9kZWxheSA9IDA7DQo+ID4g
PiA+ID4gKwl9DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJZGF0YS0+bW9kZSA9IG1vZGU7DQo+
ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJcmV0dXJuIDA7DQo+ID4gPiA+ID4gK30NCj4gPiA+ID4g
PiArDQo+ID4gPiA+ID4gK3N0YXRpYyBpbnQgdG11X2dldF90ZW1wKHN0cnVjdCB0aGVybWFsX3pv
bmVfZGV2aWNlICp0eiwNCj4gPiA+ID4gPiArdW5zaWduZWQgbG9uZw0KPiA+ID4gPiA+ICsqdGVt
cCkgew0KPiA+ID4gPiA+ICsJdTggdmFsOw0KPiA+ID4gPiA+ICsJc3RydWN0IHFvcmlxX3RtdV9k
YXRhICpkYXRhID0gdHotPmRldmRhdGE7DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJdmFsID0g
aW9yZWFkMzJiZSgmZGF0YS0+cmVncy0+c2l0ZVswXS50cml0c3IpOw0KPiA+ID4gPiA+ICsJKnRl
bXAgPSAodW5zaWduZWQgbG9uZyl2YWwgKiAxMDAwOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiAr
CXJldHVybiAwOw0KPiA+ID4gPiA+ICt9DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICtzdGF0aWMg
aW50IHRtdV9nZXRfdHJpcF90eXBlKHN0cnVjdCB0aGVybWFsX3pvbmVfZGV2aWNlICp0eiwNCj4g
PiA+ID4gPiAraW50DQo+ID4gdHJpcCwNCj4gPiA+ID4gPiArCQkJICAgICBlbnVtIHRoZXJtYWxf
dHJpcF90eXBlICp0eXBlKSB7DQo+ID4gPiA+ID4gKwkqdHlwZSA9ICh0cmlwID09IFRNVV9UUklQ
X1BBU1NJVkUpID8gVEhFUk1BTF9UUklQX1BBU1NJVkUgOg0KPiA+ID4gPiA+ICsJCQkJCSAgICAg
VEhFUk1BTF9UUklQX0NSSVRJQ0FMOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCXJldHVybiAw
Ow0KPiA+ID4gPiA+ICt9DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICtzdGF0aWMgaW50IHRtdV9n
ZXRfdHJpcF90ZW1wKHN0cnVjdCB0aGVybWFsX3pvbmVfZGV2aWNlICp0eiwNCj4gPiA+ID4gPiAr
aW50DQo+ID4gdHJpcCwNCj4gPiA+ID4gPiArCQkJICAgICB1bnNpZ25lZCBsb25nICp0ZW1wKQ0K
PiA+ID4gPiA+ICt7DQo+ID4gPiA+ID4gKwlzdHJ1Y3QgcW9yaXFfdG11X2RhdGEgKmRhdGEgPSB0
ei0+ZGV2ZGF0YTsNCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKwkqdGVtcCA9ICh0cmlwID09IFRN
VV9UUklQX1BBU1NJVkUpID8gZGF0YS0+dGVtcF9wYXNzaXZlIDoNCj4gPiA+ID4gPiArCQkJCQkg
ICAgIGRhdGEtPnRlbXBfY3JpdGljYWw7DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJcmV0dXJu
IDA7DQo+ID4gPiA+ID4gK30NCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gK3N0YXRpYyBpbnQgdG11
X2dldF9jcml0X3RlbXAoc3RydWN0IHRoZXJtYWxfem9uZV9kZXZpY2UgKnR6LA0KPiA+ID4gPiA+
ICsJCQkgICAgIHVuc2lnbmVkIGxvbmcgKnRlbXApDQo+ID4gPiA+ID4gK3sNCj4gPiA+ID4gPiAr
CXN0cnVjdCBxb3JpcV90bXVfZGF0YSAqZGF0YSA9IHR6LT5kZXZkYXRhOw0KPiA+ID4gPiA+ICsN
Cj4gPiA+ID4gPiArCSp0ZW1wID0gZGF0YS0+dGVtcF9jcml0aWNhbDsNCj4gPiA+ID4gPiArDQo+
ID4gPiA+ID4gKwlyZXR1cm4gMDsNCj4gPiA+ID4gPiArfQ0KPiA+ID4gPiA+ICsNCj4gPiA+ID4g
PiArc3RhdGljIGludCB0bXVfYmluZChzdHJ1Y3QgdGhlcm1hbF96b25lX2RldmljZSAqdHosDQo+
ID4gPiA+ID4gKwkJICAgIHN0cnVjdCB0aGVybWFsX2Nvb2xpbmdfZGV2aWNlICpjZGV2KSB7DQo+
ID4gPiA+ID4gKwlpbnQgcmV0Ow0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCXJldCA9IHRoZXJt
YWxfem9uZV9iaW5kX2Nvb2xpbmdfZGV2aWNlKHR6LCBUTVVfVFJJUF9QQVNTSVZFLA0KPiA+IGNk
ZXYsDQo+ID4gPiA+ID4gKwkJCQkJICAgICAgIFRIRVJNQUxfTk9fTElNSVQsDQo+ID4gPiA+ID4g
KwkJCQkJICAgICAgIFRIRVJNQUxfTk9fTElNSVQsDQo+ID4gPiA+ID4gKwkJCQkJICAgICAgIFRI
RVJNQUxfV0VJR0hUX0RFRkFVTFQpOw0KPiA+ID4gPiA+ICsJaWYgKHJldCkgew0KPiA+ID4gPiA+
ICsJCWRldl9lcnIoJnR6LT5kZXZpY2UsDQo+ID4gPiA+ID4gKwkJCSJCaW5kaW5nIHpvbmUgJXMg
d2l0aCBjZGV2ICVzIGZhaWxlZDolZFxuIiwNCj4gPiA+ID4gPiArCQkJdHotPnR5cGUsIGNkZXYt
PnR5cGUsIHJldCk7DQo+ID4gPiA+ID4gKwl9DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJcmV0
dXJuIHJldDsNCj4gPiA+ID4gPiArfQ0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArc3RhdGljIGlu
dCB0bXVfdW5iaW5kKHN0cnVjdCB0aGVybWFsX3pvbmVfZGV2aWNlICp0eiwNCj4gPiA+ID4gPiAr
CQkgICAgICBzdHJ1Y3QgdGhlcm1hbF9jb29saW5nX2RldmljZSAqY2Rldikgew0KPiA+ID4gPiA+
ICsJaW50IHJldDsNCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKwlyZXQgPSB0aGVybWFsX3pvbmVf
dW5iaW5kX2Nvb2xpbmdfZGV2aWNlKHR6LCBUTVVfVFJJUF9QQVNTSVZFLA0KPiA+ID4gPiBjZGV2
KTsNCj4gPiA+ID4gPiArCWlmIChyZXQpIHsNCj4gPiA+ID4gPiArCQlkZXZfZXJyKCZ0ei0+ZGV2
aWNlLA0KPiA+ID4gPiA+ICsJCQkiVW5iaW5kaW5nIHpvbmUgJXMgd2l0aCBjZGV2ICVzIGZhaWxl
ZDolZFxuIiwNCj4gPiA+ID4gPiArCQkJdHotPnR5cGUsIGNkZXYtPnR5cGUsIHJldCk7DQo+ID4g
PiA+ID4gKwl9DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJcmV0dXJuIHJldDsNCj4gPiA+ID4g
PiArfQ0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArc3RhdGljIGludCBxb3JpcV90bXVfY2FsaWJy
YXRpb24oc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikgew0KPiA+ID4gPiA+ICsJaW50IGks
IHZhbCwgbGVuOw0KPiA+ID4gPiA+ICsJdTMyIHJhbmdlWzRdOw0KPiA+ID4gPiA+ICsJY29uc3Qg
X19iZTMyICpjYWxpYnJhdGlvbjsNCj4gPiA+ID4gPiArCXN0cnVjdCBkZXZpY2Vfbm9kZSAqbm9k
ZSA9IHBkZXYtPmRldi5vZl9ub2RlOw0KPiA+ID4gPiA+ICsJc3RydWN0IHFvcmlxX3RtdV9kYXRh
ICpkYXRhID0gZGV2X2dldF9kcnZkYXRhKCZwZGV2LT5kZXYpOw0KPiA+ID4gPiA+ICsNCj4gPiA+
ID4gPiArCS8qIERpc2FibGUgbW9uaXRvcmluZyBiZWZvcmUgY2FsaWJyYXRpb24gKi8NCj4gPiA+
ID4gPiArCWlvd3JpdGUzMmJlKFRNUl9ESVNBQkxFLCAmZGF0YS0+cmVncy0+dG1yKTsNCj4gPiA+
ID4gPiArDQo+ID4gPiA+ID4gKwlpZiAob2ZfcHJvcGVydHlfcmVhZF91MzJfYXJyYXkobm9kZSwg
ImZzbCx0bXUtcmFuZ2UiLCByYW5nZSwNCj4gPiA0KSkNCj4gPiA+ID4gPiArCQlyZXR1cm4gLTE7
DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJLyogSW5pdCB0ZW1wZXJhdHVyZSByYW5nZSByZWdp
c3RlcnMgKi8NCj4gPiA+ID4gPiArCWlvd3JpdGUzMmJlKHJhbmdlWzBdLCAmZGF0YS0+cmVncy0+
dHRyMGNyKTsNCj4gPiA+ID4gPiArCWlvd3JpdGUzMmJlKHJhbmdlWzFdLCAmZGF0YS0+cmVncy0+
dHRyMWNyKTsNCj4gPiA+ID4gPiArCWlvd3JpdGUzMmJlKHJhbmdlWzJdLCAmZGF0YS0+cmVncy0+
dHRyMmNyKTsNCj4gPiA+ID4gPiArCWlvd3JpdGUzMmJlKHJhbmdlWzNdLCAmZGF0YS0+cmVncy0+
dHRyM2NyKTsNCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKwljYWxpYnJhdGlvbiA9IG9mX2dldF9w
cm9wZXJ0eShub2RlLCAiZnNsLHRtdS1jYWxpYnJhdGlvbiIsDQo+ID4gJmxlbik7DQo+ID4gPiA+
ID4gKwlpZiAoY2FsaWJyYXRpb24gPT0gTlVMTCkNCj4gPiA+ID4gPiArCQlyZXR1cm4gLTE7DQo+
ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJZm9yIChpID0gMDsgaSA8IGxlbjsgaSArPSA4LCBjYWxp
YnJhdGlvbiArPSAyKSB7DQo+ID4gPiA+ID4gKwkJdmFsID0gKGludClvZl9yZWFkX251bWJlcihj
YWxpYnJhdGlvbiwgMSk7DQo+ID4gPiA+ID4gKwkJaW93cml0ZTMyYmUodmFsLCAmZGF0YS0+cmVn
cy0+dHRjZmdyKTsNCj4gPiA+ID4gPiArCQl2YWwgPSAoaW50KW9mX3JlYWRfbnVtYmVyKGNhbGli
cmF0aW9uICsgMSwgMSk7DQo+ID4gPiA+ID4gKwkJaW93cml0ZTMyYmUodmFsLCAmZGF0YS0+cmVn
cy0+dHNjZmdyKTsNCj4gPiA+ID4gPiArCX0NCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKwlyZXR1
cm4gMDsNCj4gPiA+ID4gPiArfQ0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArc3RhdGljIHZvaWQg
cW9yaXFfdG11X2luaXRfZGV2aWNlKHN0cnVjdCBxb3JpcV90bXVfZGF0YSAqZGF0YSkgew0KPiA+
ID4gPiA+ICsJLyogRGlzYWJsZSBpbnRlcnJ1cHQsIHVzaW5nIHBvbGxpbmcgaW5zdGVhZCAqLw0K
PiA+ID4gPiA+ICsJaW93cml0ZTMyYmUoVElFUl9ESVNBQkxFLCAmZGF0YS0+cmVncy0+dGllcik7
DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJLyogU2V0IHVwZGF0ZV9pbnRlcnZhbCAqLw0KPiA+
ID4gPiA+ICsJaW93cml0ZTMyYmUoVE1UTUlSX0RFRkFVTFQsICZkYXRhLT5yZWdzLT50bXRtaXIp
Ow0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCS8qIEVuYWJsZSBtb25pdG9yaW5nICovDQo+ID4g
PiA+ID4gKwlpb3dyaXRlMzJiZShUTVJfQUxMLCAmZGF0YS0+cmVncy0+dG1yKTsgfQ0KPiA+ID4g
PiA+ICsNCj4gPiA+ID4gPiArc3RhdGljIHN0cnVjdCB0aGVybWFsX3pvbmVfZGV2aWNlX29wcyB0
bXVfdHpfb3BzID0gew0KPiA+ID4gPiA+ICsJLmJpbmQgPSB0bXVfYmluZCwNCj4gPiA+ID4gPiAr
CS51bmJpbmQgPSB0bXVfdW5iaW5kLA0KPiA+ID4gPiA+ICsJLmdldF90ZW1wID0gdG11X2dldF90
ZW1wLA0KPiA+ID4gPiA+ICsJLmdldF9tb2RlID0gdG11X2dldF9tb2RlLA0KPiA+ID4gPiA+ICsJ
LnNldF9tb2RlID0gdG11X3NldF9tb2RlLA0KPiA+ID4gPiA+ICsJLmdldF90cmlwX3R5cGUgPSB0
bXVfZ2V0X3RyaXBfdHlwZSwNCj4gPiA+ID4gPiArCS5nZXRfdHJpcF90ZW1wID0gdG11X2dldF90
cmlwX3RlbXAsDQo+ID4gPiA+ID4gKwkuZ2V0X2NyaXRfdGVtcCA9IHRtdV9nZXRfY3JpdF90ZW1w
LCB9Ow0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArc3RhdGljIGludCBxb3JpcV90bXVfcHJvYmUo
c3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikgew0KPiA+ID4gPiA+ICsJaW50IHJldDsNCj4g
PiA+ID4gPiArCXN0cnVjdCBjcHVtYXNrIGNsaXBfY3B1czsNCj4gPiA+ID4gPiArCXN0cnVjdCBx
b3JpcV90bXVfZGF0YSAqZGF0YTsNCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gKwlpZiAoIWNwdWZy
ZXFfZ2V0X2N1cnJlbnRfZHJpdmVyKCkpIHsNCj4gPiA+ID4gPiArCQlkZXZfZGJnKCZwZGV2LT5k
ZXYsICJObyBjcHVmcmVxIGRyaXZlciB5ZXRcbiIpOw0KPiA+ID4gPiA+ICsJCXJldHVybiAtRVBS
T0JFX0RFRkVSOw0KPiA+ID4gPiA+ICsJfQ0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCWlmICgh
cGRldi0+ZGV2Lm9mX25vZGUpIHsNCj4gPiA+ID4gPiArCQlkZXZfZXJyKCZwZGV2LT5kZXYsICJE
ZXZpY2UgT0YtTm9kZSBpcyBOVUxMIik7DQo+ID4gPiA+ID4gKwkJcmV0dXJuIC1FRkFVTFQ7DQo+
ID4gPiA+ID4gKwl9DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJZGF0YSA9IGRldm1fa3phbGxv
YygmcGRldi0+ZGV2LCBzaXplb2Yoc3RydWN0IHFvcmlxX3RtdV9kYXRhKSwNCj4gPiA+ID4gPiAr
CQkJICAgIEdGUF9LRVJORUwpOw0KPiA+ID4gPiA+ICsJaWYgKCFkYXRhKQ0KPiA+ID4gPiA+ICsJ
CXJldHVybiAtRU5PTUVNOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCWRldl9zZXRfZHJ2ZGF0
YSgmcGRldi0+ZGV2LCBkYXRhKTsNCj4gPiA+ID4gPiArCWRhdGEtPnJlZ3MgPSBvZl9pb21hcChw
ZGV2LT5kZXYub2Zfbm9kZSwgMCk7DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJaWYgKCFkYXRh
LT5yZWdzKSB7DQo+ID4gPiA+ID4gKwkJZGV2X2VycigmcGRldi0+ZGV2LCAiRmFpbGVkIHRvIGdl
dCBtZW1vcnkgcmVnaW9uXG4iKTsNCj4gPiA+ID4gPiArCQlyZXQgPSAtRU5PREVWOw0KPiA+ID4g
PiA+ICsJCWdvdG8gZXJyX2lvbWFwOw0KPiA+ID4gPiA+ICsJfQ0KPiA+ID4gPiA+ICsNCj4gPiA+
ID4gPiArCXJldCA9IHFvcmlxX3RtdV9jYWxpYnJhdGlvbihwZGV2KTsJLyogVE1VIGNhbGlicmF0
aW9uICovDQo+ID4gPiA+ID4gKwlpZiAocmV0IDwgMCkgew0KPiA+ID4gPiA+ICsJCWRldl9lcnIo
JnBkZXYtPmRldiwgIlRNVSBjYWxpYnJhdGlvbiBmYWlsZWQuXG4iKTsNCj4gPiA+ID4gPiArCQly
ZXQgPSAtRU5PREVWOw0KPiA+ID4gPiA+ICsJCWdvdG8gZXJyX2lvbWFwOw0KPiA+ID4gPiA+ICsJ
fQ0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCXFvcmlxX3RtdV9pbml0X2RldmljZShkYXRhKTsJ
LyogVE1VIGluaXRpYWxpemF0aW9uICovDQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJY3B1bWFz
a19zZXRhbGwoJmNsaXBfY3B1cyk7DQo+ID4gPiA+ID4gKwlkYXRhLT5jZGV2ID0gY3B1ZnJlcV9j
b29saW5nX3JlZ2lzdGVyKCZjbGlwX2NwdXMpOw0KPiA+ID4gPiA+ICsJaWYgKElTX0VSUihkYXRh
LT5jZGV2KSkgew0KPiA+ID4gPiA+ICsJCXJldCA9IFBUUl9FUlIoZGF0YS0+Y2Rldik7DQo+ID4g
PiA+ID4gKwkJZGV2X2VycigmZGF0YS0+Y2Rldi0+ZGV2aWNlLA0KPiA+ID4gPiA+ICsJCQkiRmFp
bGVkIHRvIHJlZ2lzdGVyIGNwdWZyZXEgY29vbGluZyBkZXZpY2U6ICVkXG4iLA0KPiA+IHJldCk7
DQo+ID4gPiA+ID4gKwkJZ290byBlcnJfY29vbGluZzsNCj4gPiA+ID4gPiArCX0NCj4gPiA+ID4g
PiArDQo+ID4gPiA+ID4gKwlkYXRhLT50ZW1wX3Bhc3NpdmUgPSBUTVVfVEVNUF9QQVNTSVZFOw0K
PiA+ID4gPiA+ICsJZGF0YS0+dGVtcF9jcml0aWNhbCA9IFRNVV9URU1QX0NSSVRJQ0FMOw0KPiA+
ID4gPiA+ICsJZGF0YS0+dHogPSB0aGVybWFsX3pvbmVfZGV2aWNlX3JlZ2lzdGVyKCJ0bXVfdGhl
cm1hbF96b25lIiwNCj4gPiA+ID4NCj4gPiA+ID4gQW55IHNwZWNpZmljIHJlYXNvbiB3aHkgbm90
IHVzaW5nIE9GIHRoZXJtYWw/DQo+ID4gPiA+DQo+ID4gPiA+ID4gKwkJCQkJCVRNVV9UUklQX05V
TSwNCj4gPiA+ID4gPiArCQkJCQkJMCwgZGF0YSwNCj4gPiA+ID4gPiArCQkJCQkJJnRtdV90el9v
cHMsIE5VTEwsDQo+ID4gPiA+ID4gKwkJCQkJCVRNVV9QQVNTSVZFX0RFTEFZLA0KPiA+ID4gPiA+
ICsJCQkJCQlUTVVfUE9MTElOR19ERUxBWSk7DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJaWYg
KElTX0VSUihkYXRhLT50eikpIHsNCj4gPiA+ID4gPiArCQlyZXQgPSBQVFJfRVJSKGRhdGEtPnR6
KTsNCj4gPiA+ID4gPiArCQlkZXZfZXJyKCZwZGV2LT5kZXYsDQo+ID4gPiA+ID4gKwkJCSJGYWls
ZWQgdG8gcmVnaXN0ZXIgdGhlcm1hbCB6b25lIGRldmljZSAlZFxuIiwNCj4gPiByZXQpOw0KPiA+
ID4gPiA+ICsJCWdvdG8gZXJyX3RoZXJtYWw7DQo+ID4gPiA+ID4gKwl9DQo+ID4gPiA+ID4gKw0K
PiA+ID4gPiA+ICsJZGF0YS0+bW9kZSA9IFRIRVJNQUxfREVWSUNFX0VOQUJMRUQ7DQo+ID4gPiA+
ID4gKw0KPiA+ID4gPiA+ICsJcmV0dXJuIDA7DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICtlcnJf
dGhlcm1hbDoNCj4gPiA+ID4gPiArCWNwdWZyZXFfY29vbGluZ191bnJlZ2lzdGVyKGRhdGEtPmNk
ZXYpOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArZXJyX2Nvb2xpbmc6DQo+ID4gPiA+ID4gKwlp
b3VubWFwKGRhdGEtPnJlZ3MpOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArZXJyX2lvbWFwOg0K
PiA+ID4gPiA+ICsJZGV2X3NldF9kcnZkYXRhKCZwZGV2LT5kZXYsIE5VTEwpOw0KPiA+ID4gPiA+
ICsJZGV2bV9rZnJlZSgmcGRldi0+ZGV2LCBkYXRhKTsNCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4g
KwlyZXR1cm4gcmV0Ow0KPiA+ID4gPiA+ICt9DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICtzdGF0
aWMgaW50IHFvcmlxX3RtdV9yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikgew0K
PiA+ID4gPiA+ICsJc3RydWN0IHFvcmlxX3RtdV9kYXRhICpkYXRhID0gZGV2X2dldF9kcnZkYXRh
KCZwZGV2LT5kZXYpOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCS8qIERpc2FibGUgbW9uaXRv
cmluZyAqLw0KPiA+ID4gPiA+ICsJaW93cml0ZTMyYmUoVE1SX0RJU0FCTEUsICZkYXRhLT5yZWdz
LT50bXIpOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCXRoZXJtYWxfem9uZV9kZXZpY2VfdW5y
ZWdpc3RlcihkYXRhLT50eik7DQo+ID4gPiA+ID4gKwljcHVmcmVxX2Nvb2xpbmdfdW5yZWdpc3Rl
cihkYXRhLT5jZGV2KTsNCj4gPiA+ID4gPiArCWlvdW5tYXAoZGF0YS0+cmVncyk7DQo+ID4gPiA+
ID4gKw0KPiA+ID4gPiA+ICsJZGV2X3NldF9kcnZkYXRhKCZwZGV2LT5kZXYsIE5VTEwpOw0KPiA+
ID4gPiA+ICsJZGV2bV9rZnJlZSgmcGRldi0+ZGV2LCBkYXRhKTsNCj4gPiA+ID4gPiArDQo+ID4g
PiA+ID4gKwlyZXR1cm4gMDsNCj4gPiA+ID4gPiArfQ0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiAr
I2lmZGVmIENPTkZJR19QTV9TTEVFUA0KPiA+ID4gPiA+ICtzdGF0aWMgaW50IHFvcmlxX3RtdV9z
dXNwZW5kKHN0cnVjdCBkZXZpY2UgKmRldikgew0KPiA+ID4gPiA+ICsJc3RydWN0IHFvcmlxX3Rt
dV9kYXRhICpkYXRhID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4gPiA+ID4gKw0KPiA+ID4g
PiA+ICsJLyogRGlzYWJsZSBtb25pdG9yaW5nICovDQo+ID4gPiA+ID4gKwlpb3dyaXRlMzJiZShU
TVJfRElTQUJMRSwgJmRhdGEtPnJlZ3MtPnRtcik7DQo+ID4gPiA+ID4gKwlkYXRhLT5tb2RlID0g
VEhFUk1BTF9ERVZJQ0VfRElTQUJMRUQ7DQo+ID4gPiA+ID4gKw0KPiA+ID4gPiA+ICsJcmV0dXJu
IDA7DQo+ID4gPiA+ID4gK30NCj4gPiA+ID4gPiArDQo+ID4gPiA+ID4gK3N0YXRpYyBpbnQgcW9y
aXFfdG11X3Jlc3VtZShzdHJ1Y3QgZGV2aWNlICpkZXYpIHsNCj4gPiA+ID4gPiArCXN0cnVjdCBx
b3JpcV90bXVfZGF0YSAqZGF0YSA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+ID4gPiA+ICsN
Cj4gPiA+ID4gPiArCS8qIEVuYWJsZSBtb25pdG9yaW5nICovDQo+ID4gPiA+ID4gKwlpb3dyaXRl
MzJiZShUTVJfQUxMLCAmZGF0YS0+cmVncy0+dG1yKTsNCj4gPiA+ID4gPiArCWRhdGEtPm1vZGUg
PSBUSEVSTUFMX0RFVklDRV9FTkFCTEVEOw0KPiA+ID4gPiA+ICsNCj4gPiA+ID4gPiArCXJldHVy
biAwOw0KPiA+ID4gPiA+ICt9DQo+ID4gPiA+ID4gKyNlbmRpZg0KPiA+ID4gPiA+ICsNCj4gPiA+
ID4gPiArc3RhdGljIFNJTVBMRV9ERVZfUE1fT1BTKHFvcmlxX3RtdV9wbV9vcHMsDQo+ID4gPiA+
ID4gKwkJCSBxb3JpcV90bXVfc3VzcGVuZCwgcW9yaXFfdG11X3Jlc3VtZSk7DQo+ID4gPiA+ID4g
Kw0KPiA+ID4gPiA+ICtzdGF0aWMgY29uc3Qgc3RydWN0IG9mX2RldmljZV9pZCBxb3JpcV90bXVf
bWF0Y2hbXSA9IHsNCj4gPiA+ID4gPiArCXsgLmNvbXBhdGlibGUgPSAiZnNsLHFvcmlxLXRtdSIs
IH0sDQo+ID4gPiA+ID4gKwl7fSwNCj4gPiA+ID4gPiArfTsNCj4gPiA+ID4gPiArDQo+ID4gPiA+
ID4gK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVyIHFvcmlxX3RtdSA9IHsNCj4gPiA+ID4g
PiArCS5kcml2ZXIJPSB7DQo+ID4gPiA+ID4gKwkJLm93bmVyCQk9IFRISVNfTU9EVUxFLA0KPiA+
ID4gPiA+ICsJCS5uYW1lCQk9ICJxb3JpcV90aGVybWFsIiwNCj4gPiA+ID4gPiArCQkucG0gPSAm
cW9yaXFfdG11X3BtX29wcywNCj4gPiA+ID4gPiArCQkub2ZfbWF0Y2hfdGFibGUJPSBxb3JpcV90
bXVfbWF0Y2gsDQo+ID4gPiA+ID4gKwl9LA0KPiA+ID4gPiA+ICsJLnByb2JlCT0gcW9yaXFfdG11
X3Byb2JlLA0KPiA+ID4gPiA+ICsJLnJlbW92ZQk9IHFvcmlxX3RtdV9yZW1vdmUsDQo+ID4gPiA+
ID4gK307DQo+ID4gPiA+ID4gK21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIocW9yaXFfdG11KTsNCj4g
PiA+ID4gPiArDQo+ID4gPiA+ID4gK01PRFVMRV9BVVRIT1IoIkppYSBIb25ndGFvIDxob25ndGFv
LmppYUBmcmVlc2NhbGUuY29tPiIpOw0KPiA+ID4gPiA+ICtNT0RVTEVfREVTQ1JJUFRJT04oIkZy
ZWVzY2FsZSBRb3JJUSBUaGVybWFsIE1vbml0b3JpbmcgVW5pdA0KPiA+ID4gPiA+ICtkcml2ZXIi
KTsgTU9EVUxFX0xJQ0VOU0UoIkdQTCB2MiIpOw0KPiA+ID4gPiA+IC0tDQo+ID4gPiA+ID4gMi4x
LjAuMjcuZzk2ZGIzMjQNCj4gPiA+ID4gPg0KPiBfX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fXw0KPiBMaW51eHBwYy1kZXYgbWFpbGluZyBsaXN0DQo+IExpbnV4
cHBjLWRldkBsaXN0cy5vemxhYnMub3JnDQo+IGh0dHBzOi8vbGlzdHMub3psYWJzLm9yZy9saXN0
aW5mby9saW51eHBwYy1kZXYNCg==
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH V2] QorIQ/TMU: add thermal management support based on TMU
2015-08-14 3:15 ` Hongtao Jia
@ 2015-08-14 4:29 ` Eduardo Valentin
2015-08-14 6:40 ` Hongtao Jia
0 siblings, 1 reply; 8+ messages in thread
From: Eduardo Valentin @ 2015-08-14 4:29 UTC (permalink / raw)
To: Hongtao Jia
Cc: Scott Wood, linuxppc-dev@lists.ozlabs.org,
linux-pm@vger.kernel.org
Hello Hongtao,
On Fri, Aug 14, 2015 at 03:15:22AM +0000, Hongtao Jia wrote:
> Hi Eduardo,
>
> In previous mail I asked questions about including header files in device tree.
> Don't bother, I have already figured out the solution.
>
> Another questions is about cpu cooling:
> I found out that there is no explicit calling for registering cpu cooling
> device in the of-thermal style drivers.
Your understanding is correct.
>
> And Samsung did it in cpufreq driver: drivers/cpufreq/exynos-cpufreq.c
>
Yes.
> Should all the of-thermal driver use the same way?
of-thermal won't handle the cooling device registering. It is typically
registered by the cpufreq driver. Have a look in
drivers/cpufreq/cpufreq-dt.c
> Or is there any recommendation for registering cpu cooling device?
> (I enabled the CONFIG_CPUFREQ_DT and still got no cooling device registered)
If your system supports using cpufreq-dt, then it will handle
registering the cpucooling for you, if you configures the cooling dt
properties in your DT files.
How does your DT entry look like?
BR,
Eduardo
>
> Thanks.
>
> ---
> Best Regards,
> Hongtao
>
>
> > -----Original Message-----
> > From: Linuxppc-dev [mailto:linuxppc-dev-
> > bounces+b38951=freescale.com@lists.ozlabs.org] On Behalf Of Hongtao Jia
> > Sent: Friday, August 07, 2015 4:15 PM
> > To: Eduardo Valentin
> > Cc: Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org; linux-
> > pm@vger.kernel.org
> > Subject: RE: [PATCH V2] QorIQ/TMU: add thermal management support based
> > on TMU
> >
> > Thanks for your comments.
> > Please see my questions inline.
> >
> > Thanks.
> > ---
> > Best Regards,
> > Hongtao
> >
> >
> > > -----Original Message-----
> > > From: Eduardo Valentin [mailto:edubezval@gmail.com]
> > > Sent: Thursday, August 06, 2015 3:43 AM
> > > To: Jia Hongtao-B38951
> > > Cc: linux-pm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Wood
> > > Scott-
> > > B07421
> > > Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management support
> > > based on TMU
> > >
> > > On Thu, Jul 30, 2015 at 08:13:09AM +0000, Hongtao Jia wrote:
> > > > - "Any specific reason why not using OF thermal?"
> > > > - No, actually.
> > > >
> > > > I'd like to use OF thermal after some clarification.
> > > >
> > > > Regarding to "cooling-maps". For some cases there should be more
> > > > than one cpus as cooling device and they are independent.
> > > > 1. Let's say 4. So we need to provide 4 maps like map0-map3. Right?
> > >
> > > That would depend on the amount of sensors you have. Do you have one
> > > sensor per cpu? if the answer is yes, then you probably want to have
> > > four different map entries, yes, but one on each thermal zone of each
> > > cpu temperature sensor. if the answer is no, then you would need to
> > > have all the maps in the same thermal zone.
> > >
> > > > 2. "cooling-max-level" may vary depend on switch settings or firmware.
> > > Is that
> > > > OK if I do not provide "cooling-min-level" and "cooling-max-level"
> > > property?
> > >
> > > That is already achievable by using the cooling-device property of a
> > > cooling map.
> > >
> > > Please have a look in the example section of the
> > > Documentation/devicetree/bindings/thermal/thermal.txt
> >
> > Yes, I read this file.
> > So in my understanding:
> > There is no need to provide "cooling-min-level" and "cooling-max-level"
> > property.
> > THERMAL_NO_LIMIT value in cooling device node will indicate the driver to
> > automatically parse the min and max state, right?
> >
> > Talking about THERMAL_NO_LIMIT, I need to #include <dt-
> > bindings/thermal/thermal.h> to provide the definition. But I got
> > compiling error when build dtb file.
> > I did some research and using "make t1040qds.dtb" in order to involve
> > preprocessor.
> > But with simply adding "#include <dt-bindings/thermal/thermal.h>" to
> > t1040si-post.dtsi at line 35 I still got error like this:
> > Error: arch/powerpc/boot/dts/fsl/t1040si-post.dtsi:35.1-9 syntax error
> > FATAL ERROR: Unable to parse input tree
> >
> > Could you help me out here.
> > Thanks.
> >
> > >
> > > Let me know if you need further clarification.
> > >
> > >
> > > BR,
> > >
> > > Eduardo Valentin
> > >
> > > >
> > > > Thanks.
> > > > -Hongtao
> > > >
> > > >
> > > > > -----Original Message-----
> > > > > From: Eduardo Valentin [mailto:edubezval@gmail.com]
> > > > > Sent: Thursday, July 30, 2015 2:56 PM
> > > > > To: Jia Hongtao-B38951
> > > > > Cc: linux-pm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Wood
> > > > > Scott-
> > > > > B07421
> > > > > Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management support
> > > > > based on TMU
> > > > >
> > > > > On Wed, Jul 29, 2015 at 02:19:39PM +0800, Jia Hongtao wrote:
> > > > > > 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.
> > > > > >
> > > > > > Signed-off-by: Jia Hongtao <hongtao.jia@freescale.com>
> > > > > > ---
> > > > > > This patch based on:
> > > > > > http://patchwork.ozlabs.org/patch/482987/
> > > > > >
> > > > > > Changes for V2:
> > > > > > * Add tmu-range parse.
> > > > > > * Use default trend hook.
> > > > > > * Using latest thermal_zone_bind_cooling_device API.
> > > > > > * Add calibration check during initialization.
> > > > > > * Disable/enalbe device when suspend/resume.
> > > > > >
> > > > > > drivers/thermal/Kconfig | 11 ++
> > > > > > drivers/thermal/Makefile | 1 +
> > > > > > drivers/thermal/qoriq_thermal.c | 406
> > > > > > ++++++++++++++++++++++++++++++++++++++++
> > > > > > 3 files changed, 418 insertions(+) create mode 100644
> > > > > > drivers/thermal/qoriq_thermal.c
> > > > > >
> > > > > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > > > > > index
> > > > > > 118938e..a200745 100644
> > > > > > --- a/drivers/thermal/Kconfig
> > > > > > +++ b/drivers/thermal/Kconfig
> > > > > > @@ -180,6 +180,17 @@ config IMX_THERMAL
> > > > > > cpufreq is used as the cooling device to throttle CPUs when
> > > the
> > > > > > passive trip is crossed.
> > > > > >
> > > > > > +config QORIQ_THERMAL
> > > > > > + tristate "Freescale QorIQ Thermal Monitoring Unit"
> > > > > > + depends on CPU_THERMAL
> > > > > > + depends on OF
> > > > > > + default n
> > > > > > + help
> > > > > > + Enable thermal management based on Freescale QorIQ Thermal
> > > > > Monitoring
> > > > > > + Unit (TMU). 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
> > > > > > bool "SPEAr thermal sensor driver"
> > > > > > depends on PLAT_SPEAR
> > > > > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > > > > > index
> > > > > > 535dfee..8c25859 100644
> > > > > > --- a/drivers/thermal/Makefile
> > > > > > +++ b/drivers/thermal/Makefile
> > > > > > @@ -33,6 +33,7 @@ obj-$(CONFIG_DOVE_THERMAL) +=
> > dove_thermal.o
> > > > > > obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
> > > > > > obj-$(CONFIG_ARMADA_THERMAL) += armada_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..0694f42
> > > > > > --- /dev/null
> > > > > > +++ b/drivers/thermal/qoriq_thermal.c
> > > > > > @@ -0,0 +1,406 @@
> > > > > > +/*
> > > > > > + * Copyright 2015 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.
> > > > > > + *
> > > > > > + */
> > > > > > +
> > > > > > +/*
> > > > > > + * Based on Freescale QorIQ Thermal Monitoring Unit (TMU) */
> > > > > > +#include <linux/cpufreq.h> #include <linux/cpu_cooling.h>
> > > > > > +#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>
> > > > > > +
> > > > > > +#define SITES_MAX 16
> > > > > > +
> > > > > > +#define TMU_TEMP_PASSIVE 85000
> > > > > > +#define TMU_TEMP_CRITICAL 95000
> > > > > > +
> > > > > > +#define TMU_PASSIVE_DELAY 1000 /* Milliseconds */
> > > > > > +#define TMU_POLLING_DELAY 5000
> > > > > > +
> > > > > > +/* The driver supports 1 passive trip point and 1 critical trip
> > > > > > +point */ enum tmu_thermal_trip {
> > > > > > + TMU_TRIP_PASSIVE,
> > > > > > + TMU_TRIP_CRITICAL,
> > > > > > + TMU_TRIP_NUM,
> > > > > > +};
> > > > > > +
> > > > > > +/*
> > > > > > + * QorIQ TMU Registers
> > > > > > + */
> > > > > > +struct qoriq_tmu_site_regs {
> > > > > > + __be32 tritsr; /* Immediate Temperature Site
> > > Register */
> > > > > > + __be32 tratsr; /* Average Temperature Site Register
> > > */
> > > > > > + u8 res0[0x8];
> > > > > > +} __packed;
> > > > > > +
> > > > > > +struct qoriq_tmu_regs {
> > > > > > + __be32 tmr; /* Mode Register */
> > > > > > +#define TMR_DISABLE 0x0
> > > > > > +#define TMR_ME 0x80000000
> > > > > > +#define TMR_ALPF 0x0c000000
> > > > > > +#define TMR_MSITE 0x00008000
> > > > > > +#define TMR_ALL (TMR_ME | TMR_ALPF | TMR_MSITE)
> > > > > > + __be32 tsr; /* Status Register */
> > > > > > + __be32 tmtmir; /* Temperature measurement interval
> > > > > Register */
> > > > > > +#define TMTMIR_DEFAULT 0x00000007
> > > > > > + u8 res0[0x14];
> > > > > > + __be32 tier; /* Interrupt Enable Register */
> > > > > > +#define TIER_DISABLE 0x0
> > > > > > + __be32 tidr; /* Interrupt Detect Register */
> > > > > > + __be32 tiscr; /* Interrupt Site Capture Register */
> > > > > > + __be32 ticscr; /* Interrupt Critical Site Capture
> > > Register
> > > > > */
> > > > > > + u8 res1[0x10];
> > > > > > + __be32 tmhtcrh; /* High Temperature Capture Register
> > > */
> > > > > > + __be32 tmhtcrl; /* Low Temperature Capture Register
> > > */
> > > > > > + u8 res2[0x8];
> > > > > > + __be32 tmhtitr; /* High Temperature Immediate
> > > Threshold */
> > > > > > + __be32 tmhtatr; /* High Temperature Average Threshold
> > > */
> > > > > > + __be32 tmhtactr; /* High Temperature Average Crit Threshold
> > > */
> > > > > > + u8 res3[0x24];
> > > > > > + __be32 ttcfgr; /* Temperature Configuration Register
> > > */
> > > > > > + __be32 tscfgr; /* Sensor Configuration Register */
> > > > > > + u8 res4[0x78];
> > > > > > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > > > > > + u8 res5[0x9f8];
> > > > > > + __be32 ipbrr0; /* IP Block Revision Register 0 */
> > > > > > + __be32 ipbrr1; /* IP Block Revision Register 1 */
> > > > > > + u8 res6[0x310];
> > > > > > + __be32 ttr0cr; /* Temperature Range 0 Control
> > > Register */
> > > > > > + __be32 ttr1cr; /* Temperature Range 1 Control
> > > Register */
> > > > > > + __be32 ttr2cr; /* Temperature Range 2 Control
> > > Register */
> > > > > > + __be32 ttr3cr; /* Temperature Range 3 Control
> > > Register */
> > > > > > +};
> > > > > > +
> > > > > > +/*
> > > > > > + * Thermal zone data
> > > > > > + */
> > > > > > +struct qoriq_tmu_data {
> > > > > > + struct thermal_zone_device *tz;
> > > > > > + struct thermal_cooling_device *cdev;
> > > > > > + enum thermal_device_mode mode;
> > > > > > + unsigned long temp_passive;
> > > > > > + unsigned long temp_critical;
> > > > > > + struct qoriq_tmu_regs __iomem *regs; };
> > > > > > +
> > > > > > +static int tmu_get_mode(struct thermal_zone_device *tz,
> > > > > > + enum thermal_device_mode *mode) {
> > > > > > + struct qoriq_tmu_data *data = tz->devdata;
> > > > > > +
> > > > > > + *mode = data->mode;
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int tmu_set_mode(struct thermal_zone_device *tz,
> > > > > > + enum thermal_device_mode mode) {
> > > > > > + struct qoriq_tmu_data *data = tz->devdata;
> > > > > > +
> > > > > > + if (mode == THERMAL_DEVICE_ENABLED) {
> > > > > > + tz->polling_delay = TMU_POLLING_DELAY;
> > > > > > + tz->passive_delay = TMU_PASSIVE_DELAY;
> > > > > > + thermal_zone_device_update(tz);
> > > > > > + } else {
> > > > > > + tz->polling_delay = 0;
> > > > > > + tz->passive_delay = 0;
> > > > > > + }
> > > > > > +
> > > > > > + data->mode = mode;
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int tmu_get_temp(struct thermal_zone_device *tz,
> > > > > > +unsigned long
> > > > > > +*temp) {
> > > > > > + u8 val;
> > > > > > + struct qoriq_tmu_data *data = tz->devdata;
> > > > > > +
> > > > > > + val = ioread32be(&data->regs->site[0].tritsr);
> > > > > > + *temp = (unsigned long)val * 1000;
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int tmu_get_trip_type(struct thermal_zone_device *tz,
> > > > > > +int
> > > trip,
> > > > > > + enum thermal_trip_type *type) {
> > > > > > + *type = (trip == TMU_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
> > > > > > + THERMAL_TRIP_CRITICAL;
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int tmu_get_trip_temp(struct thermal_zone_device *tz,
> > > > > > +int
> > > trip,
> > > > > > + unsigned long *temp)
> > > > > > +{
> > > > > > + struct qoriq_tmu_data *data = tz->devdata;
> > > > > > +
> > > > > > + *temp = (trip == TMU_TRIP_PASSIVE) ? data->temp_passive :
> > > > > > + data->temp_critical;
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int tmu_get_crit_temp(struct thermal_zone_device *tz,
> > > > > > + unsigned long *temp)
> > > > > > +{
> > > > > > + struct qoriq_tmu_data *data = tz->devdata;
> > > > > > +
> > > > > > + *temp = data->temp_critical;
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int tmu_bind(struct thermal_zone_device *tz,
> > > > > > + struct thermal_cooling_device *cdev) {
> > > > > > + int ret;
> > > > > > +
> > > > > > + ret = thermal_zone_bind_cooling_device(tz, TMU_TRIP_PASSIVE,
> > > cdev,
> > > > > > + THERMAL_NO_LIMIT,
> > > > > > + THERMAL_NO_LIMIT,
> > > > > > + THERMAL_WEIGHT_DEFAULT);
> > > > > > + if (ret) {
> > > > > > + dev_err(&tz->device,
> > > > > > + "Binding zone %s with cdev %s failed:%d\n",
> > > > > > + tz->type, cdev->type, ret);
> > > > > > + }
> > > > > > +
> > > > > > + return ret;
> > > > > > +}
> > > > > > +
> > > > > > +static int tmu_unbind(struct thermal_zone_device *tz,
> > > > > > + struct thermal_cooling_device *cdev) {
> > > > > > + int ret;
> > > > > > +
> > > > > > + ret = thermal_zone_unbind_cooling_device(tz, TMU_TRIP_PASSIVE,
> > > > > cdev);
> > > > > > + if (ret) {
> > > > > > + dev_err(&tz->device,
> > > > > > + "Unbinding zone %s with cdev %s failed:%d\n",
> > > > > > + tz->type, cdev->type, ret);
> > > > > > + }
> > > > > > +
> > > > > > + return ret;
> > > > > > +}
> > > > > > +
> > > > > > +static int qoriq_tmu_calibration(struct platform_device *pdev) {
> > > > > > + int i, val, len;
> > > > > > + u32 range[4];
> > > > > > + const __be32 *calibration;
> > > > > > + struct device_node *node = pdev->dev.of_node;
> > > > > > + struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev);
> > > > > > +
> > > > > > + /* Disable monitoring before calibration */
> > > > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > > > +
> > > > > > + if (of_property_read_u32_array(node, "fsl,tmu-range", range,
> > > 4))
> > > > > > + return -1;
> > > > > > +
> > > > > > + /* Init temperature range registers */
> > > > > > + iowrite32be(range[0], &data->regs->ttr0cr);
> > > > > > + iowrite32be(range[1], &data->regs->ttr1cr);
> > > > > > + iowrite32be(range[2], &data->regs->ttr2cr);
> > > > > > + iowrite32be(range[3], &data->regs->ttr3cr);
> > > > > > +
> > > > > > + calibration = of_get_property(node, "fsl,tmu-calibration",
> > > &len);
> > > > > > + if (calibration == NULL)
> > > > > > + return -1;
> > > > > > +
> > > > > > + for (i = 0; i < len; i += 8, calibration += 2) {
> > > > > > + val = (int)of_read_number(calibration, 1);
> > > > > > + iowrite32be(val, &data->regs->ttcfgr);
> > > > > > + val = (int)of_read_number(calibration + 1, 1);
> > > > > > + iowrite32be(val, &data->regs->tscfgr);
> > > > > > + }
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) {
> > > > > > + /* Disable interrupt, using polling instead */
> > > > > > + iowrite32be(TIER_DISABLE, &data->regs->tier);
> > > > > > +
> > > > > > + /* Set update_interval */
> > > > > > + iowrite32be(TMTMIR_DEFAULT, &data->regs->tmtmir);
> > > > > > +
> > > > > > + /* Enable monitoring */
> > > > > > + iowrite32be(TMR_ALL, &data->regs->tmr); }
> > > > > > +
> > > > > > +static struct thermal_zone_device_ops tmu_tz_ops = {
> > > > > > + .bind = tmu_bind,
> > > > > > + .unbind = tmu_unbind,
> > > > > > + .get_temp = tmu_get_temp,
> > > > > > + .get_mode = tmu_get_mode,
> > > > > > + .set_mode = tmu_set_mode,
> > > > > > + .get_trip_type = tmu_get_trip_type,
> > > > > > + .get_trip_temp = tmu_get_trip_temp,
> > > > > > + .get_crit_temp = tmu_get_crit_temp, };
> > > > > > +
> > > > > > +static int qoriq_tmu_probe(struct platform_device *pdev) {
> > > > > > + int ret;
> > > > > > + struct cpumask clip_cpus;
> > > > > > + struct qoriq_tmu_data *data;
> > > > > > +
> > > > > > + if (!cpufreq_get_current_driver()) {
> > > > > > + dev_dbg(&pdev->dev, "No cpufreq driver yet\n");
> > > > > > + return -EPROBE_DEFER;
> > > > > > + }
> > > > > > +
> > > > > > + if (!pdev->dev.of_node) {
> > > > > > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > > > > > + return -EFAULT;
> > > > > > + }
> > > > > > +
> > > > > > + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
> > > > > > + GFP_KERNEL);
> > > > > > + if (!data)
> > > > > > + return -ENOMEM;
> > > > > > +
> > > > > > + dev_set_drvdata(&pdev->dev, data);
> > > > > > + data->regs = of_iomap(pdev->dev.of_node, 0);
> > > > > > +
> > > > > > + if (!data->regs) {
> > > > > > + dev_err(&pdev->dev, "Failed to get memory region\n");
> > > > > > + ret = -ENODEV;
> > > > > > + goto err_iomap;
> > > > > > + }
> > > > > > +
> > > > > > + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */
> > > > > > + if (ret < 0) {
> > > > > > + dev_err(&pdev->dev, "TMU calibration failed.\n");
> > > > > > + ret = -ENODEV;
> > > > > > + goto err_iomap;
> > > > > > + }
> > > > > > +
> > > > > > + qoriq_tmu_init_device(data); /* TMU initialization */
> > > > > > +
> > > > > > + cpumask_setall(&clip_cpus);
> > > > > > + data->cdev = cpufreq_cooling_register(&clip_cpus);
> > > > > > + if (IS_ERR(data->cdev)) {
> > > > > > + ret = PTR_ERR(data->cdev);
> > > > > > + dev_err(&data->cdev->device,
> > > > > > + "Failed to register cpufreq cooling device: %d\n",
> > > ret);
> > > > > > + goto err_cooling;
> > > > > > + }
> > > > > > +
> > > > > > + data->temp_passive = TMU_TEMP_PASSIVE;
> > > > > > + data->temp_critical = TMU_TEMP_CRITICAL;
> > > > > > + data->tz = thermal_zone_device_register("tmu_thermal_zone",
> > > > >
> > > > > Any specific reason why not using OF thermal?
> > > > >
> > > > > > + TMU_TRIP_NUM,
> > > > > > + 0, data,
> > > > > > + &tmu_tz_ops, NULL,
> > > > > > + TMU_PASSIVE_DELAY,
> > > > > > + TMU_POLLING_DELAY);
> > > > > > +
> > > > > > + 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_thermal;
> > > > > > + }
> > > > > > +
> > > > > > + data->mode = THERMAL_DEVICE_ENABLED;
> > > > > > +
> > > > > > + return 0;
> > > > > > +
> > > > > > +err_thermal:
> > > > > > + cpufreq_cooling_unregister(data->cdev);
> > > > > > +
> > > > > > +err_cooling:
> > > > > > + iounmap(data->regs);
> > > > > > +
> > > > > > +err_iomap:
> > > > > > + dev_set_drvdata(&pdev->dev, NULL);
> > > > > > + devm_kfree(&pdev->dev, data);
> > > > > > +
> > > > > > + return ret;
> > > > > > +}
> > > > > > +
> > > > > > +static int qoriq_tmu_remove(struct platform_device *pdev) {
> > > > > > + struct qoriq_tmu_data *data = dev_get_drvdata(&pdev->dev);
> > > > > > +
> > > > > > + /* Disable monitoring */
> > > > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > > > +
> > > > > > + thermal_zone_device_unregister(data->tz);
> > > > > > + cpufreq_cooling_unregister(data->cdev);
> > > > > > + iounmap(data->regs);
> > > > > > +
> > > > > > + dev_set_drvdata(&pdev->dev, NULL);
> > > > > > + devm_kfree(&pdev->dev, data);
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +#ifdef CONFIG_PM_SLEEP
> > > > > > +static int qoriq_tmu_suspend(struct device *dev) {
> > > > > > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > > > > > +
> > > > > > + /* Disable monitoring */
> > > > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > > > + data->mode = THERMAL_DEVICE_DISABLED;
> > > > > > +
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int qoriq_tmu_resume(struct device *dev) {
> > > > > > + struct qoriq_tmu_data *data = dev_get_drvdata(dev);
> > > > > > +
> > > > > > + /* Enable monitoring */
> > > > > > + iowrite32be(TMR_ALL, &data->regs->tmr);
> > > > > > + data->mode = THERMAL_DEVICE_ENABLED;
> > > > > > +
> > > > > > + 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", },
> > > > > > + {},
> > > > > > +};
> > > > > > +
> > > > > > +static struct platform_driver qoriq_tmu = {
> > > > > > + .driver = {
> > > > > > + .owner = THIS_MODULE,
> > > > > > + .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@freescale.com>");
> > > > > > +MODULE_DESCRIPTION("Freescale 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] 8+ messages in thread
* RE: [PATCH V2] QorIQ/TMU: add thermal management support based on TMU
2015-08-14 4:29 ` Eduardo Valentin
@ 2015-08-14 6:40 ` Hongtao Jia
0 siblings, 0 replies; 8+ messages in thread
From: Hongtao Jia @ 2015-08-14 6:40 UTC (permalink / raw)
To: Eduardo Valentin
Cc: Scott Wood, linuxppc-dev@lists.ozlabs.org,
linux-pm@vger.kernel.org
> -----Original Message-----
> From: Eduardo Valentin [mailto:edubezval@gmail.com]
> Sent: Friday, August 14, 2015 12:29 PM
> To: Jia Hongtao-B38951
> Cc: Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org; linux-
> pm@vger.kernel.org
> Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management support based
> on TMU
>=20
> Hello Hongtao,
>=20
> On Fri, Aug 14, 2015 at 03:15:22AM +0000, Hongtao Jia wrote:
> > Hi Eduardo,
> >
> > In previous mail I asked questions about including header files in
> device tree.
> > Don't bother, I have already figured out the solution.
> >
> > Another questions is about cpu cooling:
> > I found out that there is no explicit calling for registering cpu
> > cooling device in the of-thermal style drivers.
>=20
> Your understanding is correct.
>=20
> >
> > And Samsung did it in cpufreq driver: drivers/cpufreq/exynos-cpufreq.c
> >
>=20
> Yes.
>=20
> > Should all the of-thermal driver use the same way?
>=20
> of-thermal won't handle the cooling device registering. It is typically
> registered by the cpufreq driver. Have a look in
> drivers/cpufreq/cpufreq-dt.c
>=20
> > Or is there any recommendation for registering cpu cooling device?
> > (I enabled the CONFIG_CPUFREQ_DT and still got no cooling device
> > registered)
>=20
> If your system supports using cpufreq-dt, then it will handle registering
> the cpucooling for you, if you configures the cooling dt properties in
> your DT files.
>=20
> How does your DT entry look like?
Here is the related code snippet:
cpus {
#address-cells =3D <1>;
#size-cells =3D <0>;
cpu0: PowerPC,e5500@0 {
device_type =3D "cpu";
reg =3D <0>;
clocks =3D <&mux0>;
next-level-cache =3D <&L2_1>;
/*cooling-min-level =3D <0>;*/
/*cooling-max-level =3D <1>;*/
#cooling-cells =3D <2>;
L2_1: l2-cache {
next-level-cache =3D <&cpc>;
};
};
cpu1: PowerPC,e5500@1 {
device_type =3D "cpu";
reg =3D <1>;
clocks =3D <&mux1>;
next-level-cache =3D <&L2_2>;
/*cooling-min-level =3D <0>;*/
/*cooling-max-level =3D <1>;*/
#cooling-cells =3D <2>;
L2_2: l2-cache {
next-level-cache =3D <&cpc>;
};
};
cpu2: PowerPC,e5500@2 {
device_type =3D "cpu";
reg =3D <2>;
clocks =3D <&mux2>;
next-level-cache =3D <&L2_3>;
/*cooling-min-level =3D <0>;*/
/*cooling-max-level =3D <1>;*/
#cooling-cells =3D <2>;
L2_3: l2-cache {
next-level-cache =3D <&cpc>;
};
};
cpu3: PowerPC,e5500@3 {
device_type =3D "cpu";
reg =3D <3>;
clocks =3D <&mux3>;
next-level-cache =3D <&L2_4>;
/*cooling-min-level =3D <0>;*/
/*cooling-max-level =3D <1>;*/
#cooling-cells =3D <2>;
L2_4: l2-cache {
next-level-cache =3D <&cpc>;
};
};
......
thermal-zones {
cpu_thermal: cpu-thermal {
polling-delay-passive =3D <1000>;
polling-delay =3D <5000>;
thermal-sensors =3D <&tmu>;
trips {
cpu_alert: cpu-alert {
temperature =3D <45000>;
hysteresis =3D <2000>;
type =3D "passive";
};
cpu_crit: cpu-crit {
temperature =3D <95000>;
hysteresis =3D <2000>;
type =3D "critical";
};
};
cooling-maps {
map0 {
trip =3D <&cpu_alert>;
cooling-device =3D
<&cpu0 THERMAL_NO_LIMIT
THERMAL_NO_LIMIT>;
};
map1 {
trip =3D <&cpu_alert>;
cooling-device =3D
<&cpu1 THERMAL_NO_LIMIT
THERMAL_NO_LIMIT>;
};
map2 {
trip =3D <&cpu_alert>;
cooling-device =3D
<&cpu2 THERMAL_NO_LIMIT
THERMAL_NO_LIMIT>;
};
map3 {
trip =3D <&cpu_alert>;
cooling-device =3D
<&cpu3 THERMAL_NO_LIMIT
THERMAL_NO_LIMIT>;
};
};
};
};
Please note that I comment out the cooling-min-level and cooling-max-level
property. I'd like to let cpufreq driver parse these two property. I don't
quite clear if this is OK.
You said if the system support cpufreq-dt it will handle the cooling device
registration.
Then how to support cpufreq-dt? Do I need to update qoriq-cpufreq.c?
Do you have a quick answer for me?
Thanks.
-Hongtao.
>=20
> BR,
>=20
> Eduardo
> >
> > Thanks.
> >
> > ---
> > Best Regards,
> > Hongtao
> >
> >
> > > -----Original Message-----
> > > From: Linuxppc-dev [mailto:linuxppc-dev-
> > > bounces+b38951=3Dfreescale.com@lists.ozlabs.org] On Behalf Of Hongtao
> > > bounces+Jia
> > > Sent: Friday, August 07, 2015 4:15 PM
> > > To: Eduardo Valentin
> > > Cc: Wood Scott-B07421; linuxppc-dev@lists.ozlabs.org; linux-
> > > pm@vger.kernel.org
> > > Subject: RE: [PATCH V2] QorIQ/TMU: add thermal management support
> > > based on TMU
> > >
> > > Thanks for your comments.
> > > Please see my questions inline.
> > >
> > > Thanks.
> > > ---
> > > Best Regards,
> > > Hongtao
> > >
> > >
> > > > -----Original Message-----
> > > > From: Eduardo Valentin [mailto:edubezval@gmail.com]
> > > > Sent: Thursday, August 06, 2015 3:43 AM
> > > > To: Jia Hongtao-B38951
> > > > Cc: linux-pm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Wood
> > > > Scott-
> > > > B07421
> > > > Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management support
> > > > based on TMU
> > > >
> > > > On Thu, Jul 30, 2015 at 08:13:09AM +0000, Hongtao Jia wrote:
> > > > > - "Any specific reason why not using OF thermal?"
> > > > > - No, actually.
> > > > >
> > > > > I'd like to use OF thermal after some clarification.
> > > > >
> > > > > Regarding to "cooling-maps". For some cases there should be more
> > > > > than one cpus as cooling device and they are independent.
> > > > > 1. Let's say 4. So we need to provide 4 maps like map0-map3.
> Right?
> > > >
> > > > That would depend on the amount of sensors you have. Do you have
> > > > one sensor per cpu? if the answer is yes, then you probably want
> > > > to have four different map entries, yes, but one on each thermal
> > > > zone of each cpu temperature sensor. if the answer is no, then you
> > > > would need to have all the maps in the same thermal zone.
> > > >
> > > > > 2. "cooling-max-level" may vary depend on switch settings or
> firmware.
> > > > Is that
> > > > > OK if I do not provide "cooling-min-level" and "cooling-max-
> level"
> > > > property?
> > > >
> > > > That is already achievable by using the cooling-device property of
> > > > a cooling map.
> > > >
> > > > Please have a look in the example section of the
> > > > Documentation/devicetree/bindings/thermal/thermal.txt
> > >
> > > Yes, I read this file.
> > > So in my understanding:
> > > There is no need to provide "cooling-min-level" and "cooling-max-
> level"
> > > property.
> > > THERMAL_NO_LIMIT value in cooling device node will indicate the
> > > driver to automatically parse the min and max state, right?
> > >
> > > Talking about THERMAL_NO_LIMIT, I need to #include <dt-
> > > bindings/thermal/thermal.h> to provide the definition. But I got
> > > compiling error when build dtb file.
> > > I did some research and using "make t1040qds.dtb" in order to
> > > involve preprocessor.
> > > But with simply adding "#include <dt-bindings/thermal/thermal.h>" to
> > > t1040si-post.dtsi at line 35 I still got error like this:
> > > Error: arch/powerpc/boot/dts/fsl/t1040si-post.dtsi:35.1-9 syntax
> > > error FATAL ERROR: Unable to parse input tree
> > >
> > > Could you help me out here.
> > > Thanks.
> > >
> > > >
> > > > Let me know if you need further clarification.
> > > >
> > > >
> > > > BR,
> > > >
> > > > Eduardo Valentin
> > > >
> > > > >
> > > > > Thanks.
> > > > > -Hongtao
> > > > >
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Eduardo Valentin [mailto:edubezval@gmail.com]
> > > > > > Sent: Thursday, July 30, 2015 2:56 PM
> > > > > > To: Jia Hongtao-B38951
> > > > > > Cc: linux-pm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org;
> > > > > > Wood
> > > > > > Scott-
> > > > > > B07421
> > > > > > Subject: Re: [PATCH V2] QorIQ/TMU: add thermal management
> > > > > > support based on TMU
> > > > > >
> > > > > > On Wed, Jul 29, 2015 at 02:19:39PM +0800, Jia Hongtao wrote:
> > > > > > > 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.
> > > > > > >
> > > > > > > Signed-off-by: Jia Hongtao <hongtao.jia@freescale.com>
> > > > > > > ---
> > > > > > > This patch based on:
> > > > > > > http://patchwork.ozlabs.org/patch/482987/
> > > > > > >
> > > > > > > Changes for V2:
> > > > > > > * Add tmu-range parse.
> > > > > > > * Use default trend hook.
> > > > > > > * Using latest thermal_zone_bind_cooling_device API.
> > > > > > > * Add calibration check during initialization.
> > > > > > > * Disable/enalbe device when suspend/resume.
> > > > > > >
> > > > > > > drivers/thermal/Kconfig | 11 ++
> > > > > > > drivers/thermal/Makefile | 1 +
> > > > > > > drivers/thermal/qoriq_thermal.c | 406
> > > > > > > ++++++++++++++++++++++++++++++++++++++++
> > > > > > > 3 files changed, 418 insertions(+) create mode 100644
> > > > > > > drivers/thermal/qoriq_thermal.c
> > > > > > >
> > > > > > > diff --git a/drivers/thermal/Kconfig
> > > > > > > b/drivers/thermal/Kconfig index
> > > > > > > 118938e..a200745 100644
> > > > > > > --- a/drivers/thermal/Kconfig
> > > > > > > +++ b/drivers/thermal/Kconfig
> > > > > > > @@ -180,6 +180,17 @@ config IMX_THERMAL
> > > > > > > cpufreq is used as the cooling device to throttle
> CPUs
> > > > > > > when
> > > > the
> > > > > > > passive trip is crossed.
> > > > > > >
> > > > > > > +config QORIQ_THERMAL
> > > > > > > + tristate "Freescale QorIQ Thermal Monitoring Unit"
> > > > > > > + depends on CPU_THERMAL
> > > > > > > + depends on OF
> > > > > > > + default n
> > > > > > > + help
> > > > > > > + Enable thermal management based on Freescale QorIQ
> > > > > > > +Thermal
> > > > > > Monitoring
> > > > > > > + Unit (TMU). 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
> > > > > > > bool "SPEAr thermal sensor driver"
> > > > > > > depends on PLAT_SPEAR
> > > > > > > diff --git a/drivers/thermal/Makefile
> > > > > > > b/drivers/thermal/Makefile index
> > > > > > > 535dfee..8c25859 100644
> > > > > > > --- a/drivers/thermal/Makefile
> > > > > > > +++ b/drivers/thermal/Makefile
> > > > > > > @@ -33,6 +33,7 @@ obj-$(CONFIG_DOVE_THERMAL) +=3D
> > > dove_thermal.o
> > > > > > > obj-$(CONFIG_DB8500_THERMAL) +=3D db8500_thermal.o
> > > > > > > obj-$(CONFIG_ARMADA_THERMAL) +=3D armada_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_thermal.c new file mode 100644 index
> > > > > > > 0000000..0694f42
> > > > > > > --- /dev/null
> > > > > > > +++ b/drivers/thermal/qoriq_thermal.c
> > > > > > > @@ -0,0 +1,406 @@
> > > > > > > +/*
> > > > > > > + * Copyright 2015 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.
> > > > > > > + *
> > > > > > > + */
> > > > > > > +
> > > > > > > +/*
> > > > > > > + * Based on Freescale QorIQ Thermal Monitoring Unit (TMU)
> > > > > > > +*/ #include <linux/cpufreq.h> #include
> > > > > > > +<linux/cpu_cooling.h> #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>
> > > > > > > +
> > > > > > > +#define SITES_MAX 16
> > > > > > > +
> > > > > > > +#define TMU_TEMP_PASSIVE 85000
> > > > > > > +#define TMU_TEMP_CRITICAL 95000
> > > > > > > +
> > > > > > > +#define TMU_PASSIVE_DELAY 1000 /* Milliseconds */
> > > > > > > +#define TMU_POLLING_DELAY 5000
> > > > > > > +
> > > > > > > +/* The driver supports 1 passive trip point and 1 critical
> > > > > > > +trip point */ enum tmu_thermal_trip {
> > > > > > > + TMU_TRIP_PASSIVE,
> > > > > > > + TMU_TRIP_CRITICAL,
> > > > > > > + TMU_TRIP_NUM,
> > > > > > > +};
> > > > > > > +
> > > > > > > +/*
> > > > > > > + * QorIQ TMU Registers
> > > > > > > + */
> > > > > > > +struct qoriq_tmu_site_regs {
> > > > > > > + __be32 tritsr; /* Immediate Temperature Site
> > > > Register */
> > > > > > > + __be32 tratsr; /* Average Temperature Site
> Register
> > > > */
> > > > > > > + u8 res0[0x8];
> > > > > > > +} __packed;
> > > > > > > +
> > > > > > > +struct qoriq_tmu_regs {
> > > > > > > + __be32 tmr; /* Mode Register */
> > > > > > > +#define TMR_DISABLE 0x0
> > > > > > > +#define TMR_ME 0x80000000
> > > > > > > +#define TMR_ALPF 0x0c000000
> > > > > > > +#define TMR_MSITE 0x00008000
> > > > > > > +#define TMR_ALL (TMR_ME | TMR_ALPF | TMR_MSITE)
> > > > > > > + __be32 tsr; /* Status Register */
> > > > > > > + __be32 tmtmir; /* Temperature measurement
> interval
> > > > > > Register */
> > > > > > > +#define TMTMIR_DEFAULT 0x00000007
> > > > > > > + u8 res0[0x14];
> > > > > > > + __be32 tier; /* Interrupt Enable Register */
> > > > > > > +#define TIER_DISABLE 0x0
> > > > > > > + __be32 tidr; /* Interrupt Detect Register */
> > > > > > > + __be32 tiscr; /* Interrupt Site Capture
> Register */
> > > > > > > + __be32 ticscr; /* Interrupt Critical Site
> Capture
> > > > Register
> > > > > > */
> > > > > > > + u8 res1[0x10];
> > > > > > > + __be32 tmhtcrh; /* High Temperature Capture
> Register
> > > > */
> > > > > > > + __be32 tmhtcrl; /* Low Temperature Capture
> Register
> > > > */
> > > > > > > + u8 res2[0x8];
> > > > > > > + __be32 tmhtitr; /* High Temperature Immediate
> > > > Threshold */
> > > > > > > + __be32 tmhtatr; /* High Temperature Average
> Threshold
> > > > */
> > > > > > > + __be32 tmhtactr; /* High Temperature Average Crit
> Threshold
> > > > */
> > > > > > > + u8 res3[0x24];
> > > > > > > + __be32 ttcfgr; /* Temperature Configuration
> Register
> > > > */
> > > > > > > + __be32 tscfgr; /* Sensor Configuration
> Register */
> > > > > > > + u8 res4[0x78];
> > > > > > > + struct qoriq_tmu_site_regs site[SITES_MAX];
> > > > > > > + u8 res5[0x9f8];
> > > > > > > + __be32 ipbrr0; /* IP Block Revision Register 0
> */
> > > > > > > + __be32 ipbrr1; /* IP Block Revision Register 1
> */
> > > > > > > + u8 res6[0x310];
> > > > > > > + __be32 ttr0cr; /* Temperature Range 0 Control
> > > > Register */
> > > > > > > + __be32 ttr1cr; /* Temperature Range 1 Control
> > > > Register */
> > > > > > > + __be32 ttr2cr; /* Temperature Range 2 Control
> > > > Register */
> > > > > > > + __be32 ttr3cr; /* Temperature Range 3 Control
> > > > Register */
> > > > > > > +};
> > > > > > > +
> > > > > > > +/*
> > > > > > > + * Thermal zone data
> > > > > > > + */
> > > > > > > +struct qoriq_tmu_data {
> > > > > > > + struct thermal_zone_device *tz;
> > > > > > > + struct thermal_cooling_device *cdev;
> > > > > > > + enum thermal_device_mode mode;
> > > > > > > + unsigned long temp_passive;
> > > > > > > + unsigned long temp_critical;
> > > > > > > + struct qoriq_tmu_regs __iomem *regs; };
> > > > > > > +
> > > > > > > +static int tmu_get_mode(struct thermal_zone_device *tz,
> > > > > > > + enum thermal_device_mode *mode) {
> > > > > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > > > > +
> > > > > > > + *mode =3D data->mode;
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int tmu_set_mode(struct thermal_zone_device *tz,
> > > > > > > + enum thermal_device_mode mode) {
> > > > > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > > > > +
> > > > > > > + if (mode =3D=3D THERMAL_DEVICE_ENABLED) {
> > > > > > > + tz->polling_delay =3D TMU_POLLING_DELAY;
> > > > > > > + tz->passive_delay =3D TMU_PASSIVE_DELAY;
> > > > > > > + thermal_zone_device_update(tz);
> > > > > > > + } else {
> > > > > > > + tz->polling_delay =3D 0;
> > > > > > > + tz->passive_delay =3D 0;
> > > > > > > + }
> > > > > > > +
> > > > > > > + data->mode =3D mode;
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int tmu_get_temp(struct thermal_zone_device *tz,
> > > > > > > +unsigned long
> > > > > > > +*temp) {
> > > > > > > + u8 val;
> > > > > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > > > > +
> > > > > > > + val =3D ioread32be(&data->regs->site[0].tritsr);
> > > > > > > + *temp =3D (unsigned long)val * 1000;
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int tmu_get_trip_type(struct thermal_zone_device
> > > > > > > +*tz, int
> > > > trip,
> > > > > > > + enum thermal_trip_type *type) {
> > > > > > > + *type =3D (trip =3D=3D TMU_TRIP_PASSIVE) ?
> THERMAL_TRIP_PASSIVE :
> > > > > > > + THERMAL_TRIP_CRITICAL;
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int tmu_get_trip_temp(struct thermal_zone_device
> > > > > > > +*tz, int
> > > > trip,
> > > > > > > + unsigned long *temp) {
> > > > > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > > > > +
> > > > > > > + *temp =3D (trip =3D=3D TMU_TRIP_PASSIVE) ? data-
> >temp_passive :
> > > > > > > + data->temp_critical;
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int tmu_get_crit_temp(struct thermal_zone_device *tz,
> > > > > > > + unsigned long *temp) {
> > > > > > > + struct qoriq_tmu_data *data =3D tz->devdata;
> > > > > > > +
> > > > > > > + *temp =3D data->temp_critical;
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int tmu_bind(struct thermal_zone_device *tz,
> > > > > > > + struct thermal_cooling_device *cdev) {
> > > > > > > + int ret;
> > > > > > > +
> > > > > > > + ret =3D thermal_zone_bind_cooling_device(tz,
> > > > > > > +TMU_TRIP_PASSIVE,
> > > > cdev,
> > > > > > > + THERMAL_NO_LIMIT,
> > > > > > > + THERMAL_NO_LIMIT,
> > > > > > > + THERMAL_WEIGHT_DEFAULT);
> > > > > > > + if (ret) {
> > > > > > > + dev_err(&tz->device,
> > > > > > > + "Binding zone %s with cdev %s failed:%d\n",
> > > > > > > + tz->type, cdev->type, ret);
> > > > > > > + }
> > > > > > > +
> > > > > > > + return ret;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int tmu_unbind(struct thermal_zone_device *tz,
> > > > > > > + struct thermal_cooling_device *cdev) {
> > > > > > > + int ret;
> > > > > > > +
> > > > > > > + ret =3D thermal_zone_unbind_cooling_device(tz,
> > > > > > > +TMU_TRIP_PASSIVE,
> > > > > > cdev);
> > > > > > > + if (ret) {
> > > > > > > + dev_err(&tz->device,
> > > > > > > + "Unbinding zone %s with cdev %s
> failed:%d\n",
> > > > > > > + tz->type, cdev->type, ret);
> > > > > > > + }
> > > > > > > +
> > > > > > > + return ret;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int qoriq_tmu_calibration(struct platform_device
> *pdev) {
> > > > > > > + int i, val, len;
> > > > > > > + u32 range[4];
> > > > > > > + const __be32 *calibration;
> > > > > > > + struct device_node *node =3D pdev->dev.of_node;
> > > > > > > + struct qoriq_tmu_data *data =3D dev_get_drvdata(&pdev-
> >dev);
> > > > > > > +
> > > > > > > + /* Disable monitoring before calibration */
> > > > > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > > > > +
> > > > > > > + if (of_property_read_u32_array(node, "fsl,tmu-range",
> > > > > > > +range,
> > > > 4))
> > > > > > > + return -1;
> > > > > > > +
> > > > > > > + /* Init temperature range registers */
> > > > > > > + iowrite32be(range[0], &data->regs->ttr0cr);
> > > > > > > + iowrite32be(range[1], &data->regs->ttr1cr);
> > > > > > > + iowrite32be(range[2], &data->regs->ttr2cr);
> > > > > > > + iowrite32be(range[3], &data->regs->ttr3cr);
> > > > > > > +
> > > > > > > + calibration =3D of_get_property(node, "fsl,tmu-
> calibration",
> > > > &len);
> > > > > > > + if (calibration =3D=3D NULL)
> > > > > > > + return -1;
> > > > > > > +
> > > > > > > + for (i =3D 0; i < len; i +=3D 8, calibration +=3D 2) {
> > > > > > > + val =3D (int)of_read_number(calibration, 1);
> > > > > > > + iowrite32be(val, &data->regs->ttcfgr);
> > > > > > > + val =3D (int)of_read_number(calibration + 1, 1);
> > > > > > > + iowrite32be(val, &data->regs->tscfgr);
> > > > > > > + }
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void qoriq_tmu_init_device(struct qoriq_tmu_data
> *data) {
> > > > > > > + /* Disable interrupt, using polling instead */
> > > > > > > + iowrite32be(TIER_DISABLE, &data->regs->tier);
> > > > > > > +
> > > > > > > + /* Set update_interval */
> > > > > > > + iowrite32be(TMTMIR_DEFAULT, &data->regs->tmtmir);
> > > > > > > +
> > > > > > > + /* Enable monitoring */
> > > > > > > + iowrite32be(TMR_ALL, &data->regs->tmr); }
> > > > > > > +
> > > > > > > +static struct thermal_zone_device_ops tmu_tz_ops =3D {
> > > > > > > + .bind =3D tmu_bind,
> > > > > > > + .unbind =3D tmu_unbind,
> > > > > > > + .get_temp =3D tmu_get_temp,
> > > > > > > + .get_mode =3D tmu_get_mode,
> > > > > > > + .set_mode =3D tmu_set_mode,
> > > > > > > + .get_trip_type =3D tmu_get_trip_type,
> > > > > > > + .get_trip_temp =3D tmu_get_trip_temp,
> > > > > > > + .get_crit_temp =3D tmu_get_crit_temp, };
> > > > > > > +
> > > > > > > +static int qoriq_tmu_probe(struct platform_device *pdev) {
> > > > > > > + int ret;
> > > > > > > + struct cpumask clip_cpus;
> > > > > > > + struct qoriq_tmu_data *data;
> > > > > > > +
> > > > > > > + if (!cpufreq_get_current_driver()) {
> > > > > > > + dev_dbg(&pdev->dev, "No cpufreq driver yet\n");
> > > > > > > + return -EPROBE_DEFER;
> > > > > > > + }
> > > > > > > +
> > > > > > > + if (!pdev->dev.of_node) {
> > > > > > > + dev_err(&pdev->dev, "Device OF-Node is NULL");
> > > > > > > + return -EFAULT;
> > > > > > > + }
> > > > > > > +
> > > > > > > + data =3D devm_kzalloc(&pdev->dev, sizeof(struct
> qoriq_tmu_data),
> > > > > > > + GFP_KERNEL);
> > > > > > > + if (!data)
> > > > > > > + return -ENOMEM;
> > > > > > > +
> > > > > > > + dev_set_drvdata(&pdev->dev, data);
> > > > > > > + data->regs =3D of_iomap(pdev->dev.of_node, 0);
> > > > > > > +
> > > > > > > + if (!data->regs) {
> > > > > > > + dev_err(&pdev->dev, "Failed to get memory
> region\n");
> > > > > > > + ret =3D -ENODEV;
> > > > > > > + goto err_iomap;
> > > > > > > + }
> > > > > > > +
> > > > > > > + ret =3D qoriq_tmu_calibration(pdev); /* TMU calibration
> */
> > > > > > > + if (ret < 0) {
> > > > > > > + dev_err(&pdev->dev, "TMU calibration failed.\n");
> > > > > > > + ret =3D -ENODEV;
> > > > > > > + goto err_iomap;
> > > > > > > + }
> > > > > > > +
> > > > > > > + qoriq_tmu_init_device(data); /* TMU initialization */
> > > > > > > +
> > > > > > > + cpumask_setall(&clip_cpus);
> > > > > > > + data->cdev =3D cpufreq_cooling_register(&clip_cpus);
> > > > > > > + if (IS_ERR(data->cdev)) {
> > > > > > > + ret =3D PTR_ERR(data->cdev);
> > > > > > > + dev_err(&data->cdev->device,
> > > > > > > + "Failed to register cpufreq cooling
> device: %d\n",
> > > > ret);
> > > > > > > + goto err_cooling;
> > > > > > > + }
> > > > > > > +
> > > > > > > + data->temp_passive =3D TMU_TEMP_PASSIVE;
> > > > > > > + data->temp_critical =3D TMU_TEMP_CRITICAL;
> > > > > > > + data->tz =3D
> > > > > > > +thermal_zone_device_register("tmu_thermal_zone",
> > > > > >
> > > > > > Any specific reason why not using OF thermal?
> > > > > >
> > > > > > > + TMU_TRIP_NUM,
> > > > > > > + 0, data,
> > > > > > > + &tmu_tz_ops, NULL,
> > > > > > > + TMU_PASSIVE_DELAY,
> > > > > > > + TMU_POLLING_DELAY);
> > > > > > > +
> > > > > > > + 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_thermal;
> > > > > > > + }
> > > > > > > +
> > > > > > > + data->mode =3D THERMAL_DEVICE_ENABLED;
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +
> > > > > > > +err_thermal:
> > > > > > > + cpufreq_cooling_unregister(data->cdev);
> > > > > > > +
> > > > > > > +err_cooling:
> > > > > > > + iounmap(data->regs);
> > > > > > > +
> > > > > > > +err_iomap:
> > > > > > > + dev_set_drvdata(&pdev->dev, NULL);
> > > > > > > + devm_kfree(&pdev->dev, data);
> > > > > > > +
> > > > > > > + return ret;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int qoriq_tmu_remove(struct platform_device *pdev) {
> > > > > > > + struct qoriq_tmu_data *data =3D dev_get_drvdata(&pdev-
> >dev);
> > > > > > > +
> > > > > > > + /* Disable monitoring */
> > > > > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > > > > +
> > > > > > > + thermal_zone_device_unregister(data->tz);
> > > > > > > + cpufreq_cooling_unregister(data->cdev);
> > > > > > > + iounmap(data->regs);
> > > > > > > +
> > > > > > > + dev_set_drvdata(&pdev->dev, NULL);
> > > > > > > + devm_kfree(&pdev->dev, data);
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +#ifdef CONFIG_PM_SLEEP
> > > > > > > +static int qoriq_tmu_suspend(struct device *dev) {
> > > > > > > + struct qoriq_tmu_data *data =3D dev_get_drvdata(dev);
> > > > > > > +
> > > > > > > + /* Disable monitoring */
> > > > > > > + iowrite32be(TMR_DISABLE, &data->regs->tmr);
> > > > > > > + data->mode =3D THERMAL_DEVICE_DISABLED;
> > > > > > > +
> > > > > > > + return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int qoriq_tmu_resume(struct device *dev) {
> > > > > > > + struct qoriq_tmu_data *data =3D dev_get_drvdata(dev);
> > > > > > > +
> > > > > > > + /* Enable monitoring */
> > > > > > > + iowrite32be(TMR_ALL, &data->regs->tmr);
> > > > > > > + data->mode =3D THERMAL_DEVICE_ENABLED;
> > > > > > > +
> > > > > > > + 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", },
> > > > > > > + {},
> > > > > > > +};
> > > > > > > +
> > > > > > > +static struct platform_driver qoriq_tmu =3D {
> > > > > > > + .driver =3D {
> > > > > > > + .owner =3D THIS_MODULE,
> > > > > > > + .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@freescale.com>");
> > > > > > > +MODULE_DESCRIPTION("Freescale 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] 8+ messages in thread
end of thread, other threads:[~2015-08-14 7:14 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-07-29 6:19 [PATCH V2] QorIQ/TMU: add thermal management support based on TMU Jia Hongtao
2015-07-30 6:55 ` Eduardo Valentin
2015-07-30 8:13 ` Hongtao Jia
2015-08-05 19:42 ` Eduardo Valentin
2015-08-07 8:14 ` Hongtao Jia
2015-08-14 3:15 ` Hongtao Jia
2015-08-14 4:29 ` Eduardo Valentin
2015-08-14 6:40 ` Hongtao Jia
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).