diff options
Diffstat (limited to 'target/linux/imx6/patches-3.10/0015-thermal-add-imx-thermal-driver-support.patch')
-rw-r--r-- | target/linux/imx6/patches-3.10/0015-thermal-add-imx-thermal-driver-support.patch | 477 |
1 files changed, 0 insertions, 477 deletions
diff --git a/target/linux/imx6/patches-3.10/0015-thermal-add-imx-thermal-driver-support.patch b/target/linux/imx6/patches-3.10/0015-thermal-add-imx-thermal-driver-support.patch deleted file mode 100644 index 0b7ac7d308..0000000000 --- a/target/linux/imx6/patches-3.10/0015-thermal-add-imx-thermal-driver-support.patch +++ /dev/null @@ -1,477 +0,0 @@ -From ca3de46b50809000b5ba708634e26ad979a4a63a Mon Sep 17 00:00:00 2001 -From: Shawn Guo <shawn.guo@linaro.org> -Date: Mon, 24 Jun 2013 14:30:44 +0800 -Subject: [PATCH] thermal: add imx thermal driver support - -This is based on the initial imx thermal work done by -Rob Lee <rob.lee@linaro.org> (Not sure if the email address is still -valid). Since he is no longer interested in the work and I have -rewritten a significant amount of the code, I just took the authorship -over from him. - -It adds the imx thermal support using Temperature Monitor (TEMPMON) -block found on some Freescale i.MX SoCs. The driver uses syscon regmap -interface to access TEMPMON control registers and calibration data, and -supports cpufreq as the cooling device. - -Signed-off-by: Shawn Guo <shawn.guo@linaro.org> -Signed-off-by: Eduardo Valentin <eduardo.valentin@ti.com> ---- - .../devicetree/bindings/thermal/imx-thermal.txt | 17 + - drivers/thermal/Kconfig | 11 + - drivers/thermal/Makefile | 1 + - drivers/thermal/imx_thermal.c | 397 +++++++++++++++++++++ - 4 files changed, 426 insertions(+) - create mode 100644 Documentation/devicetree/bindings/thermal/imx-thermal.txt - create mode 100644 drivers/thermal/imx_thermal.c - ---- /dev/null -+++ b/Documentation/devicetree/bindings/thermal/imx-thermal.txt -@@ -0,0 +1,17 @@ -+* Temperature Monitor (TEMPMON) on Freescale i.MX SoCs -+ -+Required properties: -+- compatible : "fsl,imx6q-thermal" -+- fsl,tempmon : phandle pointer to system controller that contains TEMPMON -+ control registers, e.g. ANATOP on imx6q. -+- fsl,tempmon-data : phandle pointer to fuse controller that contains TEMPMON -+ calibration data, e.g. OCOTP on imx6q. The details about calibration data -+ can be found in SoC Reference Manual. -+ -+Example: -+ -+tempmon { -+ compatible = "fsl,imx6q-tempmon"; -+ fsl,tempmon = <&anatop>; -+ fsl,tempmon-data = <&ocotp>; -+}; ---- a/drivers/thermal/Kconfig -+++ b/drivers/thermal/Kconfig -@@ -91,6 +91,17 @@ config THERMAL_EMULATION - because userland can easily disable the thermal policy by simply - flooding this sysfs node with low temperature values. - -+config IMX_THERMAL -+ tristate "Temperature sensor driver for Freescale i.MX SoCs" -+ depends on CPU_THERMAL -+ depends on MFD_SYSCON -+ depends on OF -+ help -+ Support for Temperature Monitor (TEMPMON) found on Freescale i.MX SoCs. -+ 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 ---- a/drivers/thermal/Makefile -+++ b/drivers/thermal/Makefile -@@ -21,6 +21,7 @@ obj-$(CONFIG_EXYNOS_THERMAL) += exynos_t - 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_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o - obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o - ---- /dev/null -+++ b/drivers/thermal/imx_thermal.c -@@ -0,0 +1,397 @@ -+/* -+ * Copyright 2013 Freescale Semiconductor, Inc. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ */ -+ -+#include <linux/cpu_cooling.h> -+#include <linux/cpufreq.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/init.h> -+#include <linux/io.h> -+#include <linux/kernel.h> -+#include <linux/mfd/syscon.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <linux/regmap.h> -+#include <linux/slab.h> -+#include <linux/thermal.h> -+#include <linux/types.h> -+ -+#define REG_SET 0x4 -+#define REG_CLR 0x8 -+#define REG_TOG 0xc -+ -+#define MISC0 0x0150 -+#define MISC0_REFTOP_SELBIASOFF (1 << 3) -+ -+#define TEMPSENSE0 0x0180 -+#define TEMPSENSE0_TEMP_CNT_SHIFT 8 -+#define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT) -+#define TEMPSENSE0_FINISHED (1 << 2) -+#define TEMPSENSE0_MEASURE_TEMP (1 << 1) -+#define TEMPSENSE0_POWER_DOWN (1 << 0) -+ -+#define TEMPSENSE1 0x0190 -+#define TEMPSENSE1_MEASURE_FREQ 0xffff -+ -+#define OCOTP_ANA1 0x04e0 -+ -+/* The driver supports 1 passive trip point and 1 critical trip point */ -+enum imx_thermal_trip { -+ IMX_TRIP_PASSIVE, -+ IMX_TRIP_CRITICAL, -+ IMX_TRIP_NUM, -+}; -+ -+/* -+ * It defines the temperature in millicelsius for passive trip point -+ * that will trigger cooling action when crossed. -+ */ -+#define IMX_TEMP_PASSIVE 85000 -+ -+/* -+ * The maximum die temperature on imx parts is 105C, let's give some cushion -+ * for noise and possible temperature rise between measurements. -+ */ -+#define IMX_TEMP_CRITICAL 100000 -+ -+#define IMX_POLLING_DELAY 2000 /* millisecond */ -+#define IMX_PASSIVE_DELAY 1000 -+ -+struct imx_thermal_data { -+ struct thermal_zone_device *tz; -+ struct thermal_cooling_device *cdev; -+ enum thermal_device_mode mode; -+ struct regmap *tempmon; -+ int c1, c2; /* See formula in imx_get_sensor_data() */ -+}; -+ -+static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) -+{ -+ struct imx_thermal_data *data = tz->devdata; -+ struct regmap *map = data->tempmon; -+ static unsigned long last_temp; -+ unsigned int n_meas; -+ u32 val; -+ -+ /* -+ * Every time we measure the temperature, we will power on the -+ * temperature sensor, enable measurements, take a reading, -+ * disable measurements, power off the temperature sensor. -+ */ -+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); -+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); -+ -+ /* -+ * According to the temp sensor designers, it may require up to ~17us -+ * to complete a measurement. -+ */ -+ usleep_range(20, 50); -+ -+ regmap_read(map, TEMPSENSE0, &val); -+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP); -+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); -+ -+ if ((val & TEMPSENSE0_FINISHED) == 0) { -+ dev_dbg(&tz->device, "temp measurement never finished\n"); -+ return -EAGAIN; -+ } -+ -+ n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT; -+ -+ /* See imx_get_sensor_data() for formula derivation */ -+ *temp = data->c2 + data->c1 * n_meas; -+ -+ if (*temp != last_temp) { -+ dev_dbg(&tz->device, "millicelsius: %ld\n", *temp); -+ last_temp = *temp; -+ } -+ -+ return 0; -+} -+ -+static int imx_get_mode(struct thermal_zone_device *tz, -+ enum thermal_device_mode *mode) -+{ -+ struct imx_thermal_data *data = tz->devdata; -+ -+ *mode = data->mode; -+ -+ return 0; -+} -+ -+static int imx_set_mode(struct thermal_zone_device *tz, -+ enum thermal_device_mode mode) -+{ -+ struct imx_thermal_data *data = tz->devdata; -+ -+ if (mode == THERMAL_DEVICE_ENABLED) { -+ tz->polling_delay = IMX_POLLING_DELAY; -+ tz->passive_delay = IMX_PASSIVE_DELAY; -+ } else { -+ tz->polling_delay = 0; -+ tz->passive_delay = 0; -+ } -+ -+ data->mode = mode; -+ thermal_zone_device_update(tz); -+ -+ return 0; -+} -+ -+static int imx_get_trip_type(struct thermal_zone_device *tz, int trip, -+ enum thermal_trip_type *type) -+{ -+ *type = (trip == IMX_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE : -+ THERMAL_TRIP_CRITICAL; -+ return 0; -+} -+ -+static int imx_get_crit_temp(struct thermal_zone_device *tz, -+ unsigned long *temp) -+{ -+ *temp = IMX_TEMP_CRITICAL; -+ return 0; -+} -+ -+static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip, -+ unsigned long *temp) -+{ -+ *temp = (trip == IMX_TRIP_PASSIVE) ? IMX_TEMP_PASSIVE : -+ IMX_TEMP_CRITICAL; -+ return 0; -+} -+ -+static int imx_bind(struct thermal_zone_device *tz, -+ struct thermal_cooling_device *cdev) -+{ -+ int ret; -+ -+ ret = thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev, -+ THERMAL_NO_LIMIT, -+ THERMAL_NO_LIMIT); -+ if (ret) { -+ dev_err(&tz->device, -+ "binding zone %s with cdev %s failed:%d\n", -+ tz->type, cdev->type, ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int imx_unbind(struct thermal_zone_device *tz, -+ struct thermal_cooling_device *cdev) -+{ -+ int ret; -+ -+ ret = thermal_zone_unbind_cooling_device(tz, IMX_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; -+ } -+ -+ return 0; -+} -+ -+static const struct thermal_zone_device_ops imx_tz_ops = { -+ .bind = imx_bind, -+ .unbind = imx_unbind, -+ .get_temp = imx_get_temp, -+ .get_mode = imx_get_mode, -+ .set_mode = imx_set_mode, -+ .get_trip_type = imx_get_trip_type, -+ .get_trip_temp = imx_get_trip_temp, -+ .get_crit_temp = imx_get_crit_temp, -+}; -+ -+static int imx_get_sensor_data(struct platform_device *pdev) -+{ -+ struct imx_thermal_data *data = platform_get_drvdata(pdev); -+ struct regmap *map; -+ int t1, t2, n1, n2; -+ int ret; -+ u32 val; -+ -+ map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, -+ "fsl,tempmon-data"); -+ if (IS_ERR(map)) { -+ ret = PTR_ERR(map); -+ dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret); -+ return ret; -+ } -+ -+ ret = regmap_read(map, OCOTP_ANA1, &val); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret); -+ return ret; -+ } -+ -+ if (val == 0 || val == ~0) { -+ dev_err(&pdev->dev, "invalid sensor calibration data\n"); -+ return -EINVAL; -+ } -+ -+ /* -+ * Sensor data layout: -+ * [31:20] - sensor value @ 25C -+ * [19:8] - sensor value of hot -+ * [7:0] - hot temperature value -+ */ -+ n1 = val >> 20; -+ n2 = (val & 0xfff00) >> 8; -+ t2 = val & 0xff; -+ t1 = 25; /* t1 always 25C */ -+ -+ /* -+ * 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 millicelsius -+ * 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) -+ */ -+ data->c1 = 1000 * (t1 - t2) / (n1 - n2); -+ data->c2 = 1000 * t2 - data->c1 * n2; -+ -+ return 0; -+} -+ -+static int imx_thermal_probe(struct platform_device *pdev) -+{ -+ struct imx_thermal_data *data; -+ struct cpumask clip_cpus; -+ struct regmap *map; -+ int ret; -+ -+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); -+ if (!data) -+ return -ENOMEM; -+ -+ map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,tempmon"); -+ if (IS_ERR(map)) { -+ ret = PTR_ERR(map); -+ dev_err(&pdev->dev, "failed to get tempmon regmap: %d\n", ret); -+ return ret; -+ } -+ data->tempmon = map; -+ -+ platform_set_drvdata(pdev, data); -+ -+ ret = imx_get_sensor_data(pdev); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to get sensor data\n"); -+ return ret; -+ } -+ -+ /* Make sure sensor is in known good state for measurements */ -+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); -+ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP); -+ regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ); -+ regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF); -+ regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); -+ -+ cpumask_set_cpu(0, &clip_cpus); -+ data->cdev = cpufreq_cooling_register(&clip_cpus); -+ if (IS_ERR(data->cdev)) { -+ ret = PTR_ERR(data->cdev); -+ dev_err(&pdev->dev, -+ "failed to register cpufreq cooling device: %d\n", ret); -+ return ret; -+ } -+ -+ data->tz = thermal_zone_device_register("imx_thermal_zone", -+ IMX_TRIP_NUM, 0, data, -+ &imx_tz_ops, NULL, -+ IMX_PASSIVE_DELAY, -+ IMX_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); -+ cpufreq_cooling_unregister(data->cdev); -+ return ret; -+ } -+ -+ data->mode = THERMAL_DEVICE_ENABLED; -+ -+ return 0; -+} -+ -+static int imx_thermal_remove(struct platform_device *pdev) -+{ -+ struct imx_thermal_data *data = platform_get_drvdata(pdev); -+ -+ thermal_zone_device_unregister(data->tz); -+ cpufreq_cooling_unregister(data->cdev); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM_SLEEP -+static int imx_thermal_suspend(struct device *dev) -+{ -+ struct imx_thermal_data *data = dev_get_drvdata(dev); -+ struct regmap *map = data->tempmon; -+ u32 val; -+ -+ regmap_read(map, TEMPSENSE0, &val); -+ if ((val & TEMPSENSE0_POWER_DOWN) == 0) { -+ /* -+ * If a measurement is taking place, wait for a long enough -+ * time for it to finish, and then check again. If it still -+ * does not finish, something must go wrong. -+ */ -+ udelay(50); -+ regmap_read(map, TEMPSENSE0, &val); -+ if ((val & TEMPSENSE0_POWER_DOWN) == 0) -+ return -ETIMEDOUT; -+ } -+ -+ return 0; -+} -+ -+static int imx_thermal_resume(struct device *dev) -+{ -+ /* Nothing to do for now */ -+ return 0; -+} -+#endif -+ -+static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops, -+ imx_thermal_suspend, imx_thermal_resume); -+ -+static const struct of_device_id of_imx_thermal_match[] = { -+ { .compatible = "fsl,imx6q-tempmon", }, -+ { /* end */ } -+}; -+ -+static struct platform_driver imx_thermal = { -+ .driver = { -+ .name = "imx_thermal", -+ .owner = THIS_MODULE, -+ .pm = &imx_thermal_pm_ops, -+ .of_match_table = of_imx_thermal_match, -+ }, -+ .probe = imx_thermal_probe, -+ .remove = imx_thermal_remove, -+}; -+module_platform_driver(imx_thermal); -+ -+MODULE_AUTHOR("Freescale Semiconductor, Inc."); -+MODULE_DESCRIPTION("Thermal driver for Freescale i.MX SoCs"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:imx-thermal"); |