diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-28 16:31:08 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-28 16:31:08 -0800 |
commit | abb22e44cff3f11d9e087bdd46c04bb32ff57678 (patch) | |
tree | 2e690946d91b9e498028503eaad73cd4f805cbec /drivers/thermal | |
parent | fb95aae6e67c4e319a24b3eea32032d4246a5335 (diff) | |
parent | 2f23e319b133c7392493e77f515a237c8e1e6dd6 (diff) |
Merge tag 'thermal-v5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux
Pull thermal updates from Daniel Lezcano:
- Depromote debug print on the db8500 platform (Linus Walleij)
- Fix compilation warning when compiling with make W=1 (Amit Kucheria)
- Code cleanup and refactoring, regmap conversion and add hwmon support
on Qoriq (Andrey Smirnov)
- Add an idle injection cpu cooling device and its documentation,
rename the cpu_cooling device to cpufreq_cooling device (Daniel
Lezcano)
- Convert unexported functions to static, add the __init annotation in
the thermal-of code and remove the pointless wrapper functions
(Daniel Lezcano)
- Fix register offset for Armada XP and register reset bit
initialization (Zak Hays)
- Enable hwmon on the rockchip (Stefan Schaeckeler)
- Add the thermal sensor for the H6/H5/H3/A64/A83T/R40 sun8i platform
and their device tree bindings, followed by a fix for the ths number
and the sparse warnings (Yangtao Li)
- Code cleansup for the sun8i and hwmon support (Yangtao Li)
- Silent some messages which are misleading given the changes made in
the previous version on generic-adc (Martin Blumenstingl)
- Rename exynos to Exynos (Krzysztof Kozlowski)
- Add the bcm2711 thermal driver with the device tree bindings (Stefan
Wahren)
- Use usleep_range() instead of udelay() as the call is always done in
a sleep-able context (Geert Uytterhoeven)
- Do code cleanup and re-organization to set the scene for a new
process for the brcmstb (Florian Fainelli)
- Fix bindings check issues on brcm (Stefan Wahren)
- Add Jasper Lake support on int340x (Nivedita Swaminathan)
- Add Comet Lake support on intel pch (Gayatri Kammela)
- Fix unmatched pci_release_region() on x86 (Chuhong Yuan)
- Remove temperature boundaries for rcar and rcar3 (Niklas Söderlund)
- Fix return value to -ENODEV when thermal_zone_of_sensor_register() is
called with the of-node is missing (Peter Mamonov)
- Code cleanup, interrupt bouncing, and better support on stm32 (Pascal
Paillet)
* tag 'thermal-v5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (66 commits)
thermal: stm32: Fix low threshold interrupt flood
thermal: stm32: Improve temperature computing
thermal: stm32: Handle multiple trip points
thermal: stm32: Disable interrupts at probe
thermal: stm32: Rework sensor mode management
thermal: stm32: Fix icifr register name
thermal: of: Make thermal_zone_of_sensor_register return -ENODEV if a sensor OF node is missing
thermal: rcar_gen3_thermal: Remove temperature bound
thermal: rcar_thermal: Remove temperature bound
thermal: intel: intel_pch_thermal: Add Comet Lake (CML) platform support
thermal: intel: Fix unmatched pci_release_region
thermal: int340x: processor_thermal: Add Jasper Lake support
dt-bindings: brcm,avs-ro-thermal: Fix binding check issues
thermal: brcmstb_thermal: Register different ops per process
thermal: brcmstb_thermal: Restructure interrupt registration
thermal: brcmstb_thermal: Add 16nm process thermal parameters
dt-bindings: thermal: Define BCM7216 thermal sensor compatible
thermal: brcmstb_thermal: Prepare to support a different process
thermal: brcmstb_thermal: Do not use DT coefficients
thermal: rcar_thermal: Use usleep_range() instead of udelay()
...
Diffstat (limited to 'drivers/thermal')
36 files changed, 1556 insertions, 578 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 79b27865c6f4..5a05db5438d6 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -151,16 +151,33 @@ config THERMAL_GOV_POWER_ALLOCATOR config CPU_THERMAL bool "Generic cpu cooling support" - depends on CPU_FREQ depends on THERMAL_OF help + Enable the CPU cooling features. If the system has no active + cooling device available, this option allows to use the CPU + as a cooling device. + +if CPU_THERMAL + +config CPU_FREQ_THERMAL + bool "CPU frequency cooling device" + depends on CPU_FREQ + default y + help This implements the generic cpu cooling mechanism through frequency reduction. An ACPI version of this already exists (drivers/acpi/processor_thermal.c). 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 here. +config CPU_IDLE_THERMAL + bool "CPU idle cooling device" + depends on IDLE_INJECT + help + This implements the CPU cooling mechanism through + idle injection. This will throttle the CPU by injecting + idle cycle. +endif config CLOCK_THERMAL bool "Generic clock cooling support" @@ -263,6 +280,20 @@ config SPEAR_THERMAL Enable this to plug the SPEAr thermal sensor driver into the Linux thermal framework. +config SUN8I_THERMAL + tristate "Allwinner sun8i thermal driver" + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM + depends on NVMEM + depends on OF + depends on RESET_CONTROLLER + help + Support for the sun8i thermal sensor driver into the Linux thermal + framework. + + To compile this driver as a module, choose M here: the + module will be called sun8i-thermal. + config ROCKCHIP_THERMAL tristate "Rockchip thermal driver" depends on ARCH_ROCKCHIP || COMPILE_TEST diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index baeb70bf0568..9fb88e26fb10 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -19,7 +19,8 @@ thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o # cpufreq cooling -thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o +thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpufreq_cooling.o +thermal_sys-$(CONFIG_CPU_IDLE_THERMAL) += cpuidle_cooling.o # clock cooling thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o @@ -31,6 +32,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o obj-y += broadcom/ obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o +obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c index 8a9e9bc421c6..ccb1fe18e993 100644 --- a/drivers/thermal/amlogic_thermal.c +++ b/drivers/thermal/amlogic_thermal.c @@ -67,7 +67,11 @@ /** * struct amlogic_thermal_soc_calib_data - * @A, B, m, n: calibration parameters + * @A: calibration parameters + * @B: calibration parameters + * @m: calibration parameters + * @n: calibration parameters + * * This structure is required for configuration of amlogic thermal driver. */ struct amlogic_thermal_soc_calib_data { diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 709a22f455e9..8c4d1244ee7a 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -155,6 +155,9 @@ static void armadaxp_init(struct platform_device *pdev, regmap_write(priv->syscon, data->syscon_control1_off, reg); + reg &= ~PMU_TDC0_SW_RST_MASK; + regmap_write(priv->syscon, data->syscon_control1_off, reg); + /* Enable the sensor */ regmap_read(priv->syscon, data->syscon_status_off, ®); reg &= ~PMU_TM_DISABLE_MASK; @@ -578,7 +581,7 @@ static const struct armada_thermal_data armadaxp_data = { .coef_m = 10000000ULL, .coef_div = 13825, .syscon_status_off = 0xb0, - .syscon_control1_off = 0xd0, + .syscon_control1_off = 0x2d0, }; static const struct armada_thermal_data armada370_data = { diff --git a/drivers/thermal/broadcom/Kconfig b/drivers/thermal/broadcom/Kconfig index cf43e1520de9..061f1db6edc9 100644 --- a/drivers/thermal/broadcom/Kconfig +++ b/drivers/thermal/broadcom/Kconfig @@ -1,4 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only +config BCM2711_THERMAL + tristate "Broadcom AVS RO thermal sensor driver" + depends on ARCH_BCM2835 || COMPILE_TEST + depends on THERMAL_OF && MFD_SYSCON + help + Support for thermal sensors on Broadcom BCM2711 SoCs. + config BCM2835_THERMAL tristate "Thermal sensors on bcm2835 SoC" depends on ARCH_BCM2835 || COMPILE_TEST diff --git a/drivers/thermal/broadcom/Makefile b/drivers/thermal/broadcom/Makefile index 490ab1f7a86d..c917b243d5f0 100644 --- a/drivers/thermal/broadcom/Makefile +++ b/drivers/thermal/broadcom/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_BCM2711_THERMAL) += bcm2711_thermal.o obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o obj-$(CONFIG_BRCMSTB_THERMAL) += brcmstb_thermal.o obj-$(CONFIG_BCM_NS_THERMAL) += ns-thermal.o diff --git a/drivers/thermal/broadcom/bcm2711_thermal.c b/drivers/thermal/broadcom/bcm2711_thermal.c new file mode 100644 index 000000000000..67c2a737bc9d --- /dev/null +++ b/drivers/thermal/broadcom/bcm2711_thermal.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Broadcom AVS RO thermal sensor driver + * + * based on brcmstb_thermal + * + * Copyright (C) 2020 Stefan Wahren + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/thermal.h> + +#include "../thermal_hwmon.h" + +#define AVS_RO_TEMP_STATUS 0x200 +#define AVS_RO_TEMP_STATUS_VALID_MSK (BIT(16) | BIT(10)) +#define AVS_RO_TEMP_STATUS_DATA_MSK GENMASK(9, 0) + +struct bcm2711_thermal_priv { + struct regmap *regmap; + struct thermal_zone_device *thermal; +}; + +static int bcm2711_get_temp(void *data, int *temp) +{ + struct bcm2711_thermal_priv *priv = data; + int slope = thermal_zone_get_slope(priv->thermal); + int offset = thermal_zone_get_offset(priv->thermal); + u32 val; + int ret; + long t; + + ret = regmap_read(priv->regmap, AVS_RO_TEMP_STATUS, &val); + if (ret) + return ret; + + if (!(val & AVS_RO_TEMP_STATUS_VALID_MSK)) + return -EIO; + + val &= AVS_RO_TEMP_STATUS_DATA_MSK; + + /* Convert a HW code to a temperature reading (millidegree celsius) */ + t = slope * val + offset; + + *temp = t < 0 ? 0 : t; + + return 0; +} + +static const struct thermal_zone_of_device_ops bcm2711_thermal_of_ops = { + .get_temp = bcm2711_get_temp, +}; + +static const struct of_device_id bcm2711_thermal_id_table[] = { + { .compatible = "brcm,bcm2711-thermal" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2711_thermal_id_table); + +static int bcm2711_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal; + struct bcm2711_thermal_priv *priv; + struct device *dev = &pdev->dev; + struct device_node *parent; + struct regmap *regmap; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* get regmap from syscon node */ + parent = of_get_parent(dev->of_node); /* parent should be syscon node */ + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "failed to get regmap: %d\n", ret); + return ret; + } + priv->regmap = regmap; + + thermal = devm_thermal_zone_of_sensor_register(dev, 0, priv, + &bcm2711_thermal_of_ops); + if (IS_ERR(thermal)) { + ret = PTR_ERR(thermal); + dev_err(dev, "could not register sensor: %d\n", ret); + return ret; + } + + priv->thermal = thermal; + + thermal->tzp->no_hwmon = false; + ret = thermal_add_hwmon_sysfs(thermal); + if (ret) + return ret; + + return 0; +} + +static struct platform_driver bcm2711_thermal_driver = { + .probe = bcm2711_thermal_probe, + .driver = { + .name = "bcm2711_thermal", + .of_match_table = bcm2711_thermal_id_table, + }, +}; +module_platform_driver(bcm2711_thermal_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Stefan Wahren"); +MODULE_DESCRIPTION("Broadcom AVS RO thermal sensor driver"); diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 5825ac581f56..8df5edef1ded 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -49,7 +49,7 @@ #define AVS_TMON_TP_TEST_ENABLE 0x20 /* Default coefficients */ -#define AVS_TMON_TEMP_SLOPE -487 +#define AVS_TMON_TEMP_SLOPE 487 #define AVS_TMON_TEMP_OFFSET 410040 /* HW related temperature constants */ @@ -102,29 +102,28 @@ static struct avs_tmon_trip avs_tmon_trips[] = { }, }; +struct brcmstb_thermal_params { + unsigned int offset; + unsigned int mult; + const struct thermal_zone_of_device_ops *of_ops; +}; + struct brcmstb_thermal_priv { void __iomem *tmon_base; struct device *dev; struct thermal_zone_device *thermal; + /* Process specific thermal parameters used for calculations */ + const struct brcmstb_thermal_params *temp_params; }; -static void avs_tmon_get_coeffs(struct thermal_zone_device *tz, int *slope, - int *offset) -{ - *slope = thermal_zone_get_slope(tz); - *offset = thermal_zone_get_offset(tz); -} - /* Convert a HW code to a temperature reading (millidegree celsius) */ -static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, +static inline int avs_tmon_code_to_temp(struct brcmstb_thermal_priv *priv, u32 code) { - const int val = code & AVS_TMON_TEMP_MASK; - int slope, offset; + int offset = priv->temp_params->offset; + int mult = priv->temp_params->mult; - avs_tmon_get_coeffs(tz, &slope, &offset); - - return slope * val + offset; + return (offset - (int)((code & AVS_TMON_TEMP_MASK) * mult)); } /* @@ -133,23 +132,22 @@ static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, * @temp: temperature to convert * @low: if true, round toward the low side */ -static inline u32 avs_tmon_temp_to_code(struct thermal_zone_device *tz, +static inline u32 avs_tmon_temp_to_code(struct brcmstb_thermal_priv *priv, int temp, bool low) { - int slope, offset; + int offset = priv->temp_params->offset; + int mult = priv->temp_params->mult; if (temp < AVS_TMON_TEMP_MIN) - return AVS_TMON_TEMP_MAX; /* Maximum code value */ - - avs_tmon_get_coeffs(tz, &slope, &offset); + return AVS_TMON_TEMP_MAX; /* Maximum code value */ if (temp >= offset) return 0; /* Minimum code value */ if (low) - return (u32)(DIV_ROUND_UP(offset - temp, abs(slope))); + return (u32)(DIV_ROUND_UP(offset - temp, mult)); else - return (u32)((offset - temp) / abs(slope)); + return (u32)((offset - temp) / mult); } static int brcmstb_get_temp(void *data, int *temp) @@ -167,7 +165,7 @@ static int brcmstb_get_temp(void *data, int *temp) val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift; - t = avs_tmon_code_to_temp(priv->thermal, val); + t = avs_tmon_code_to_temp(priv, val); if (t < 0) *temp = 0; else @@ -201,7 +199,7 @@ static int avs_tmon_get_trip_temp(struct brcmstb_thermal_priv *priv, val &= trip->reg_msk; val >>= trip->reg_shift; - return avs_tmon_code_to_temp(priv->thermal, val); + return avs_tmon_code_to_temp(priv, val); } static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv, @@ -214,7 +212,7 @@ static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv, dev_dbg(priv->dev, "set temp %d to %d\n", type, temp); /* round toward low temp for the low interrupt */ - val = avs_tmon_temp_to_code(priv->thermal, temp, + val = avs_tmon_temp_to_code(priv, temp, type == TMON_TRIP_TYPE_LOW); val <<= trip->reg_shift; @@ -231,7 +229,7 @@ static int avs_tmon_get_intr_temp(struct brcmstb_thermal_priv *priv) u32 val; val = __raw_readl(priv->tmon_base + AVS_TMON_TEMP_INT_CODE); - return avs_tmon_code_to_temp(priv->thermal, val); + return avs_tmon_code_to_temp(priv, val); } static irqreturn_t brcmstb_tmon_irq_thread(int irq, void *data) @@ -290,19 +288,37 @@ static int brcmstb_set_trips(void *data, int low, int high) return 0; } -static const struct thermal_zone_of_device_ops of_ops = { +static const struct thermal_zone_of_device_ops brcmstb_16nm_of_ops = { + .get_temp = brcmstb_get_temp, +}; + +static const struct brcmstb_thermal_params brcmstb_16nm_params = { + .offset = 457829, + .mult = 557, + .of_ops = &brcmstb_16nm_of_ops, +}; + +static const struct thermal_zone_of_device_ops brcmstb_28nm_of_ops = { .get_temp = brcmstb_get_temp, .set_trips = brcmstb_set_trips, }; +static const struct brcmstb_thermal_params brcmstb_28nm_params = { + .offset = 410040, + .mult = 487, + .of_ops = &brcmstb_28nm_of_ops, +}; + static const struct of_device_id brcmstb_thermal_id_table[] = { - { .compatible = "brcm,avs-tmon" }, + { .compatible = "brcm,avs-tmon-bcm7216", .data = &brcmstb_16nm_params }, + { .compatible = "brcm,avs-tmon", .data = &brcmstb_28nm_params }, {}, }; MODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table); static int brcmstb_thermal_probe(struct platform_device *pdev) { + const struct thermal_zone_of_device_ops *of_ops; struct thermal_zone_device *thermal; struct brcmstb_thermal_priv *priv; struct resource *res; @@ -312,6 +328,10 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->temp_params = of_device_get_match_data(&pdev->dev); + if (!priv->temp_params) + return -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->tmon_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->tmon_base)) @@ -319,9 +339,10 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) priv->dev = &pdev->dev; platform_set_drvdata(pdev, priv); + of_ops = priv->temp_params->of_ops; thermal = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, - &of_ops); + of_ops); if (IS_ERR(thermal)) { ret = PTR_ERR(thermal); dev_err(&pdev->dev, "could not register sensor: %d\n", ret); @@ -331,16 +352,15 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) priv->thermal = thermal; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "could not get IRQ\n"); - return irq; - } - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - brcmstb_tmon_irq_thread, IRQF_ONESHOT, - DRV_NAME, priv); - if (ret < 0) { - dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); - return ret; + if (irq >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + brcmstb_tmon_irq_thread, + IRQF_ONESHOT, + DRV_NAME, priv); + if (ret < 0) { + dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); + return ret; + } } dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n"); diff --git a/drivers/thermal/clock_cooling.c b/drivers/thermal/clock_cooling.c index 3ad3256c48fd..7cb3ae4b44ee 100644 --- a/drivers/thermal/clock_cooling.c +++ b/drivers/thermal/clock_cooling.c @@ -7,7 +7,7 @@ * Copyright (C) 2013 Texas Instruments Inc. * Contact: Eduardo Valentin <eduardo.valentin@ti.com> * - * Highly based on cpu_cooling.c. + * Highly based on cpufreq_cooling.c. * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> */ diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpufreq_cooling.c index 52569b27b426..fe83d7a210d4 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpufreq_cooling.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * linux/drivers/thermal/cpu_cooling.c + * linux/drivers/thermal/cpufreq_cooling.c * * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) * @@ -63,6 +63,7 @@ struct time_in_idle { * @policy: cpufreq policy. * @node: list_head to link all cpufreq_cooling_device together. * @idle_time: idle time stats + * @qos_req: PM QoS contraint to apply * * This structure is required for keeping information of each registered * cpufreq_cooling_device. @@ -620,7 +621,7 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) struct thermal_cooling_device *cdev = NULL; if (!np) { - pr_err("cpu_cooling: OF node not available for cpu%d\n", + pr_err("cpufreq_cooling: OF node not available for cpu%d\n", policy->cpu); return NULL; } @@ -630,7 +631,7 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) cdev = __cpufreq_cooling_register(np, policy, em); if (IS_ERR(cdev)) { - pr_err("cpu_cooling: cpu%d failed to register as cooling device: %ld\n", + pr_err("cpufreq_cooling: cpu%d failed to register as cooling device: %ld\n", policy->cpu, PTR_ERR(cdev)); cdev = NULL; } diff --git a/drivers/thermal/cpuidle_cooling.c b/drivers/thermal/cpuidle_cooling.c new file mode 100644 index 000000000000..0bb843246f59 --- /dev/null +++ b/drivers/thermal/cpuidle_cooling.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Limited. + * + * Author: Daniel Lezcano <daniel.lezcano@linaro.org> + * + */ +#include <linux/cpu_cooling.h> +#include <linux/cpuidle.h> +#include <linux/err.h> +#include <linux/idle_inject.h> +#include <linux/idr.h> +#include <linux/slab.h> +#include <linux/thermal.h> + +/** + * struct cpuidle_cooling_device - data for the idle cooling device + * @ii_dev: an atomic to keep track of the last task exiting the idle cycle + * @state: a normalized integer giving the state of the cooling device + */ +struct cpuidle_cooling_device { + struct idle_inject_device *ii_dev; + unsigned long state; +}; + +static DEFINE_IDA(cpuidle_ida); + +/** + * cpuidle_cooling_runtime - Running time computation + * @idle_duration_us: the idle cooling device + * @state: a percentile based number + * + * The running duration is computed from the idle injection duration + * which is fixed. If we reach 100% of idle injection ratio, that + * means the running duration is zero. If we have a 50% ratio + * injection, that means we have equal duration for idle and for + * running duration. + * + * The formula is deduced as follows: + * + * running = idle x ((100 / ratio) - 1) + * + * For precision purpose for integer math, we use the following: + * + * running = (idle x 100) / ratio - idle + * + * For example, if we have an injected duration of 50%, then we end up + * with 10ms of idle injection and 10ms of running duration. + * + * Return: An unsigned int for a usec based runtime duration. + */ +static unsigned int cpuidle_cooling_runtime(unsigned int idle_duration_us, + unsigned long state) +{ + if (!state) + return 0; + + return ((idle_duration_us * 100) / state) - idle_duration_us; +} + +/** + * cpuidle_cooling_get_max_state - Get the maximum state + * @cdev : the thermal cooling device + * @state : a pointer to the state variable to be filled + * + * The function always returns 100 as the injection ratio. It is + * percentile based for consistency accross different platforms. + * + * Return: The function can not fail, it is always zero + */ +static int cpuidle_cooling_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + /* + * Depending on the configuration or the hardware, the running + * cycle and the idle cycle could be different. We want to + * unify that to an 0..100 interval, so the set state + * interface will be the same whatever the platform is. + * + * The state 100% will make the cluster 100% ... idle. A 0% + * injection ratio means no idle injection at all and 50% + * means for 10ms of idle injection, we have 10ms of running + * time. + */ + *state = 100; + + return 0; +} + +/** + * cpuidle_cooling_get_cur_state - Get the current cooling state + * @cdev: the thermal cooling device + * @state: a pointer to the state + * + * The function just copies the state value from the private thermal + * cooling device structure, the mapping is 1 <-> 1. + * + * Return: The function can not fail, it is always zero + */ +static int cpuidle_cooling_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct cpuidle_cooling_device *idle_cdev = cdev->devdata; + + *state = idle_cdev->state; + + return 0; +} + +/** + * cpuidle_cooling_set_cur_state - Set the current cooling state + * @cdev: the thermal cooling device + * @state: the target state + * + * The function checks first if we are initiating the mitigation which + * in turn wakes up all the idle injection tasks belonging to the idle + * cooling device. In any case, it updates the internal state for the + * cooling device. + * + * Return: The function can not fail, it is always zero + */ +static int cpuidle_cooling_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct cpuidle_cooling_device *idle_cdev = cdev->devdata; + struct idle_inject_device *ii_dev = idle_cdev->ii_dev; + unsigned long current_state = idle_cdev->state; + unsigned int runtime_us, idle_duration_us; + + idle_cdev->state = state; + + idle_inject_get_duration(ii_dev, &runtime_us, &idle_duration_us); + + runtime_us = cpuidle_cooling_runtime(idle_duration_us, state); + + idle_inject_set_duration(ii_dev, runtime_us, idle_duration_us); + + if (current_state == 0 && state > 0) { + idle_inject_start(ii_dev); + } else if (current_state > 0 && !state) { + idle_inject_stop(ii_dev); + } + + return 0; +} + +/** + * cpuidle_cooling_ops - thermal cooling device ops + */ +static struct thermal_cooling_device_ops cpuidle_cooling_ops = { + .get_max_state = cpuidle_cooling_get_max_state, + .get_cur_state = cpuidle_cooling_get_cur_state, + .set_cur_state = cpuidle_cooling_set_cur_state, +}; + +/** + * cpuidle_of_cooling_register - Idle cooling device initialization function + * @drv: a cpuidle driver structure pointer + * @np: a node pointer to a device tree cooling device node + * + * This function is in charge of creating a cooling device per cpuidle + * driver and register it to thermal framework. + * + * Return: zero on success, or negative value corresponding to the + * error detected in the underlying subsystems. + */ +int cpuidle_of_cooling_register(struct device_node *np, + struct cpuidle_driver *drv) +{ + struct idle_inject_device *ii_dev; + struct cpuidle_cooling_device *idle_cdev; + struct t |