From 9c5c87e5936e792ddca5cb876d523b6e5d6042e6 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 2 Apr 2015 16:49:07 +0200 Subject: ti-soc-thermal: implement omap3 support This adds support for OMAP3 chips to ti-soc-thermal. As requested by TI people, it is marked unreliable and warning is printed. Signed-off-by: Pavel Machek Signed-off-by: Eduardo Valentin --- drivers/thermal/ti-soc-thermal/Kconfig | 15 +++ drivers/thermal/ti-soc-thermal/Makefile | 1 + .../thermal/ti-soc-thermal/omap3-thermal-data.c | 103 +++++++++++++++++++++ drivers/thermal/ti-soc-thermal/ti-bandgap.c | 10 ++ drivers/thermal/ti-soc-thermal/ti-bandgap.h | 9 ++ 5 files changed, 138 insertions(+) create mode 100644 drivers/thermal/ti-soc-thermal/omap3-thermal-data.c (limited to 'drivers/thermal') diff --git a/drivers/thermal/ti-soc-thermal/Kconfig b/drivers/thermal/ti-soc-thermal/Kconfig index cb6686f..ea8283f 100644 --- a/drivers/thermal/ti-soc-thermal/Kconfig +++ b/drivers/thermal/ti-soc-thermal/Kconfig @@ -19,6 +19,21 @@ config TI_THERMAL This includes trip points definitions, extrapolation rules and CPU cooling device bindings. +config OMAP3_THERMAL + bool "Texas Instruments OMAP3 thermal support" + depends on TI_SOC_THERMAL + depends on ARCH_OMAP3 || COMPILE_TEST + help + If you say yes here you get thermal support for the Texas Instruments + OMAP3 SoC family. The current chips supported are: + - OMAP3430 + + OMAP3 chips normally don't need thermal management, and sensors in + this generation are not accurate, nor they are very close to + the important hotspots. + + Say 'N' here. + config OMAP4_THERMAL bool "Texas Instruments OMAP4 thermal support" depends on TI_SOC_THERMAL diff --git a/drivers/thermal/ti-soc-thermal/Makefile b/drivers/thermal/ti-soc-thermal/Makefile index 1226b24..0f89bdf 100644 --- a/drivers/thermal/ti-soc-thermal/Makefile +++ b/drivers/thermal/ti-soc-thermal/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal.o ti-soc-thermal-y := ti-bandgap.o ti-soc-thermal-$(CONFIG_TI_THERMAL) += ti-thermal-common.o ti-soc-thermal-$(CONFIG_DRA752_THERMAL) += dra752-thermal-data.o +ti-soc-thermal-$(CONFIG_OMAP3_THERMAL) += omap3-thermal-data.o ti-soc-thermal-$(CONFIG_OMAP4_THERMAL) += omap4-thermal-data.o ti-soc-thermal-$(CONFIG_OMAP5_THERMAL) += omap5-thermal-data.o diff --git a/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c new file mode 100644 index 0000000..8470225 --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c @@ -0,0 +1,103 @@ +/* + * OMAP3 thermal driver. + * + * Copyright (C) 2011-2012 Texas Instruments Inc. + * Copyright (C) 2014 Pavel Machek + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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. + * + * Note + * http://www.ti.com/lit/er/sprz278f/sprz278f.pdf "Advisory + * 3.1.1.186 MMC OCP Clock Not Gated When Thermal Sensor Is Used" + * + * Also TI says: + * Just be careful when you try to make thermal policy like decisions + * based on this sensor. Placement of the sensor w.r.t the actual logic + * generating heat has to be a factor as well. If you are just looking + * for an approximation temperature (thermometerish kind), you might be + * ok with this. I am not sure we'd find any TI data around this.. just a + * heads up. + */ + +#include "ti-thermal.h" +#include "ti-bandgap.h" + +/* + * OMAP34XX has one instance of thermal sensor for MPU + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap34xx_mpu_temp_sensor_registers = { + .temp_sensor_ctrl = 0, + .bgap_soc_mask = BIT(8), + .bgap_eocz_mask = BIT(7), + .bgap_dtemp_mask = 0x7f, + + .bgap_mode_ctrl = 0, + .mode_ctrl_mask = BIT(9), +}; + +/* Thresholds and limits for OMAP34XX MPU temperature sensor */ +static struct temp_sensor_data omap34xx_mpu_temp_sensor_data = { + .min_freq = 32768, + .max_freq = 32768, + .max_temp = 125000, + .min_temp = -40000, + .hyst_val = 5000, +}; + +/* + * Temperature values in milli degree celsius + */ +static const int +omap34xx_adc_to_temp[128] = { + -40000, -40000, -40000, -40000, -40000, -39000, -38000, -36000, + -34000, -32000, -31000, -29000, -28000, -26000, -25000, -24000, + -22000, -21000, -19000, -18000, -17000, -15000, -14000, -12000, + -11000, -9000, -8000, -7000, -5000, -4000, -2000, -1000, 0000, + 1000, 3000, 4000, 5000, 7000, 8000, 10000, 11000, 13000, 14000, + 15000, 17000, 18000, 20000, 21000, 22000, 24000, 25000, 27000, + 28000, 30000, 31000, 32000, 34000, 35000, 37000, 38000, 39000, + 41000, 42000, 44000, 45000, 47000, 48000, 49000, 51000, 52000, + 53000, 55000, 56000, 58000, 59000, 60000, 62000, 63000, 65000, + 66000, 67000, 69000, 70000, 72000, 73000, 74000, 76000, 77000, + 79000, 80000, 81000, 83000, 84000, 85000, 87000, 88000, 89000, + 91000, 92000, 94000, 95000, 96000, 98000, 99000, 100000, + 102000, 103000, 105000, 106000, 107000, 109000, 110000, 111000, + 113000, 114000, 116000, 117000, 118000, 120000, 121000, 122000, + 124000, 124000, 125000, 125000, 125000, 125000, 125000 +}; + +/* OMAP34XX data */ +const struct ti_bandgap_data omap34xx_data = { + .features = TI_BANDGAP_FEATURE_CLK_CTRL | TI_BANDGAP_FEATURE_UNRELIABLE, + .fclock_name = "ts_fck", + .div_ck_name = "ts_fck", + .conv_table = omap34xx_adc_to_temp, + .adc_start_val = 0, + .adc_end_val = 127, + .expose_sensor = ti_thermal_expose_sensor, + .remove_sensor = ti_thermal_remove_sensor, + + .sensors = { + { + .registers = &omap34xx_mpu_temp_sensor_registers, + .ts_data = &omap34xx_mpu_temp_sensor_data, + .domain = "cpu", + .slope = 0, + .constant = 20000, + .slope_pcb = 0, + .constant_pcb = 20000, + .register_cooling = NULL, + .unregister_cooling = NULL, + }, + }, + .sensor_count = 1, +}; diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c index 10c47c0..9f9ed72 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c @@ -1274,6 +1274,10 @@ int ti_bandgap_probe(struct platform_device *pdev) } bgp->dev = &pdev->dev; + if (TI_BANDGAP_HAS(bgp, UNRELIABLE)) + dev_warn(&pdev->dev, + "This OMAP thermal sensor is unreliable. You've been warned\n"); + if (TI_BANDGAP_HAS(bgp, TSHUT)) { ret = ti_bandgap_tshut_init(bgp, pdev); if (ret) { @@ -1579,6 +1583,12 @@ static SIMPLE_DEV_PM_OPS(ti_bandgap_dev_pm_ops, ti_bandgap_suspend, #endif static const struct of_device_id of_ti_bandgap_match[] = { +#ifdef CONFIG_OMAP3_THERMAL + { + .compatible = "ti,omap34xx-bandgap", + .data = (void *)&omap34xx_data, + }, +#endif #ifdef CONFIG_OMAP4_THERMAL { .compatible = "ti,omap4430-bandgap", diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.h b/drivers/thermal/ti-soc-thermal/ti-bandgap.h index 0c52f7a..045f642 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.h +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.h @@ -322,6 +322,8 @@ struct ti_temp_sensor { * has Errata 814 * TI_BANDGAP_FEATURE_ERRATA_813 - used to workaorund when the bandgap device * has Errata 813 + * TI_BANDGAP_FEATURE_UNRELIABLE - used when the sensor readings are too + * inaccurate. * TI_BANDGAP_HAS(b, f) - macro to check if a bandgap device is capable of a * specific feature (above) or not. Return non-zero, if yes. */ @@ -337,6 +339,7 @@ struct ti_temp_sensor { #define TI_BANDGAP_FEATURE_HISTORY_BUFFER BIT(9) #define TI_BANDGAP_FEATURE_ERRATA_814 BIT(10) #define TI_BANDGAP_FEATURE_ERRATA_813 BIT(11) +#define TI_BANDGAP_FEATURE_UNRELIABLE BIT(12) #define TI_BANDGAP_HAS(b, f) \ ((b)->conf->features & TI_BANDGAP_FEATURE_ ## f) @@ -390,6 +393,12 @@ int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data); void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id); int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend); +#ifdef CONFIG_OMAP3_THERMAL +extern const struct ti_bandgap_data omap34xx_data; +#else +#define omap34xx_data NULL +#endif + #ifdef CONFIG_OMAP4_THERMAL extern const struct ti_bandgap_data omap4430_data; extern const struct ti_bandgap_data omap4460_data; -- cgit v1.1 From b840b6e65cbe2b84ec398bb286d121a9eb2597ee Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 21 Sep 2015 17:32:15 -0700 Subject: thermal: ti-soc-thermal: add OMAP36xx support Add OMAP36xx support to ti-soc-thermal driver. This chip is also unreliable. The data provided here is based on OMAP36xx TRM: http://www.ti.com/lit/ug/swpu177aa/swpu177aa.pdf Signed-off-by: Eduardo Valentin Signed-off-by: Eduardo Valentin --- .../thermal/ti-soc-thermal/omap3-thermal-data.c | 73 ++++++++++++++++++++++ drivers/thermal/ti-soc-thermal/ti-bandgap.c | 4 ++ drivers/thermal/ti-soc-thermal/ti-bandgap.h | 2 + 3 files changed, 79 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c index 8470225..3ee3434 100644 --- a/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c +++ b/drivers/thermal/ti-soc-thermal/omap3-thermal-data.c @@ -101,3 +101,76 @@ const struct ti_bandgap_data omap34xx_data = { }, .sensor_count = 1, }; + +/* + * OMAP36XX has one instance of thermal sensor for MPU + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap36xx_mpu_temp_sensor_registers = { + .temp_sensor_ctrl = 0, + .bgap_soc_mask = BIT(9), + .bgap_eocz_mask = BIT(8), + .bgap_dtemp_mask = 0xFF, + + .bgap_mode_ctrl = 0, + .mode_ctrl_mask = BIT(10), +}; + +/* Thresholds and limits for OMAP36XX MPU temperature sensor */ +static struct temp_sensor_data omap36xx_mpu_temp_sensor_data = { + .min_freq = 32768, + .max_freq = 32768, + .max_temp = 125000, + .min_temp = -40000, + .hyst_val = 5000, +}; + +/* + * Temperature values in milli degree celsius + */ +static const int +omap36xx_adc_to_temp[128] = { + -40000, -40000, -40000, -40000, -40000, -40000, -40000, -40000, + -40000, -40000, -40000, -40000, -40000, -38000, -35000, -34000, + -32000, -30000, -28000, -26000, -24000, -22000, -20000, -18500, + -17000, -15000, -13500, -12000, -10000, -8000, -6500, -5000, -3500, + -1500, 0, 2000, 3500, 5000, 6500, 8500, 10000, 12000, 13500, + 15000, 17000, 19000, 21000, 23000, 25000, 27000, 28500, 30000, + 32000, 33500, 35000, 37000, 38500, 40000, 42000, 43500, 45000, + 47000, 48500, 50000, 52000, 53500, 55000, 57000, 58500, 60000, + 62000, 64000, 66000, 68000, 70000, 71500, 73500, 75000, 77000, + 78500, 80000, 82000, 83500, 85000, 87000, 88500, 90000, 92000, + 93500, 95000, 97000, 98500, 100000, 102000, 103500, 105000, 107000, + 109000, 111000, 113000, 115000, 117000, 118500, 120000, 122000, + 123500, 125000, 125000, 125000, 125000, 125000, 125000, 125000, + 125000, 125000, 125000, 125000, 125000, 125000, 125000, 125000, + 125000, 125000, 125000, 125000, 125000, 125000, 125000 +}; + +/* OMAP36XX data */ +const struct ti_bandgap_data omap36xx_data = { + .features = TI_BANDGAP_FEATURE_CLK_CTRL | TI_BANDGAP_FEATURE_UNRELIABLE, + .fclock_name = "ts_fck", + .div_ck_name = "ts_fck", + .conv_table = omap36xx_adc_to_temp, + .adc_start_val = 0, + .adc_end_val = 127, + .expose_sensor = ti_thermal_expose_sensor, + .remove_sensor = ti_thermal_remove_sensor, + + .sensors = { + { + .registers = &omap36xx_mpu_temp_sensor_registers, + .ts_data = &omap36xx_mpu_temp_sensor_data, + .domain = "cpu", + .slope = 0, + .constant = 20000, + .slope_pcb = 0, + .constant_pcb = 20000, + .register_cooling = NULL, + .unregister_cooling = NULL, + }, + }, + .sensor_count = 1, +}; diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c index 9f9ed72..1e34a1e 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c @@ -1588,6 +1588,10 @@ static const struct of_device_id of_ti_bandgap_match[] = { .compatible = "ti,omap34xx-bandgap", .data = (void *)&omap34xx_data, }, + { + .compatible = "ti,omap36xx-bandgap", + .data = (void *)&omap36xx_data, + }, #endif #ifdef CONFIG_OMAP4_THERMAL { diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.h b/drivers/thermal/ti-soc-thermal/ti-bandgap.h index 045f642..fe0adb8 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.h +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.h @@ -395,8 +395,10 @@ int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend); #ifdef CONFIG_OMAP3_THERMAL extern const struct ti_bandgap_data omap34xx_data; +extern const struct ti_bandgap_data omap36xx_data; #else #define omap34xx_data NULL +#define omap36xx_data NULL #endif #ifdef CONFIG_OMAP4_THERMAL -- cgit v1.1 From 98cadf25dae234130994d9c71cd0d925db4b2a6a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 3 Aug 2015 04:13:16 -0700 Subject: thermal: kconfig: select THERMAL_WRITABLE_TRIPS for x86 thermal After the commit "thermal: core: Add Kconfig option to enable writable trips", by default the trips are read only. This cause user space thermal controllers to poll for temperature as they can't set temperature thresholds for getting a notification via uevents. These programs use RW trip in a zone to register thresholds. Since we need to enable the new config introduced by above commit to allow writable trips, selecting CONFIG_THERMAL_WRITABLE_TRIP for x86 thermal drivers. Signed-off-by: Srinivas Pandruvada --- drivers/thermal/Kconfig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 5aabc4b..a94ebb5 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -275,6 +275,7 @@ config X86_PKG_TEMP_THERMAL tristate "X86 package temperature thermal driver" depends on X86_THERMAL_VECTOR select THERMAL_GOV_USER_SPACE + select THERMAL_WRITABLE_TRIPS default m help Enable this to register CPU digital sensor for package temperature as @@ -296,6 +297,7 @@ config INTEL_SOC_DTS_THERMAL tristate "Intel SoCs DTS thermal driver" depends on X86 select INTEL_SOC_DTS_IOSF_CORE + select THERMAL_WRITABLE_TRIPS help Enable this to register Intel SoCs (e.g. Bay Trail) platform digital temperature sensor (DTS). These SoCs have two additional DTSs in @@ -322,6 +324,7 @@ config INT340X_THERMAL select ACPI_THERMAL_REL select ACPI_FAN select INTEL_SOC_DTS_IOSF_CORE + select THERMAL_WRITABLE_TRIPS help Newer laptops and tablets that use ACPI may have thermal sensors and other devices with thermal control capabilities outside the core -- cgit v1.1 From a71544cd9373b879bbb40f55ce4d01556a4bc29e Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Wed, 19 Aug 2015 11:52:19 +0530 Subject: thermal: cpu_cooling: Remove usage of devm functions In the function cpufreq_get_requested_power, the memory allocated for load_cpu is live within the function only. And after the allocation it is immediately freed with devm_kfree. There is no need to allocate memory for load_cpu with devm function so replace devm_kcalloc with kcalloc and devm_kfree with kfree. Signed-off-by: Vaishali Thakkar Acked-by: Viresh Kumar Acked-by: Javi Merino Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 42c6f71..e3fbc5a 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -591,8 +591,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, if (trace_thermal_power_cpu_get_power_enabled()) { u32 ncpus = cpumask_weight(&cpufreq_device->allowed_cpus); - load_cpu = devm_kcalloc(&cdev->device, ncpus, sizeof(*load_cpu), - GFP_KERNEL); + load_cpu = kcalloc(ncpus, sizeof(*load_cpu), GFP_KERNEL); } for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { @@ -615,8 +614,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, dynamic_power = get_dynamic_power(cpufreq_device, freq); ret = get_static_power(cpufreq_device, tz, freq, &static_power); if (ret) { - if (load_cpu) - devm_kfree(&cdev->device, load_cpu); + kfree(load_cpu); return ret; } @@ -625,7 +623,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, &cpufreq_device->allowed_cpus, freq, load_cpu, i, dynamic_power, static_power); - devm_kfree(&cdev->device, load_cpu); + kfree(load_cpu); } *power = static_power + dynamic_power; -- cgit v1.1 From a76caf55e5b356ba20a5a43ac4d9f7a04b20941d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98rjan=20Eide?= Date: Thu, 10 Sep 2015 18:09:30 +0100 Subject: thermal: Add devfreq cooling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a generic thermal cooling device for devfreq, that is similar to cpu_cooling. The device must use devfreq. In order to use the power extension of the cooling device, it must have registered its OPPs using the OPP library. Cc: Zhang Rui Cc: Eduardo Valentin Signed-off-by: Javi Merino Signed-off-by: Ørjan Eide Signed-off-by: Eduardo Valentin --- drivers/thermal/Kconfig | 14 + drivers/thermal/Makefile | 3 + drivers/thermal/devfreq_cooling.c | 563 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 580 insertions(+) create mode 100644 drivers/thermal/devfreq_cooling.c (limited to 'drivers/thermal') diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 5aabc4b..90629f6 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -147,6 +147,20 @@ config CLOCK_THERMAL device that is configured to use this cooling mechanism will be controlled to reduce clock frequency whenever temperature is high. +config DEVFREQ_THERMAL + bool "Generic device cooling support" + depends on PM_DEVFREQ + depends on PM_OPP + help + This implements the generic devfreq cooling mechanism through + frequency reduction for devices using devfreq. + + This will throttle the device by limiting the maximum allowed DVFS + frequency corresponding to the cooling level. + + In order to use the power extensions of the cooling device, + devfreq should use the simple_ondemand governor. + If you want this support, you should say Y here. config THERMAL_EMULATION diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 26f1608..cfae6a6 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -22,6 +22,9 @@ thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o # clock cooling thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o +# devfreq cooling +thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o + # platform thermal drivers obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c new file mode 100644 index 0000000..a032c5d --- /dev/null +++ b/drivers/thermal/devfreq_cooling.c @@ -0,0 +1,563 @@ +/* + * devfreq_cooling: Thermal cooling device implementation for devices using + * devfreq + * + * Copyright (C) 2014-2015 ARM Limited + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * TODO: + * - If OPPs are added or removed after devfreq cooling has + * registered, the devfreq cooling won't react to it. + */ + +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(devfreq_lock); +static DEFINE_IDR(devfreq_idr); + +/** + * struct devfreq_cooling_device - Devfreq cooling device + * @id: unique integer value corresponding to each + * devfreq_cooling_device registered. + * @cdev: Pointer to associated thermal cooling device. + * @devfreq: Pointer to associated devfreq device. + * @cooling_state: Current cooling state. + * @power_table: Pointer to table with maximum power draw for each + * cooling state. State is the index into the table, and + * the power is in mW. + * @freq_table: Pointer to a table with the frequencies sorted in descending + * order. You can index the table by cooling device state + * @freq_table_size: Size of the @freq_table and @power_table + * @power_ops: Pointer to devfreq_cooling_power, used to generate the + * @power_table. + */ +struct devfreq_cooling_device { + int id; + struct thermal_cooling_device *cdev; + struct devfreq *devfreq; + unsigned long cooling_state; + u32 *power_table; + u32 *freq_table; + size_t freq_table_size; + struct devfreq_cooling_power *power_ops; +}; + +/** + * get_idr - function to get a unique id. + * @idr: struct idr * handle used to create a id. + * @id: int * value generated by this function. + * + * This function will populate @id with an unique + * id, using the idr API. + * + * Return: 0 on success, an error code on failure. + */ +static int get_idr(struct idr *idr, int *id) +{ + int ret; + + mutex_lock(&devfreq_lock); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); + mutex_unlock(&devfreq_lock); + if (unlikely(ret < 0)) + return ret; + *id = ret; + + return 0; +} + +/** + * release_idr - function to free the unique id. + * @idr: struct idr * handle used for creating the id. + * @id: int value representing the unique id. + */ +static void release_idr(struct idr *idr, int id) +{ + mutex_lock(&devfreq_lock); + idr_remove(idr, id); + mutex_unlock(&devfreq_lock); +} + +/** + * partition_enable_opps() - disable all opps above a given state + * @dfc: Pointer to devfreq we are operating on + * @cdev_state: cooling device state we're setting + * + * Go through the OPPs of the device, enabling all OPPs until + * @cdev_state and disabling those frequencies above it. + */ +static int partition_enable_opps(struct devfreq_cooling_device *dfc, + unsigned long cdev_state) +{ + int i; + struct device *dev = dfc->devfreq->dev.parent; + + for (i = 0; i < dfc->freq_table_size; i++) { + struct dev_pm_opp *opp; + int ret = 0; + unsigned int freq = dfc->freq_table[i]; + bool want_enable = i >= cdev_state ? true : false; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_exact(dev, freq, !want_enable); + rcu_read_unlock(); + + if (PTR_ERR(opp) == -ERANGE) + continue; + else if (IS_ERR(opp)) + return PTR_ERR(opp); + + if (want_enable) + ret = dev_pm_opp_enable(dev, freq); + else + ret = dev_pm_opp_disable(dev, freq); + + if (ret) + return ret; + } + + return 0; +} + +static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct devfreq_cooling_device *dfc = cdev->devdata; + + *state = dfc->freq_table_size - 1; + + return 0; +} + +static int devfreq_cooling_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct devfreq_cooling_device *dfc = cdev->devdata; + + *state = dfc->cooling_state; + + return 0; +} + +static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct devfreq_cooling_device *dfc = cdev->devdata; + struct devfreq *df = dfc->devfreq; + struct device *dev = df->dev.parent; + int ret; + + if (state == dfc->cooling_state) + return 0; + + dev_dbg(dev, "Setting cooling state %lu\n", state); + + if (state >= dfc->freq_table_size) + return -EINVAL; + + ret = partition_enable_opps(dfc, state); + if (ret) + return ret; + + dfc->cooling_state = state; + + return 0; +} + +/** + * freq_get_state() - get the cooling state corresponding to a frequency + * @dfc: Pointer to devfreq cooling device + * @freq: frequency in Hz + * + * Return: the cooling state associated with the @freq, or + * THERMAL_CSTATE_INVALID if it wasn't found. + */ +static unsigned long +freq_get_state(struct devfreq_cooling_device *dfc, unsigned long freq) +{ + int i; + + for (i = 0; i < dfc->freq_table_size; i++) { + if (dfc->freq_table[i] == freq) + return i; + } + + return THERMAL_CSTATE_INVALID; +} + +/** + * get_static_power() - calculate the static power + * @dfc: Pointer to devfreq cooling device + * @freq: Frequency in Hz + * + * Calculate the static power in milliwatts using the supplied + * get_static_power(). The current voltage is calculated using the + * OPP library. If no get_static_power() was supplied, assume the + * static power is negligible. + */ +static unsigned long +get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq) +{ + struct devfreq *df = dfc->devfreq; + struct device *dev = df->dev.parent; + unsigned long voltage; + struct dev_pm_opp *opp; + + if (!dfc->power_ops->get_static_power) + return 0; + + rcu_read_lock(); + + opp = dev_pm_opp_find_freq_exact(dev, freq, true); + if (IS_ERR(opp) && (PTR_ERR(opp) == -ERANGE)) + opp = dev_pm_opp_find_freq_exact(dev, freq, false); + + voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */ + + rcu_read_unlock(); + + if (voltage == 0) { + dev_warn_ratelimited(dev, + "Failed to get voltage for frequency %lu: %ld\n", + freq, IS_ERR(opp) ? PTR_ERR(opp) : 0); + return 0; + } + + return dfc->power_ops->get_static_power(voltage); +} + +/** + * get_dynamic_power - calculate the dynamic power + * @dfc: Pointer to devfreq cooling device + * @freq: Frequency in Hz + * @voltage: Voltage in millivolts + * + * Calculate the dynamic power in milliwatts consumed by the device at + * frequency @freq and voltage @voltage. If the get_dynamic_power() + * was supplied as part of the devfreq_cooling_power struct, then that + * function is used. Otherwise, a simple power model (Pdyn = Coeff * + * Voltage^2 * Frequency) is used. + */ +static unsigned long +get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq, + unsigned long voltage) +{ + unsigned long power; + u32 freq_mhz; + struct devfreq_cooling_power *dfc_power = dfc->power_ops; + + if (dfc_power->get_dynamic_power) + return dfc_power->get_dynamic_power(freq, voltage); + + freq_mhz = freq / 1000000; + power = (u64)dfc_power->dyn_power_coeff * freq_mhz * voltage * voltage; + do_div(power, 1000000000); + + return power; +} + +static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + u32 *power) +{ + struct devfreq_cooling_device *dfc = cdev->devdata; + struct devfreq *df = dfc->devfreq; + struct devfreq_dev_status *status = &df->last_status; + unsigned long state; + unsigned long freq = status->current_frequency; + u32 dyn_power, static_power; + + /* Get dynamic power for state */ + state = freq_get_state(dfc, freq); + if (state == THERMAL_CSTATE_INVALID) + return -EAGAIN; + + dyn_power = dfc->power_table[state]; + + /* Scale dynamic power for utilization */ + dyn_power = (dyn_power * status->busy_time) / status->total_time; + + /* Get static power */ + static_power = get_static_power(dfc, freq); + + *power = dyn_power + static_power; + + return 0; +} + +static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + unsigned long state, + u32 *power) +{ + struct devfreq_cooling_device *dfc = cdev->devdata; + unsigned long freq; + u32 static_power; + + if (state < 0 || state >= dfc->freq_table_size) + return -EINVAL; + + freq = dfc->freq_table[state]; + static_power = get_static_power(dfc, freq); + + *power = dfc->power_table[state] + static_power; + return 0; +} + +static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, + u32 power, unsigned long *state) +{ + struct devfreq_cooling_device *dfc = cdev->devdata; + struct devfreq *df = dfc->devfreq; + struct devfreq_dev_status *status = &df->last_status; + unsigned long freq = status->current_frequency; + unsigned long busy_time; + s32 dyn_power; + u32 static_power; + int i; + + static_power = get_static_power(dfc, freq); + + dyn_power = power - static_power; + dyn_power = dyn_power > 0 ? dyn_power : 0; + + /* Scale dynamic power for utilization */ + busy_time = status->busy_time ?: 1; + dyn_power = (dyn_power * status->total_time) / busy_time; + + /* + * Find the first cooling state that is within the power + * budget for dynamic power. + */ + for (i = 0; i < dfc->freq_table_size - 1; i++) + if (dyn_power >= dfc->power_table[i]) + break; + + *state = i; + return 0; +} + +static struct thermal_cooling_device_ops devfreq_cooling_ops = { + .get_max_state = devfreq_cooling_get_max_state, + .get_cur_state = devfreq_cooling_get_cur_state, + .set_cur_state = devfreq_cooling_set_cur_state, +}; + +/** + * devfreq_cooling_gen_tables() - Generate power and freq tables. + * @dfc: Pointer to devfreq cooling device. + * + * Generate power and frequency tables: the power table hold the + * device's maximum power usage at each cooling state (OPP). The + * static and dynamic power using the appropriate voltage and + * frequency for the state, is acquired from the struct + * devfreq_cooling_power, and summed to make the maximum power draw. + * + * The frequency table holds the frequencies in descending order. + * That way its indexed by cooling device state. + * + * The tables are malloced, and pointers put in dfc. They must be + * freed when unregistering the devfreq cooling device. + * + * Return: 0 on success, negative error code on failure. + */ +static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc) +{ + struct devfreq *df = dfc->devfreq; + struct device *dev = df->dev.parent; + int ret, num_opps; + unsigned long freq; + u32 *power_table = NULL; + u32 *freq_table; + int i; + + num_opps = dev_pm_opp_get_opp_count(dev); + + if (dfc->power_ops) { + power_table = kcalloc(num_opps, sizeof(*power_table), + GFP_KERNEL); + if (!power_table) + ret = -ENOMEM; + } + + freq_table = kcalloc(num_opps, sizeof(*freq_table), + GFP_KERNEL); + if (!freq_table) { + ret = -ENOMEM; + goto free_power_table; + } + + for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) { + unsigned long power_dyn, voltage; + struct dev_pm_opp *opp; + + rcu_read_lock(); + + opp = dev_pm_opp_find_freq_floor(dev, &freq); + if (IS_ERR(opp)) { + rcu_read_unlock(); + ret = PTR_ERR(opp); + goto free_tables; + } + + voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */ + + rcu_read_unlock(); + + if (dfc->power_ops) { + power_dyn = get_dynamic_power(dfc, freq, voltage); + + dev_dbg(dev, "Dynamic power table: %lu MHz @ %lu mV: %lu = %lu mW\n", + freq / 1000000, voltage, power_dyn, power_dyn); + + power_table[i] = power_dyn; + } + + freq_table[i] = freq; + } + + if (dfc->power_ops) + dfc->power_table = power_table; + + dfc->freq_table = freq_table; + dfc->freq_table_size = num_opps; + + return 0; + +free_tables: + kfree(freq_table); +free_power_table: + kfree(power_table); + + return ret; +} + +/** + * of_devfreq_cooling_register_power() - Register devfreq cooling device, + * with OF and power information. + * @np: Pointer to OF device_node. + * @df: Pointer to devfreq device. + * @dfc_power: Pointer to devfreq_cooling_power. + * + * Register a devfreq cooling device. The available OPPs must be + * registered on the device. + * + * If @dfc_power is provided, the cooling device is registered with the + * power extensions. For the power extensions to work correctly, + * devfreq should use the simple_ondemand governor, other governors + * are not currently supported. + */ +struct devfreq_cooling_device * +of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, + struct devfreq_cooling_power *dfc_power) +{ + struct thermal_cooling_device *cdev; + struct devfreq_cooling_device *dfc; + char dev_name[THERMAL_NAME_LENGTH]; + int err; + + dfc = kzalloc(sizeof(*dfc), GFP_KERNEL); + if (!dfc) + return ERR_PTR(-ENOMEM); + + dfc->devfreq = df; + + if (dfc_power) { + dfc->power_ops = dfc_power; + + devfreq_cooling_ops.get_requested_power = + devfreq_cooling_get_requested_power; + devfreq_cooling_ops.state2power = devfreq_cooling_state2power; + devfreq_cooling_ops.power2state = devfreq_cooling_power2state; + } + + err = devfreq_cooling_gen_tables(dfc); + if (err) + goto free_dfc; + + err = get_idr(&devfreq_idr, &dfc->id); + if (err) + goto free_tables; + + snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d", dfc->id); + + cdev = thermal_of_cooling_device_register(np, dev_name, dfc, + &devfreq_cooling_ops); + if (IS_ERR(cdev)) { + err = PTR_ERR(cdev); + dev_err(df->dev.parent, + "Failed to register devfreq cooling device (%d)\n", + err); + goto release_idr; + } + + dfc->cdev = cdev; + + return dfc; + +release_idr: + release_idr(&devfreq_idr, dfc->id); +free_tables: + kfree(dfc->power_table); + kfree(dfc->freq_table); +free_dfc: + kfree(dfc); + + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(of_devfreq_cooling_register_power); + +/** + * of_devfreq_cooling_register() - Register devfreq cooling device, + * with OF information. + * @np: Pointer to OF device_node. + * @df: Pointer to devfreq device. + */ +struct devfreq_cooling_device * +of_devfreq_cooling_register(struct device_node *np, struct devfreq *df) +{ + return of_devfreq_cooling_register_power(np, df, NULL); +} +EXPORT_SYMBOL_GPL(of_devfreq_cooling_register); + +/** + * devfreq_cooling_register() - Register devfreq cooling device. + * @df: Pointer to devfreq device. + */ +struct devfreq_cooling_device *devfreq_cooling_register(struct devfreq *df) +{ + return of_devfreq_cooling_register(NULL, df); +} +EXPORT_SYMBOL_GPL(devfreq_cooling_register); + +/** + * devfreq_cooling_unregister() - Unregister devfreq cooling device. + * @dfc: Pointer to devfreq cooling device to unregister. + */ +void devfreq_cooling_unregister(struct devfreq_cooling_device *dfc) +{ + if (!dfc) + return; + + thermal_cooling_device_unregister(dfc->cdev); + release_idr(&devfreq_idr, dfc->id); + kfree(dfc->power_table); + kfree(dfc->freq_table); + + kfree(dfc); +} +EXPORT_SYMBOL_GPL(devfreq_cooling_unregister); -- cgit v1.1 From 9876b1a4431a3f0959c70e1d817ecab6440cb2e6 Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Thu, 10 Sep 2015 18:09:31 +0100 Subject: devfreq_cooling: add trace information Tracing is useful for debugging and performance tuning. Add similar traces to what's present in the cpu cooling device. Cc: Zhang Rui Cc: Eduardo Valentin Cc: Steven Rostedt Cc: Ingo Molnar Signed-off-by: Javi Merino Signed-off-by: Eduardo Valentin --- drivers/thermal/devfreq_cooling.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index a032c5d..a272068 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -25,6 +25,8 @@ #include #include +#include + static DEFINE_MUTEX(devfreq_lock); static DEFINE_IDR(devfreq_idr); @@ -293,6 +295,9 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd /* Get static power */ static_power = get_static_power(dfc, freq); + trace_thermal_power_devfreq_get_power(cdev, status, freq, dyn_power, + static_power); + *power = dyn_power + static_power; return 0; @@ -348,6 +353,7 @@ static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev, break; *state = i; + trace_thermal_power_devfreq_limit(cdev, freq, *state, power); return 0; } -- cgit v1.1 From d9a241cbbdca9ead3322c073dbb9dbf61ac76ef0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 7 Aug 2015 13:59:23 -0700 Subject: thermal: rockchip: fix handling of invalid readings We attempted to signal invalid code by returning -EAGAIN from rk_tsadcv2_code_to_temp(), unfortunately the return value was stuffed directly into the temperature pointer, potentially confusing upper layers with temperature of -EINVAL. Let's split temperature from error/success indicator to avoid such confusion. Also change the way we scan the temperature table to start with the 2nd element so that we do not need to worry that we may reference out of bounds element while doing binary search and keep checking that we end up with 'mid' equal to 0 (since we are looking for the temperature that would fall into interval between the 'mid' and 'mid - 1') . Tested-by: Caesar Wang Signed-off-by: Dmitry Torokhov Signed-off-by: Eduardo Valentin --- drivers/thermal/rockchip_thermal.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index c89ffb2..93ee307 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -124,7 +124,7 @@ struct rockchip_thermal_data { #define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* msec */ struct tsadc_table { - unsigned long code; + u32 code; long temp; }; @@ -164,7 +164,6 @@ static const struct tsadc_table v2_code_table[] = { {3452, 115000}, {3437, 120000}, {3421, 125000}, - {0, 125000}, }; static u32 rk_tsadcv2_temp_to_code(long temp) @@ -191,19 +190,21 @@ static u32 rk_tsadcv2_temp_to_code(long temp) return 0; } -static int rk_tsadcv2_code_to_temp(u32 code) +static int rk_tsadcv2_code_to_temp(u32 code, int *temp) { - unsigned int low = 0; + unsigned int low = 1; unsigned int high = ARRAY_SIZE(v2_code_table) - 1; unsigned int mid = (low + high) / 2; unsigned int num; unsigned long denom; - /* Invalid code, return -EAGAIN */ - if (code > TSADCV2_DATA_MASK) - return -EAGAIN; + BUILD_BUG_ON(ARRAY_SIZE(v2_code_table) < 2); - while (low <= high && mid) { + code &= TSADCV2_DATA_MASK; + if (code < v2_code_table[high].code) + return -EAGAIN; /* Incorrect reading */ + + while (low <= high) { if (code >= v2_code_table[mid].code && code < v2_code_table[mid - 1].code) break; @@ -223,7 +224,9 @@ static int rk_tsadcv2_code_to_temp(u32 code) num = v2_code_table[mid].temp - v2_code_table[mid - 1].temp; num *= v2_code_table[mid - 1].code - code; denom = v2_code_table[mid - 1].code - v2_code_table[mid].code; - return v2_code_table[mid - 1].temp + (num / denom); + *temp = v2_code_table[mid - 1].temp + (num / denom); + + return 0; } /** @@ -281,14 +284,9 @@ static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, int *temp) { u32 val; - /* the A/D value of the channel last conversion need some time */ val = readl_relaxed(regs + TSADCV2_DATA(chn)); - if (val == 0) - return -EAGAIN; - - *temp = rk_tsadcv2_code_to_temp(val); - return 0; + return rk_tsadcv2_code_to_temp(val, temp); } static void rk_tsadcv2_tshut_temp(int chn, void __iomem *regs, long temp) -- cgit v1.1 From 452e01b3dbb54f743a200c722981ced169888860 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 7 Aug 2015 14:00:52 -0700 Subject: thermal: rockhip: fix setting thermal shutdown polarity When requested thermal shutdown signal polarity is low we need to make sure that the bit representing high level of signal is reset, and not set all other bits in that register. Also rename TSADCV2_INT_PD_CLEAR to TSADCV2_INT_PD_CLEAR_MASK to better reflect its nature. Acked-by: Caesar Wang Signed-off-by: Dmitry Torokhov Signed-off-by: Eduardo Valentin --- drivers/thermal/rockchip_thermal.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 93ee307..4d5b7d4 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -106,16 +106,14 @@ struct rockchip_thermal_data { #define TSADCV2_AUTO_PERIOD_HT 0x6c #define TSADCV2_AUTO_EN BIT(0) -#define TSADCV2_AUTO_DISABLE ~BIT(0) #define TSADCV2_AUTO_SRC_EN(chn) BIT(4 + (chn)) #define TSADCV2_AUTO_TSHUT_POLARITY_HIGH BIT(8) -#define TSADCV2_AUTO_TSHUT_POLARITY_LOW ~BIT(8) #define TSADCV2_INT_SRC_EN(chn) BIT(chn) #define TSADCV2_SHUT_2GPIO_SRC_EN(chn) BIT(4 + (chn)) #define TSADCV2_SHUT_2CRU_SRC_EN(chn) BIT(8 + (chn)) -#define TSADCV2_INT_PD_CLEAR ~BIT(8) +#define TSADCV2_INT_PD_CLEAR_MASK ~BIT(8) #define TSADCV2_DATA_MASK 0xfff #define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4 @@ -244,10 +242,10 @@ static void rk_tsadcv2_initialize(void __iomem *regs, enum tshut_polarity tshut_polarity) { if (tshut_polarity == TSHUT_HIGH_ACTIVE) - writel_relaxed(0 | (TSADCV2_AUTO_TSHUT_POLARITY_HIGH), + writel_relaxed(0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH, regs + TSADCV2_AUTO_CON); else - writel_relaxed(0 | (TSADCV2_AUTO_TSHUT_POLARITY_LOW), + writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH, regs + TSADCV2_AUTO_CON); writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD); @@ -264,7 +262,7 @@ static void rk_tsadcv2_irq_ack(void __iomem *regs) u32 val; val = readl_relaxed(regs + TSADCV2_INT_PD); - writel_relaxed(val & TSADCV2_INT_PD_CLEAR, regs + TSADCV2_INT_PD); + writel_relaxed(val & TSADCV2_INT_PD_CLEAR_MASK, regs + TSADCV2_INT_PD); } static void rk_tsadcv2_control(void __iomem *regs, bool enable) -- cgit v1.1 From 84866ee5818e95f6e97194656777c10ac24cb9d3 Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Mon, 14 Sep 2015 19:09:51 +0800 Subject: thermal: imx: register irq handler later in probe The irq handler should be registered after the tempmon module has been initialized in a known state and the thermal_zone and cpu_cooling device have been registered successfully. Otherwise, if the irq is triggled earlier before thermal probe has been finished, it may lead to 'NULL' pointer kernel panic. Signed-off-by: Bai Ping Signed-off-by: Eduardo Valentin --- drivers/thermal/imx_thermal.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 4bec1d3..acd1c78 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -487,14 +487,6 @@ static int imx_thermal_probe(struct platform_device *pdev) if (data->irq < 0) return data->irq; - ret = devm_request_threaded_irq(&pdev->dev, data->irq, - imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread, - 0, "imx_thermal", data); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); - return ret; - } - platform_set_drvdata(pdev, data); ret = imx_get_sensor_data(pdev); @@ -571,6 +563,17 @@ static int imx_thermal_probe(struct platform_device *pdev) regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); + ret = devm_request_threaded_irq(&pdev->dev, data->irq, + imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread, + 0, "imx_thermal", data); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); + clk_disable_unprepare(data->thermal_clk); + thermal_zone_device_unregister(data->tz); + cpufreq_cooling_unregister(data->cdev); + return ret; + } + data->irq_enabled = true; data->mode = THERMAL_DEVICE_ENABLED; -- cgit v1.1 From b56100db5b057fef889a73cb66c24633696d188b Mon Sep 17 00:00:00 2001 From: Nadav Haklai Date: Thu, 6 Aug 2015 18:03:49 +0200 Subject: thermal: armada: Fix possible overflow in the Armada 380 thermal sensor formula Update the coefficients so the calculation will not overrun the unsigned long 32bits boundary Signed-off-by: Nadav Haklai Reviewed-by: Victor Axelrod Reviewed-by: Neta Zur Hershkovits Signed-off-by: Gregory CLEMENT Signed-off-by: Eduardo Valentin --- drivers/thermal/armada_thermal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 26b8d32..ae75328 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -224,9 +224,9 @@ static const struct armada_thermal_data armada380_data = { .is_valid_shift = 10, .temp_shift = 0, .temp_mask = 0x3ff, - .coef_b = 2931108200UL, - .coef_m = 5000000UL, - .coef_div = 10502, + .coef_b = 1172499100UL, + .coef_m = 2000096UL, + .coef_div = 4201, .inverted = true, }; -- cgit v1.1 From 8fb2b9ac2aadd6d87f89071c2c85f8c12b41c943 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 21 Aug 2015 11:49:09 +0300 Subject: thermal: underflow bug in imx_set_trip_temp() We recently changed this from unsigned long to int so it introduced an underflow bug. Fixes: 17e8351a7739 ('thermal: consistently use int for temperatures') Signed-off-by: Dan Carpenter Signed-off-by: Eduardo Valentin --- drivers/thermal/imx_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index acd1c78..c8fe3ca 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -288,7 +288,7 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip, if (trip == IMX_TRIP_CRITICAL) return -EPERM; - if (temp > IMX_TEMP_PASSIVE) + if (temp < 0 || temp > IMX_TEMP_PASSIVE) return -EINVAL; data->temp_passive = temp; -- cgit v1.1 From 3c99c2cef75eb5bfc05c5728e4560f3ee656d47e Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Mon, 2 Nov 2015 19:03:03 +0000 Subject: thermal: devfreq_cooling: use a thermal_cooling_device for register and unregister Be consistent with what other cooling devices do and return a struct thermal_cooling_device * on register. Also, for the unregister, accept a struct thermal_cooling_device * as parameter. Cc: Zhang Rui Cc: Eduardo Valentin Signed-off-by: Javi Merino Signed-off-by: Eduardo Valentin --- drivers/thermal/devfreq_cooling.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index a272068..31e40a9 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -467,7 +467,7 @@ free_power_table: * devfreq should use the simple_ondemand governor, other governors * are not currently supported. */ -struct devfreq_cooling_device * +struct thermal_cooling_device * of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, struct devfreq_cooling_power *dfc_power) { @@ -513,7 +513,7 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, dfc->cdev = cdev; - return dfc; + return cdev; release_idr: release_idr(&devfreq_idr, dfc->id); @@ -533,7 +533,7 @@ EXPORT_SYMBOL_GPL(of_devfreq_cooling_register_power); * @np: Pointer to OF device_node. * @df: Pointer to devfreq device. */ -struct devfreq_cooling_device * +struct thermal_cooling_device * of_devfreq_cooling_register(struct device_node *np, struct devfreq *df) { return of_devfreq_cooling_register_power(np, df, NULL); @@ -544,7 +544,7 @@ EXPORT_SYMBOL_GPL(of_devfreq_cooling_register); * devfreq_cooling_register() - Register devfreq cooling device. * @df: Pointer to devfreq device. */ -struct devfreq_cooling_device *devfreq_cooling_register(struct devfreq *df) +struct thermal_cooling_device *devfreq_cooling_register(struct devfreq *df) { return of_devfreq_cooling_register(NULL, df); } @@ -554,11 +554,15 @@ EXPORT_SYMBOL_GPL(devfreq_cooling_register); * devfreq_cooling_unregister() - Unregister devfreq cooling device. * @dfc: Pointer to devfreq cooling device to unregister. */ -void devfreq_cooling_unregister(struct devfreq_cooling_device *dfc) +void devfreq_cooling_unregister(struct thermal_cooling_device *cdev) { - if (!dfc) + struct devfreq_cooling_device *dfc; + + if (!cdev) return; + dfc = cdev->devdata; + thermal_cooling_device_unregister(dfc->cdev); release_idr(&devfreq_idr, dfc->id); kfree(dfc->power_table); -- cgit v1.1 From 61c8e8aa9fa266d3b917621d69cd8086a0db558d Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Mon, 2 Nov 2015 19:03:04 +0000 Subject: thermal: devfreq_cooling: Make power a u64 The prototype of do_div() is: uint32_t do_div(uint64_t *n, uint32_t base); Make power u64 to avoid the following warning: drivers/thermal/devfreq_cooling.c: In function 'get_dynamic_power': drivers/thermal/devfreq_cooling.c:267:2: warning: comparison of distinct pointer types lacks a cast [enabled by default] drivers/thermal/devfreq_cooling.c:267:2: warning: right shift count >= width of type [enabled by default] drivers/thermal/devfreq_cooling.c:267:2: warning: passing argument 1 of '__div64_32' from incompatible pointer type [enabled by default] include/asm-generic/div64.h:35:17: note: expected 'uint64_t *' but argument is of type 'long unsigned int *' Reported-by: kbuild test robot Cc: Eduardo Valentin Cc: Zhang Rui Signed-off-by: Javi Merino Signed-off-by: Eduardo Valentin --- drivers/thermal/devfreq_cooling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index 31e40a9..d1b7c32 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -257,7 +257,7 @@ static unsigned long get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq, unsigned long voltage) { - unsigned long power; + u64 power; u32 freq_mhz; struct devfreq_cooling_power *dfc_power = dfc->power_ops; -- cgit v1.1 From 7e38a5b1daa12cbaace3c76402999a84460df3e2 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Fri, 23 Oct 2015 19:25:27 +0800 Subject: thermal: rockchip: support the sleep pinctrl state to avoid glitches in s2r When we come out of system suspend state (S3) the tsadc will have been reset and back at its default state. While reprogramming the tsadc it's possible that we'll glitch the output and unintentionally cause the "over temperature" GPIO to be asserted. Since the over temperature GPIO is often hooked up to something that will cause a reboot or shutdown in hardware, this glitch can be catastrophic on some boards. We'll add support for selecting the "sleep" pinctrl state at suspend time. Boards can use this to effectively disable the tsadc at suspend time and avoid glitches when the system is resumed. Reviewed-by: Douglas Anderson Signed-off-by: Caesar Wang Signed-off-by: Eduardo Valentin --- drivers/thermal/rockchip_thermal.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/thermal') diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 4d5b7d4..2b58870 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -638,6 +638,8 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev) clk_disable(thermal->pclk); clk_disable(thermal->clk); + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -674,6 +676,8 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev) for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) rockchip_thermal_toggle_sensor(&thermal->sensors[i], true); + pinctrl_pm_select_default_state(dev); + return 0; } -- cgit v1.1 From ce5ee1611284bef81d0308c6c2749902f55ed1d5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 4 Nov 2015 16:36:20 +0300 Subject: devfreq_cooling: return on allocation failure If the allocation fails then we can't continue. Fixes: a76caf55e5b3 ('thermal: Add devfreq cooling') Signed-off-by: Dan Carpenter Signed-off-by: Eduardo Valentin --- drivers/thermal/devfreq_cooling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index d1b7c32..01f0015 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -397,7 +397,7 @@ static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc) power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); if (!power_table) - ret = -ENOMEM; + return -ENOMEM; } freq_table = kcalloc(num_opps, sizeof(*freq_table), -- cgit v1.1 From 824ead03b78403a21449cb7eb153a4344cd3b4c8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 8 Oct 2015 14:34:02 +0900 Subject: thermal: exynos: Fix unbalanced regulator disable on probe failure During probe if the regulator could not be enabled, the error exit path would still disable it. This could lead to unbalanced counter of regulator enable/disable. The patch moves code for getting and enabling the regulator from exynos_map_dt_data() to probe function because it is really not a part of getting Device Tree properties. Acked-by: Lukasz Majewski Tested-by: Lukasz Majewski Reviewed-by: Alim Akhtar Signed-off-by: Krzysztof Kozlowski Fixes: 5f09a5cbd14a ("thermal: exynos: Disable the regulator on probe failure") Cc: Signed-off-by: Eduardo Valentin --- drivers/thermal/samsung/exynos_tmu.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 0bae8cc..23f4320 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1168,27 +1168,10 @@ static int exynos_map_dt_data(struct platform_device *pdev) struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_platform_data *pdata; struct resource res; - int ret; if (!data || !pdev->dev.of_node) return -ENODEV; - /* - * Try enabling the regulator if found - * TODO: Add regulator as an SOC feature, so that regulator enable - * is a compulsory call. - */ - data->regulator = devm_regulator_get(&pdev->dev, "vtmu"); - if (!IS_ERR(data->regulator)) { - ret = regulator_enable(data->regulator); - if (ret) { - dev_err(&pdev->dev, "failed to enable vtmu\n"); - return ret; - } - } else { - dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); - } - data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); if (data->id < 0) data->id = 0; @@ -1312,6 +1295,23 @@ static int exynos_tmu_probe(struct platform_device *pdev) pr_err("thermal: tz: %p ERROR\n", data->tzd); return PTR_ERR(data->tzd); } + + /* + * Try enabling the regulator if found + * TODO: Add regulator as an SOC feature, so that regulator enable + * is a compulsory call. + */ + data->regulator = devm_regulator_get(&pdev->dev, "vtmu"); + if (!IS_ERR(data->regulator)) { + ret = regulator_enable(data->regulator); + if (ret) { + dev_err(&pdev->dev, "failed to enable vtmu\n"); + return ret; + } + } else { + dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); + } + ret = exynos_map_dt_data(pdev); if (ret) goto err_sensor; -- cgit v1.1 From 9e4249b4034090730017deaf632b46b5faaa12b9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 8 Oct 2015 14:34:03 +0900 Subject: thermal: exynos: Fix first temperature read after registering sensor Thermal core could not read the temperature after registering the thermal sensor with thermal_zone_of_sensor_register() because the driver was not yet initialized. The call trace looked like: exynos_tmu_probe() thermal_zone_of_sensor_register() of_thermal_set_mode() thermal_zone_device_update() exynos_get_temp() if (!data->tmu_read) return -EINVAL; exynos_map_dt_data() data->tmu_read = ... This produced an error in dmesg: thermal thermal_zone0: failed to read out thermal zone (-22) Register the thermal_zone_device later, after parsing Device Tree and enabling necessary clocks, but before calling exynos_tmu_initialize() which uses the registered thermal_zone_device. Reviewed-by: Alim Akhtar Tested-by: Alim Akhtar Acked-by: Lukasz Majewski Tested-by: Lukasz Majewski Signed-off-by: Krzysztof Kozlowski Fixes: 3b6a1a805f34 ("thermal: samsung: core: Exynos TMU rework to use device tree for configuration") Signed-off-by: Eduardo Valentin --- drivers/thermal/samsung/exynos_tmu.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 23f4320..bc71a61 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1289,13 +1289,6 @@ static int exynos_tmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); mutex_init(&data->lock); - data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, - &exynos_sensor_ops); - if (IS_ERR(data->tzd)) { - pr_err("thermal: tz: %p ERROR\n", data->tzd); - return PTR_ERR(data->tzd); - } - /* * Try enabling the regulator if found * TODO: Add regulator as an SOC feature, so that regulator enable @@ -1365,21 +1358,36 @@ static int exynos_tmu_probe(struct platform_device *pdev) break; }; + /* + * data->tzd must be registered before calling exynos_tmu_initialize(), + * requesting irq and calling exynos_tmu_control(). + */ + data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, + &exynos_sensor_ops); + if (IS_ERR(data->tzd)) { + ret = PTR_ERR(data->tzd); + dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret); + goto err_sclk; + } + ret = exynos_tmu_initialize(pdev); if (ret) { dev_err(&pdev->dev, "Failed to initialize TMU\n"); - goto err_sclk; + goto err_thermal; } ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); if (ret) { dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); - goto err_sclk; + goto err_thermal; } exynos_tmu_control(pdev, true); return 0; + +err_thermal: + thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); err_sclk: clk_disable_unprepare(data->sclk); err_clk: @@ -1390,7 +1398,6 @@ err_clk_sec: err_sensor: if (!IS_ERR_OR_NULL(data->regulator)) regulator_disable(data->regulator); - thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); return ret; } -- cgit v1.1 From bfa26838572cd9b2b0623fcbc7b9352dcaa4262b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 8 Oct 2015 14:34:04 +0900 Subject: thermal: exynos: Use IS_ERR() because regulator cannot be NULL The NULL check in probe's error path is not needed because in that time the regulator cannot be NULL (regulator_get() returns valid pointer or ERR_PTR). Reviewed-by: Alim Akhtar Acked-by: Lukasz Majewski Tested-by: Lukasz Majewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Eduardo Valentin --- drivers/thermal/samsung/exynos_tmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index bc71a61..eac6aeb 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1396,7 +1396,7 @@ err_clk_sec: if (!IS_ERR(data->clk_sec)) clk_unprepare(data->clk_sec); err_sensor: - if (!IS_ERR_OR_NULL(data->regulator)) + if (!IS_ERR(data->regulator)) regulator_disable(data->regulator); return ret; -- cgit v1.1 From baba1ebb99ba1ffecfa40268d80e4e6406ef1d17 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 8 Oct 2015 14:34:05 +0900 Subject: thermal: exynos: Remove unneeded semicolon Remove semicolons after switch statement. Acked-by: Lukasz Majewski Tested-by: Lukasz Majewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Eduardo Valentin --- drivers/thermal/samsung/exynos_tmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index eac6aeb..1af7ea8 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -548,7 +548,7 @@ static int exynos5433_tmu_initialize(struct platform_device *pdev) default: pdata->cal_type = TYPE_ONE_POINT_TRIMMING; break; - }; + } dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", cal_type ? 2 : 1); @@ -1356,7 +1356,7 @@ static int exynos_tmu_probe(struct platform_device *pdev) break; default: break; - }; + } /* * data->tzd must be registered before calling exynos_tmu_initialize(), -- cgit v1.1 From e35dbb4d4b4499e8a013564f0753dabc1c49e9d9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 8 Oct 2015 14:34:06 +0900 Subject: thermal: exynos: Directly return 0 instead of using local ret variable The 'ret' variable in exynos5440_tmu_initialize() is initialized to 0 and returned as is. Replace it with direct return statement. This also fixes coccinelle warning: drivers/thermal/samsung/exynos_tmu.c:611:5-8: Unneeded variable: "ret". Return "0" on line 654 Reviewed-by: Alim Akhtar Acked-by: Lukasz Majewski Tested-by: Lukasz Majewski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Eduardo Valentin --- drivers/thermal/samsung/exynos_tmu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/thermal') diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 1af7ea8..f340e6e 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -608,7 +608,7 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); unsigned int trim_info = 0, con, rising_threshold; - int ret = 0, threshold_code; + int threshold_code; int crit_temp = 0; /* @@ -651,7 +651,8 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev) /* Clear the PMIN in the common TMU register */ if (!data->id) writel(0, data->base_second + EXYNOS5440_TMU_PMIN); - return ret; + + return 0; } static int exynos7_tmu_initialize(struct platform_device *pdev) -- cgit v1.1