* [RFC PATCH v4] ARM: imx: Add basic imx6q thermal management
@ 2012-01-19 2:27 Robert Lee
2012-01-19 2:27 ` Robert Lee
0 siblings, 1 reply; 3+ messages in thread
From: Robert Lee @ 2012-01-19 2:27 UTC (permalink / raw)
To: linux-arm-kernel
Based on v3.2 plus recently submitted cpu_cooling functionality here:
http://www.spinics.net/lists/linux-pm/msg26500.html
link to previous submissions:
v3: http://www.spinics.net/lists/arm-kernel/msg155955.html
v2: http://www.spinics.net/lists/arm-kernel/msg155790.html
v1: http://www.spinics.net/lists/arm-kernel/msg155111.html
Changes in v4:
1. Removed bad suspend/resume assignment into thermal class. After further
examination and discussion with SoC designers, a sequence is now used
for making measurements that is is unaffected by system suspendresumes.
Temp Sensor automatically powers off in hardware during the low power mode
caused by a system suspend.
2. Moved some structures from static to dynamic allocation.
3. Added some noise handling to temperatuer sensor readings.
Changes in v3:
1. Fixed the various issues pointed out in v2
2. Made other code cleanup and a bit of re-organizing
3. Removed unecessary platform driver and device.
Changes in v2:
1. Cleaned up some style issues pointed out in v1
2. Made various other code cleanup and re-organizing
3. Added temperature sensor calibration
4. Created platform driver and device to hook into pm suspend.
Performed some basic testing to ensure proper cooling operating. If
you want to test this, full testing requires imx6q cpufreq
implementation (not yet in v3.2) and requires an imx6q part that has
temperature sensor calibration fuses correctly burned.
Robert Lee (1):
ARM: imx: Add basic imx6q thermal management
arch/arm/boot/dts/imx6q.dtsi | 1 +
drivers/thermal/Kconfig | 6 +
drivers/thermal/Makefile | 1 +
drivers/thermal/imx6q_thermal.c | 540 +++++++++++++++++++++++++++++++++++++++
4 files changed, 548 insertions(+), 0 deletions(-)
create mode 100644 drivers/thermal/imx6q_thermal.c
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RFC PATCH v4] ARM: imx: Add basic imx6q thermal management
2012-01-19 2:27 [RFC PATCH v4] ARM: imx: Add basic imx6q thermal management Robert Lee
@ 2012-01-19 2:27 ` Robert Lee
2012-06-13 8:46 ` Dirk Behme
0 siblings, 1 reply; 3+ messages in thread
From: Robert Lee @ 2012-01-19 2:27 UTC (permalink / raw)
To: linux-arm-kernel
Adds support for temperature sensor readings, registers with common
thermal framework, and uses the new common cpu_cooling interface.
Signed-off-by: Robert Lee <rob.lee@linaro.org>
---
arch/arm/boot/dts/imx6q.dtsi | 1 +
drivers/thermal/Kconfig | 6 +
drivers/thermal/Makefile | 1 +
drivers/thermal/imx6q_thermal.c | 540 +++++++++++++++++++++++++++++++++++++++
4 files changed, 548 insertions(+), 0 deletions(-)
create mode 100644 drivers/thermal/imx6q_thermal.c
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 7dda599..d62b88d 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -508,6 +508,7 @@
};
ocotp at 021bc000 {
+ compatible = "fsl,imx6q-ocotp";
reg = <0x021bc000 0x4000>;
};
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 298c1cd..dd8cede 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -29,3 +29,9 @@ config CPU_THERMAL
This will be useful for platforms using the generic thermal interface
and not the ACPI interface.
If you want this support, you should say Y or M here.
+
+config IMX6Q_THERMAL
+ bool "IMX6Q Thermal interface support"
+ depends on THERMAL && CPU_THERMAL
+ help
+ Adds thermal management for IMX6Q.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 655cbc4..e2bcffe 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_THERMAL) += thermal_sys.o
obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
+obj-$(CONFIG_IMX6Q_THERMAL) += imx6q_thermal.o
diff --git a/drivers/thermal/imx6q_thermal.c b/drivers/thermal/imx6q_thermal.c
new file mode 100644
index 0000000..7d520c6
--- /dev/null
+++ b/drivers/thermal/imx6q_thermal.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* i.MX6Q Thermal Implementation */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/thermal.h>
+#include <linux/io.h>
+#include <linux/syscalls.h>
+#include <linux/cpufreq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/smp.h>
+#include <linux/cpu_cooling.h>
+#include <linux/platform_device.h>
+
+/* register define of anatop */
+#define HW_ANADIG_ANA_MISC0 0x00000150
+#define HW_ANADIG_ANA_MISC0_SET 0x00000154
+#define HW_ANADIG_ANA_MISC0_CLR 0x00000158
+#define HW_ANADIG_ANA_MISC0_TOG 0x0000015c
+#define BM_ANADIG_ANA_MISC0_REFTOP_SELBIASOFF 0x00000008
+
+#define HW_ANADIG_TEMPSENSE0 0x00000180
+#define HW_ANADIG_TEMPSENSE0_SET 0x00000184
+#define HW_ANADIG_TEMPSENSE0_CLR 0x00000188
+#define HW_ANADIG_TEMPSENSE0_TOG 0x0000018c
+
+#define BP_ANADIG_TEMPSENSE0_TEMP_VALUE 8
+#define BM_ANADIG_TEMPSENSE0_TEMP_VALUE 0x000FFF00
+#define BM_ANADIG_TEMPSENSE0_FINISHED 0x00000004
+#define BM_ANADIG_TEMPSENSE0_MEASURE_TEMP 0x00000002
+#define BM_ANADIG_TEMPSENSE0_POWER_DOWN 0x00000001
+
+#define HW_ANADIG_TEMPSENSE1 0x00000190
+#define HW_ANADIG_TEMPSENSE1_SET 0x00000194
+#define HW_ANADIG_TEMPSENSE1_CLR 0x00000198
+#define BP_ANADIG_TEMPSENSE1_MEASURE_FREQ 0
+#define BM_ANADIG_TEMPSENSE1_MEASURE_FREQ 0x0000FFFF
+
+#define HW_OCOTP_ANA1 0x000004E0
+
+#define IMX6Q_THERMAL_POLLING_FREQUENCY_MS 1000
+
+#define IMX6Q_THERMAL_ACT_TRP_PTS 3
+/* assumption: always one critical trip point */
+#define IMX6Q_THERMAL_TOTAL_TRP_PTS (IMX6Q_THERMAL_ACT_TRP_PTS + 1)
+
+struct th_sys_trip_point {
+ u32 temp; /* in celcius */
+ enum thermal_trip_type type;
+};
+
+struct imx6q_sensor_data {
+ int c1, c2;
+ char name[16];
+ u8 meas_delay; /* in milliseconds */
+ u32 noise_margin; /* in millicelcius */
+ int last_temp; /* in millicelcius */
+ /*
+ * When noise filtering, if consecutive measurements are each higher
+ * up to consec_high_limit times, assume changing temperature readings
+ * to be valid and not noise.
+ */
+ u32 consec_high_limit;
+};
+
+struct imx6q_thermal_data {
+ struct th_sys_trip_point trp_pts[IMX6Q_THERMAL_TOTAL_TRP_PTS];
+ struct freq_pctg_table freq_tab[IMX6Q_THERMAL_ACT_TRP_PTS];
+};
+
+struct imx6q_thermal_zone {
+ struct thermal_zone_device *therm_dev;
+ struct thermal_cooling_device *cool_dev;
+ struct imx6q_sensor_data *sensor_data;
+ struct imx6q_thermal_data *thermal_data;
+ struct thermal_zone_device_ops dev_ops;
+};
+
+static struct imx6q_sensor_data g_sensor_data __initdata = {
+ .name = "imx6q-temp_sens",
+ .meas_delay = 1, /* in milliseconds */
+ .noise_margin = 3000,
+ .consec_high_limit = 3,
+};
+
+/*
+ * This data defines the various trip points that will trigger action
+ * when crossed.
+ */
+static struct imx6q_thermal_data g_thermal_data __initdata = {
+ .trp_pts[0] = {
+ .temp = 85000,
+ .type = THERMAL_TRIP_STATE_ACTIVE,
+ },
+ .freq_tab[0] = {
+ .freq_clip_pctg[0] = 5,
+ },
+ .trp_pts[1] = {
+ .temp = 90000,
+ .type = THERMAL_TRIP_STATE_ACTIVE,
+ },
+ .freq_tab[1] = {
+ .freq_clip_pctg[0] = 45,
+ },
+ .trp_pts[2] = {
+ .temp = 95000,
+ .type = THERMAL_TRIP_STATE_ACTIVE,
+ },
+ .freq_tab[2] = {
+ .freq_clip_pctg[0] = 99,
+ },
+ .trp_pts[3] = {
+ .temp = 100000,
+ .type = THERMAL_TRIP_CRITICAL,
+ },
+};
+
+static int th_sys_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp);
+
+static struct imx6q_thermal_zone *th_zone;
+static void __iomem *anatop_base, *ocotp_base;
+
+static inline int imx6q_get_temp(int *temp, struct imx6q_sensor_data *sd)
+{
+ unsigned int n_meas;
+ unsigned int reg;
+
+ do {
+ /*
+ * For now we only using single measure. Every time we measure
+ * the temperature, we will power on/down the anadig module
+ */
+ writel_relaxed(BM_ANADIG_TEMPSENSE0_POWER_DOWN,
+ anatop_base + HW_ANADIG_TEMPSENSE0_CLR);
+
+ writel_relaxed(BM_ANADIG_TEMPSENSE0_MEASURE_TEMP,
+ anatop_base + HW_ANADIG_TEMPSENSE0_SET);
+
+ /*
+ * According to imx6q temp sensor designers,
+ * it may require up to ~17us to complete
+ * a measurement. But this timing isn't checked on every part
+ * and specified in the datasheet, so we check the
+ * 'finished' status bit to be sure of completion.
+ */
+ msleep(sd->meas_delay);
+
+ reg = readl_relaxed(anatop_base + HW_ANADIG_TEMPSENSE0);
+
+ writel_relaxed(BM_ANADIG_TEMPSENSE0_MEASURE_TEMP,
+ anatop_base + HW_ANADIG_TEMPSENSE0_CLR);
+
+ writel_relaxed(BM_ANADIG_TEMPSENSE0_POWER_DOWN,
+ anatop_base + HW_ANADIG_TEMPSENSE0_SET);
+
+ } while (!(reg & BM_ANADIG_TEMPSENSE0_FINISHED) &&
+ (reg & BM_ANADIG_TEMPSENSE0_TEMP_VALUE));
+
+ n_meas = (reg & BM_ANADIG_TEMPSENSE0_TEMP_VALUE)
+ >> BP_ANADIG_TEMPSENSE0_TEMP_VALUE;
+
+ /* See imx6q_thermal_process_fuse_data for forumla derivation. */
+ *temp = sd->c2 + (sd->c1 * n_meas);
+
+ pr_debug("Temperature: %d\n", *temp / 1000);
+
+ return 0;
+}
+
+static int th_sys_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ int i, total = 0, tmp = 0;
+ const u8 loop = 5;
+ u32 consec_high = 0;
+
+ struct imx6q_sensor_data *sd;
+
+ sd = ((struct imx6q_thermal_zone *)thermal->devdata)->sensor_data;
+
+ /*
+ * Measure and handle noise
+ *
+ * While the imx6q temperature sensor is designed to minimize being
+ * affected by system noise, it's safest to run sanity checks and
+ * perform any necessary filitering.
+ */
+ for (i = 0; (sd->noise_margin) && (i < loop); i++) {
+ imx6q_get_temp(&tmp, sd);
+
+ if ((abs(tmp - sd->last_temp) <= sd->noise_margin) ||
+ (consec_high >= sd->consec_high_limit)) {
+ sd->last_temp = tmp;
+ i = 0;
+ break;
+ }
+ if (tmp > sd->last_temp)
+ consec_high++;
+
+ /*
+ * ignore first measurement as the previous measurement was
+ * a long time ago.
+ */
+ if (i)
+ total += tmp;
+
+ sd->last_temp = tmp;
+ }
+
+ if (sd->noise_margin && i)
+ tmp = total / (loop - 1);
+
+ /*
+ * The thermal framework code stores temperature in unsigned long. Also,
+ * it has references to "millicelcius" which limits the lowest
+ * temperature possible (compared to Kelvin).
+ */
+ if (tmp > 0)
+ *temp = tmp;
+ else
+ *temp = 0;
+
+ return 0;
+}
+
+static int th_sys_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ *mode = THERMAL_DEVICE_ENABLED;
+ return 0;
+}
+
+static int th_sys_get_trip_type(struct thermal_zone_device *thermal, int trip,
+ enum thermal_trip_type *type)
+{
+ struct imx6q_thermal_data *p;
+
+ if (trip >= thermal->trips)
+ return -EINVAL;
+
+ p = ((struct imx6q_thermal_zone *)thermal->devdata)->thermal_data;
+
+ *type = p->trp_pts[trip].type;
+
+ return 0;
+}
+
+static int th_sys_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+ unsigned long *temp)
+{
+ struct imx6q_thermal_data *p;
+
+ if (trip >= thermal->trips)
+ return -EINVAL;
+
+ p = ((struct imx6q_thermal_zone *)thermal->devdata)->thermal_data;
+
+ *temp = p->trp_pts[trip].temp;
+
+ return 0;
+}
+
+static int th_sys_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ int i;
+ struct imx6q_thermal_data *p;
+
+ p = ((struct imx6q_thermal_zone *)thermal->devdata)->thermal_data;
+
+ for (i = thermal->trips ; i > 0 ; i--) {
+ if (p->trp_pts[i-1].type == THERMAL_TRIP_CRITICAL) {
+ *temp = p->trp_pts[i-1].temp;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int th_sys_bind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev)
+{
+ int i;
+ struct thermal_cooling_device *cd;
+
+ cd = ((struct imx6q_thermal_zone *)thermal->devdata)->cool_dev;
+
+ /* if the cooling device is the one from imx6 bind it */
+ if (cdev != cd)
+ return 0;
+
+ for (i = 0; i < IMX6Q_THERMAL_ACT_TRP_PTS; i++) {
+ if (thermal_zone_bind_cooling_device(thermal, i, cdev)) {
+ pr_err("error binding cooling dev\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int th_sys_unbind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev)
+{
+ int i;
+
+ struct thermal_cooling_device *cd;
+
+ cd = ((struct imx6q_thermal_zone *)thermal->devdata)->cool_dev;
+
+ if (cdev != cd)
+ return 0;
+
+ for (i = 0; i < IMX6Q_THERMAL_ACT_TRP_PTS; i++) {
+ if (thermal_zone_unbind_cooling_device(thermal, i, cdev)) {
+ pr_err("error unbinding cooling dev\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops g_dev_ops __initdata = {
+ .bind = th_sys_bind,
+ .unbind = th_sys_unbind,
+ .get_temp = th_sys_get_temp,
+ .get_mode = th_sys_get_mode,
+ .get_trip_type = th_sys_get_trip_type,
+ .get_trip_temp = th_sys_get_trip_temp,
+ .get_crit_temp = th_sys_get_crit_temp,
+};
+
+static int imx6q_thermal_process_fuse_data(unsigned int fuse_data,
+ struct imx6q_sensor_data *sd)
+{
+ int t1, t2, n1, n2;
+
+ if (fuse_data == 0 || fuse_data == 0xffffffff)
+ return -EINVAL;
+
+ /*
+ * Fuse data layout:
+ * [31:20] sensor value @ 25C
+ * [19:8] sensor value of hot
+ * [7:0] hot temperature value
+ */
+ n1 = fuse_data >> 20;
+ n2 = (fuse_data & 0xfff00) >> 8;
+ t2 = fuse_data & 0xff;
+ t1 = 25; /* t1 always 25C */
+
+ pr_debug(" -- temperature sensor calibration data --\n");
+ pr_debug("HW_OCOTP_ANA1: %x\n", fuse_data);
+ pr_debug("n1: %d\nn2: %d\nt1: %d\nt2: %d\n", n1, n2, t1, t2);
+
+ /*
+ * Derived from linear interpolation,
+ * Tmeas = T2 + (Nmeas - N2) * (T1 - T2) / (N1 - N2)
+ * We want to reduce this down to the minimum computation necessary
+ * for each temperature read. Also, we want Tmeas in millicelcius
+ * and we don't want to lose precision from integer division. So...
+ * milli_Tmeas = 1000 * T2 + 1000 * (Nmeas - N2) * (T1 - T2) / (N1 - N2)
+ * Let constant c1 = 1000 * (T1 - T2) / (N1 - N2)
+ * milli_Tmeas = (1000 * T2) + c1 * (Nmeas - N2)
+ * milli_Tmeas = (1000 * T2) + (c1 * Nmeas) - (c1 * N2)
+ * Let constant c2 = (1000 * T2) - (c1 * N2)
+ * milli_Tmeas = c2 + (c1 * Nmeas)
+ */
+ sd->c1 = (1000 * (t1 - t2)) / (n1 - n2);
+ sd->c2 = (1000 * t2) - (sd->c1 * n2);
+
+ pr_debug("c1: %i\n", sd->c1);
+ pr_debug("c2: %i\n", sd->c2);
+
+ return 0;
+}
+
+static void __exit imx6q_thermal_exit(void)
+{
+ if (th_zone && th_zone->therm_dev)
+ thermal_zone_device_unregister(th_zone->therm_dev);
+
+ if (th_zone && th_zone->cool_dev)
+ cpufreq_cooling_unregister();
+
+ kfree(th_zone->sensor_data);
+ kfree(th_zone->thermal_data);
+ kfree(th_zone);
+
+ if (ocotp_base)
+ iounmap(ocotp_base);
+
+ if (anatop_base)
+ iounmap(anatop_base);
+
+ pr_info("i.MX6Q: Kernel Thermal management unregistered\n");
+}
+
+static int __init imx6q_thermal_init(void)
+{
+ struct device_node *np_ocotp, *np_anatop;
+ unsigned int fuse_data;
+ int ret;
+
+ np_ocotp = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp");
+ np_anatop = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
+
+ if (!(np_ocotp && np_anatop))
+ return -ENXIO; /* not a compatible platform */
+
+ ocotp_base = of_iomap(np_ocotp, 0);
+
+ if (!ocotp_base) {
+ pr_err("Could not retrieve ocotp-base\n");
+ ret = -ENXIO;
+ goto err_unregister;
+ }
+
+ anatop_base = of_iomap(np_anatop, 0);
+
+ if (!anatop_base) {
+ pr_err("Could not retrieve anantop-base\n");
+ ret = -ENXIO;
+ goto err_unregister;
+ }
+
+ fuse_data = readl_relaxed(ocotp_base + HW_OCOTP_ANA1);
+
+ th_zone = kzalloc(sizeof(struct imx6q_thermal_zone), GFP_KERNEL);
+ if (!th_zone) {
+ ret = -ENOMEM;
+ goto err_unregister;
+ }
+
+ th_zone->dev_ops = g_dev_ops;
+
+ th_zone->thermal_data =
+ kzalloc(sizeof(struct imx6q_thermal_data), GFP_KERNEL);
+
+ if (!th_zone->thermal_data) {
+ ret = -ENOMEM;
+ goto err_unregister;
+ }
+
+ memcpy(th_zone->thermal_data, &g_thermal_data,
+ sizeof(struct imx6q_thermal_data));
+
+ th_zone->sensor_data =
+ kzalloc(sizeof(struct imx6q_sensor_data), GFP_KERNEL);
+
+ if (!th_zone->sensor_data) {
+ ret = -ENOMEM;
+ goto err_unregister;
+ }
+
+ memcpy(th_zone->sensor_data, &g_sensor_data,
+ sizeof(struct imx6q_sensor_data));
+
+ ret = imx6q_thermal_process_fuse_data(fuse_data, th_zone->sensor_data);
+
+ if (ret) {
+ pr_err("Invalid temperature calibration data.\n");
+ goto err_unregister;
+ }
+
+ if (!th_zone->sensor_data->meas_delay)
+ th_zone->sensor_data->meas_delay = 1;
+
+ /* Make sure sensor is in known good state for measurements */
+ writel_relaxed(BM_ANADIG_TEMPSENSE0_POWER_DOWN,
+ anatop_base + HW_ANADIG_TEMPSENSE0_CLR);
+
+ writel_relaxed(BM_ANADIG_TEMPSENSE0_MEASURE_TEMP,
+ anatop_base + HW_ANADIG_TEMPSENSE0_CLR);
+
+ writel_relaxed(BM_ANADIG_TEMPSENSE1_MEASURE_FREQ,
+ anatop_base + HW_ANADIG_TEMPSENSE1_CLR);
+
+ writel_relaxed(BM_ANADIG_ANA_MISC0_REFTOP_SELBIASOFF,
+ anatop_base + HW_ANADIG_ANA_MISC0_SET);
+
+ writel_relaxed(BM_ANADIG_TEMPSENSE0_POWER_DOWN,
+ anatop_base + HW_ANADIG_TEMPSENSE0_SET);
+
+ th_zone->cool_dev = cpufreq_cooling_register(
+ (struct freq_pctg_table *)th_zone->thermal_data->freq_tab,
+ IMX6Q_THERMAL_ACT_TRP_PTS, cpumask_of(0));
+
+ if (IS_ERR(th_zone->cool_dev)) {
+ pr_err("Failed to register cpufreq cooling device\n");
+ ret = -EINVAL;
+ goto err_unregister;
+ }
+
+ th_zone->therm_dev = thermal_zone_device_register(
+ th_zone->sensor_data->name, IMX6Q_THERMAL_TOTAL_TRP_PTS,
+ th_zone, &th_zone->dev_ops, 0, 0, 0,
+ IMX6Q_THERMAL_POLLING_FREQUENCY_MS);
+
+ if (IS_ERR(th_zone->therm_dev)) {
+ pr_err("Failed to register thermal zone device\n");
+ ret = -EINVAL;
+ goto err_unregister;
+ }
+
+ return 0;
+
+err_unregister:
+ imx6q_thermal_exit();
+ return ret;
+}
+
+module_init(imx6q_thermal_init);
+module_exit(imx6q_thermal_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("i.MX6Q SoC thermal driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx6q-thermal");
--
1.7.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH v4] ARM: imx: Add basic imx6q thermal management
2012-01-19 2:27 ` Robert Lee
@ 2012-06-13 8:46 ` Dirk Behme
0 siblings, 0 replies; 3+ messages in thread
From: Dirk Behme @ 2012-06-13 8:46 UTC (permalink / raw)
To: linux-arm-kernel
On 19.01.2012 03:27, Robert Lee wrote:
> Adds support for temperature sensor readings, registers with common
> thermal framework, and uses the new common cpu_cooling interface.
>
> Signed-off-by: Robert Lee <rob.lee@linaro.org>
> ---
> arch/arm/boot/dts/imx6q.dtsi | 1 +
> drivers/thermal/Kconfig | 6 +
> drivers/thermal/Makefile | 1 +
> drivers/thermal/imx6q_thermal.c | 540 +++++++++++++++++++++++++++++++++++++++
> 4 files changed, 548 insertions(+), 0 deletions(-)
> create mode 100644 drivers/thermal/imx6q_thermal.c
...
> +static int __init imx6q_thermal_init(void)
> +{
> + struct device_node *np_ocotp, *np_anatop;
> + unsigned int fuse_data;
> + int ret;
> +
> + np_ocotp = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp");
> + np_anatop = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
> +
> + if (!(np_ocotp && np_anatop))
> + return -ENXIO; /* not a compatible platform */
> +
> + ocotp_base = of_iomap(np_ocotp, 0);
> +
> + if (!ocotp_base) {
> + pr_err("Could not retrieve ocotp-base\n");
> + ret = -ENXIO;
> + goto err_unregister;
> + }
> +
> + anatop_base = of_iomap(np_anatop, 0);
> +
> + if (!anatop_base) {
> + pr_err("Could not retrieve anantop-base\n");
> + ret = -ENXIO;
> + goto err_unregister;
> + }
> +
> + fuse_data = readl_relaxed(ocotp_base + HW_OCOTP_ANA1);
To be able to use this on non-calibrated sample devices, we had to add [1].
Best regards
Dirk
[1]
From: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
Subject: [PATCH] ARM: imx6: thermal: Add workaround for missing
calibration data
Sample devices not calibrated in the production don't have the fuses
containing
the calibration data. Hardcode this value if the fuse data read is zero
to be able
to use the thermal functinality on these sample devices, too.
Signed-off-by: Ying-Chun Liu <paul.liu@linaro.org>
Signed-off-by: Philipp Ahmann <pahmann@de.adit-jv.com>
---
drivers/thermal/imx6q_thermal.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/thermal/imx6q_thermal.c
b/drivers/thermal/imx6q_thermal.c
index 76b7ba1..543cd75 100644
--- a/drivers/thermal/imx6q_thermal.c
+++ b/drivers/thermal/imx6q_thermal.c
@@ -446,6 +446,8 @@ static int __init imx6q_thermal_init(void)
}
fuse_data = readl_relaxed(ocotp_base + HW_OCOTP_ANA1);
+ if (fuse_data == 0)
+ fuse_data = 0x5704c67d;
th_zone = kzalloc(sizeof(struct imx6q_thermal_zone), GFP_KERNEL);
if (!th_zone) {
--
1.7.0.4
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2012-06-13 8:46 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-01-19 2:27 [RFC PATCH v4] ARM: imx: Add basic imx6q thermal management Robert Lee
2012-01-19 2:27 ` Robert Lee
2012-06-13 8:46 ` Dirk Behme
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).