diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-29 03:31:08 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-29 03:31:08 +0300 |
commit | abb22e44cff3f11d9e087bdd46c04bb32ff57678 (patch) | |
tree | 2e690946d91b9e498028503eaad73cd4f805cbec | |
parent | fb95aae6e67c4e319a24b3eea32032d4246a5335 (diff) | |
parent | 2f23e319b133c7392493e77f515a237c8e1e6dd6 (diff) | |
download | linux-abb22e44cff3f11d9e087bdd46c04bb32ff57678.tar.xz |
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()
...
48 files changed, 2012 insertions, 590 deletions
diff --git a/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml new file mode 100644 index 000000000000..87369264feb9 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/allwinner,sun8i-a83t-ths.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner SUN8I Thermal Controller Device Tree Bindings + +maintainers: + - Vasily Khoruzhick <anarsoul@gmail.com> + - Yangtao Li <tiny.windzz@gmail.com> + +properties: + compatible: + enum: + - allwinner,sun8i-a83t-ths + - allwinner,sun8i-h3-ths + - allwinner,sun8i-r40-ths + - allwinner,sun50i-a64-ths + - allwinner,sun50i-h5-ths + - allwinner,sun50i-h6-ths + + clocks: + minItems: 1 + maxItems: 2 + items: + - description: Bus Clock + - description: Module Clock + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: bus + - const: mod + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + + nvmem-cells: + maxItems: 1 + description: Calibration data for thermal sensors + + nvmem-cell-names: + const: calibration + + # See ./thermal.txt for details + "#thermal-sensor-cells": + enum: + - 0 + - 1 + +allOf: + - if: + properties: + compatible: + contains: + const: allwinner,sun50i-h6-ths + + then: + properties: + clocks: + maxItems: 1 + + clock-names: + maxItems: 1 + + else: + properties: + clocks: + minItems: 2 + + clock-names: + minItems: 2 + + - if: + properties: + compatible: + contains: + const: allwinner,sun8i-h3-ths + + then: + properties: + "#thermal-sensor-cells": + const: 0 + + else: + properties: + "#thermal-sensor-cells": + const: 1 + + - if: + properties: + compatible: + contains: + enum: + - const: allwinner,sun8i-h3-ths + - const: allwinner,sun8i-r40-ths + - const: allwinner,sun50i-a64-ths + - const: allwinner,sun50i-h5-ths + - const: allwinner,sun50i-h6-ths + + then: + required: + - clocks + - clock-names + - resets + +required: + - compatible + - reg + - interrupts + - '#thermal-sensor-cells' + +additionalProperties: false + +examples: + - | + thermal-sensor@1f04000 { + compatible = "allwinner,sun8i-a83t-ths"; + reg = <0x01f04000 0x100>; + interrupts = <0 31 0>; + nvmem-cells = <&ths_calibration>; + nvmem-cell-names = "calibration"; + #thermal-sensor-cells = <1>; + }; + + - | + thermal-sensor@1c25000 { + compatible = "allwinner,sun8i-h3-ths"; + reg = <0x01c25000 0x400>; + clocks = <&ccu 0>, <&ccu 1>; + clock-names = "bus", "mod"; + resets = <&ccu 2>; + interrupts = <0 31 0>; + nvmem-cells = <&ths_calibration>; + nvmem-cell-names = "calibration"; + #thermal-sensor-cells = <0>; + }; + + - | + thermal-sensor@5070400 { + compatible = "allwinner,sun50i-h6-ths"; + reg = <0x05070400 0x100>; + clocks = <&ccu 0>; + clock-names = "bus"; + resets = <&ccu 2>; + interrupts = <0 15 0>; + nvmem-cells = <&ths_calibration>; + nvmem-cell-names = "calibration"; + #thermal-sensor-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/thermal/brcm,avs-ro-thermal.yaml b/Documentation/devicetree/bindings/thermal/brcm,avs-ro-thermal.yaml new file mode 100644 index 000000000000..d9fdf4809a49 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/brcm,avs-ro-thermal.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/brcm,avs-ro-thermal.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom AVS ring oscillator thermal + +maintainers: + - Stefan Wahren <wahrenst@gmx.net> + +description: |+ + The thermal node should be the child of a syscon node with the + required property: + + - compatible: Should be one of the following: + "brcm,bcm2711-avs-monitor", "syscon", "simple-mfd" + + Refer to the the bindings described in + Documentation/devicetree/bindings/mfd/syscon.txt + +properties: + compatible: + const: brcm,bcm2711-thermal + + # See ./thermal.txt for details + "#thermal-sensor-cells": + const: 0 + +required: + - compatible + - '#thermal-sensor-cells' + +additionalProperties: false + +examples: + - | + avs-monitor@7d5d2000 { + compatible = "brcm,bcm2711-avs-monitor", + "syscon", "simple-mfd"; + reg = <0x7d5d2000 0xf00>; + + thermal: thermal { + compatible = "brcm,bcm2711-thermal"; + #thermal-sensor-cells = <0>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/thermal/brcm,avs-tmon.txt b/Documentation/devicetree/bindings/thermal/brcm,avs-tmon.txt index 43a9ed545944..74a9ef09db8b 100644 --- a/Documentation/devicetree/bindings/thermal/brcm,avs-tmon.txt +++ b/Documentation/devicetree/bindings/thermal/brcm,avs-tmon.txt @@ -3,9 +3,13 @@ Thermal management core, provided by the AVS TMON hardware block. Required properties: -- compatible: must be "brcm,avs-tmon" and/or "brcm,avs-tmon-bcm7445" +- compatible: must be one of: + "brcm,avs-tmon-bcm7216" + "brcm,avs-tmon-bcm7445" + "brcm,avs-tmon" - reg: address range for the AVS TMON registers -- interrupts: temperature monitor interrupt, for high/low threshold triggers +- interrupts: temperature monitor interrupt, for high/low threshold triggers, + required except for "brcm,avs-tmon-bcm7216" - interrupt-names: should be "tmon" Example: diff --git a/Documentation/driver-api/thermal/cpu-idle-cooling.rst b/Documentation/driver-api/thermal/cpu-idle-cooling.rst new file mode 100644 index 000000000000..e4f0859486c7 --- /dev/null +++ b/Documentation/driver-api/thermal/cpu-idle-cooling.rst @@ -0,0 +1,189 @@ + +Situation: +---------- + +Under certain circumstances a SoC can reach a critical temperature +limit and is unable to stabilize the temperature around a temperature +control. When the SoC has to stabilize the temperature, the kernel can +act on a cooling device to mitigate the dissipated power. When the +critical temperature is reached, a decision must be taken to reduce +the temperature, that, in turn impacts performance. + +Another situation is when the silicon temperature continues to +increase even after the dynamic leakage is reduced to its minimum by +clock gating the component. This runaway phenomenon can continue due +to the static leakage. The only solution is to power down the +component, thus dropping the dynamic and static leakage that will +allow the component to cool down. + +Last but not least, the system can ask for a specific power budget but +because of the OPP density, we can only choose an OPP with a power +budget lower than the requested one and under-utilize the CPU, thus +losing performance. In other words, one OPP under-utilizes the CPU +with a power less than the requested power budget and the next OPP +exceeds the power budget. An intermediate OPP could have been used if +it were present. + +Solutions: +---------- + +If we can remove the static and the dynamic leakage for a specific +duration in a controlled period, the SoC temperature will +decrease. Acting on the idle state duration or the idle cycle +injection period, we can mitigate the temperature by modulating the +power budget. + +The Operating Performance Point (OPP) density has a great influence on +the control precision of cpufreq, however different vendors have a +plethora of OPP density, and some have large power gap between OPPs, +that will result in loss of performance during thermal control and +loss of power in other scenarios. + +At a specific OPP, we can assume that injecting idle cycle on all CPUs +belong to the same cluster, with a duration greater than the cluster +idle state target residency, we lead to dropping the static and the +dynamic leakage for this period (modulo the energy needed to enter +this state). So the sustainable power with idle cycles has a linear +relation with the OPP’s sustainable power and can be computed with a +coefficient similar to: + + Power(IdleCycle) = Coef x Power(OPP) + +Idle Injection: +--------------- + +The base concept of the idle injection is to force the CPU to go to an +idle state for a specified time each control cycle, it provides +another way to control CPU power and heat in addition to +cpufreq. Ideally, if all CPUs belonging to the same cluster, inject +their idle cycles synchronously, the cluster can reach its power down +state with a minimum power consumption and reduce the static leakage +to almost zero. However, these idle cycles injection will add extra +latencies as the CPUs will have to wakeup from a deep sleep state. + +We use a fixed duration of idle injection that gives an acceptable +performance penalty and a fixed latency. Mitigation can be increased +or decreased by modulating the duty cycle of the idle injection. + + ^ + | + | + |------- ------- + |_______|_______________________|_______|___________ + + <------> + idle <----------------------> + running + + <-----------------------------> + duty cycle 25% + + +The implementation of the cooling device bases the number of states on +the duty cycle percentage. When no mitigation is happening the cooling +device state is zero, meaning the duty cycle is 0%. + +When the mitigation begins, depending on the governor's policy, a +starting state is selected. With a fixed idle duration and the duty +cycle (aka the cooling device state), the running duration can be +computed. + +The governor will change the cooling device state thus the duty cycle +and this variation will modulate the cooling effect. + + ^ + | + | + |------- ------- + |_______|_______________|_______|___________ + + <------> + idle <--------------> + running + + <-----------------------------> + duty cycle 33% + + + ^ + | + | + |------- ------- + |_______|_______|_______|___________ + + <------> + idle <------> + running + + <-------------> + duty cycle 50% + +The idle injection duration value must comply with the constraints: + +- It is less than or equal to the latency we tolerate when the + mitigation begins. It is platform dependent and will depend on the + user experience, reactivity vs performance trade off we want. This + value should be specified. + +- It is greater than the idle state’s target residency we want to go + for thermal mitigation, otherwise we end up consuming more energy. + +Power considerations +-------------------- + +When we reach the thermal trip point, we have to sustain a specified +power for a specific temperature but at this time we consume: + + Power = Capacitance x Voltage^2 x Frequency x Utilisation + +... which is more than the sustainable power (or there is something +wrong in the system setup). The ‘Capacitance’ and ‘Utilisation’ are a +fixed value, ‘Voltage’ and the ‘Frequency’ are fixed artificially +because we don’t want to change the OPP. We can group the +‘Capacitance’ and the ‘Utilisation’ into a single term which is the +‘Dynamic Power Coefficient (Cdyn)’ Simplifying the above, we have: + + Pdyn = Cdyn x Voltage^2 x Frequency + +The power allocator governor will ask us somehow to reduce our power +in order to target the sustainable power defined in the device +tree. So with the idle injection mechanism, we want an average power +(Ptarget) resulting in an amount of time running at full power on a +specific OPP and idle another amount of time. That could be put in a +equation: + + P(opp)target = ((Trunning x (P(opp)running) + (Tidle x P(opp)idle)) / + (Trunning + Tidle) + ... + + Tidle = Trunning x ((P(opp)running / P(opp)target) - 1) + +At this point if we know the running period for the CPU, that gives us +the idle injection we need. Alternatively if we have the idle +injection duration, we can compute the running duration with: + + Trunning = Tidle / ((P(opp)running / P(opp)target) - 1) + +Practically, if the running power is less than the targeted power, we +end up with a negative time value, so obviously the equation usage is +bound to a power reduction, hence a higher OPP is needed to have the +running power greater than the targeted power. + +However, in this demonstration we ignore three aspects: + + * The static leakage is not defined here, we can introduce it in the + equation but assuming it will be zero most of the time as it is + difficult to get the values from the SoC vendors + + * The idle state wake up latency (or entry + exit latency) is not + taken into account, it must be added in the equation in order to + rigorously compute the idle injection + + * The injected idle duration must be greater than the idle state + target residency, otherwise we end up consuming more energy and + potentially invert the mitigation effect + +So the final equation is: + + Trunning = (Tidle - Twakeup ) x + (((P(opp)dyn + P(opp)static ) - P(opp)target) / P(opp)target ) diff --git a/Documentation/driver-api/thermal/exynos_thermal.rst b/Documentation/driver-api/thermal/exynos_thermal.rst index 5bd556566c70..764df4ab584d 100644 --- a/Documentation/driver-api/thermal/exynos_thermal.rst +++ b/Documentation/driver-api/thermal/exynos_thermal.rst @@ -4,7 +4,7 @@ Kernel driver exynos_tmu Supported chips: -* ARM SAMSUNG EXYNOS4, EXYNOS5 series of SoC +* ARM Samsung Exynos4, Exynos5 series of SoC Datasheet: Not publicly available @@ -14,7 +14,7 @@ Authors: Amit Daniel <amit.daniel@samsung.com> TMU controller Description: --------------------------- -This driver allows to read temperature inside SAMSUNG EXYNOS4/5 series of SoC. +This driver allows to read temperature inside Samsung Exynos4/5 series of SoC. The chip only exposes the measured 8-bit temperature code value through a register. @@ -43,7 +43,7 @@ The three equations are: Trimming info for 85 degree Celsius (stored at TRIMINFO register) Temperature code measured at 85 degree Celsius which is unchanged -TMU(Thermal Management Unit) in EXYNOS4/5 generates interrupt +TMU(Thermal Management Unit) in Exynos4/5 generates interrupt when temperature exceeds pre-defined levels. The maximum number of configurable threshold is five. The threshold levels are defined as follows:: @@ -67,7 +67,7 @@ TMU driver description: The exynos thermal driver is structured as:: Kernel Core thermal framework - (thermal_core.c, step_wise.c, cpu_cooling.c) + (thermal_core.c, step_wise.c, cpufreq_cooling.c) ^ | | diff --git a/MAINTAINERS b/MAINTAINERS index 3e6fa02e14b7..d51ce1a0a817 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -694,6 +694,14 @@ L: linux-crypto@vger.kernel.org S: Maintained F: drivers/crypto/allwinner/ +ALLWINNER THERMAL DRIVER +M: Vasily Khoruzhick <anarsoul@gmail.com> +M: Yangtao Li <tiny.windzz@gmail.com> +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml +F: drivers/thermal/sun8i_thermal.c + ALLWINNER VPU DRIVER M: Maxime Ripard <mripard@kernel.org> M: Paul Kocialkowski <paul.kocialkowski@bootlin.com> @@ -16432,12 +16440,15 @@ F: Documentation/devicetree/bindings/thermal/ THERMAL/CPU_COOLING M: Amit Daniel Kachhap <amit.kachhap@gmail.com> +M: Daniel Lezcano <daniel.lezcano@linaro.org> M: Viresh Kumar <viresh.kumar@linaro.org> M: Javi Merino <javi.merino@kernel.org> L: linux-pm@vger.kernel.org S: Supported F: Documentation/driver-api/thermal/cpu-cooling-api.rst -F: drivers/thermal/cpu_cooling.c +F: Documentation/driver-api/thermal/cpu-idle-cooling.rst +F: drivers/thermal/cpufreq_cooling.c +F: drivers/thermal/cpuidle_cooling.c F: include/linux/cpu_cooling.h THERMAL DRIVER FOR AMLOGIC SOCS diff --git a/arch/arm/boot/dts/bcm2711.dtsi b/arch/arm/boot/dts/bcm2711.dtsi index e2f6ffb00aa9..8eedd6eb409b 100644 --- a/arch/arm/boot/dts/bcm2711.dtsi +++ b/arch/arm/boot/dts/bcm2711.dtsi @@ -66,6 +66,17 @@ IRQ_TYPE_LEVEL_HIGH)>; }; + avs_monitor: avs-monitor@7d5d2000 { + compatible = "brcm,bcm2711-avs-monitor", + "syscon", "simple-mfd"; + reg = <0x7d5d2000 0xf00>; + + thermal: thermal { + compatible = "brcm,bcm2711-thermal"; + #thermal-sensor-cells = <0>; + }; + }; + dma: dma@7e007000 { compatible = "brcm,bcm2835-dma"; reg = <0x7e007000 0xb00>; @@ -363,6 +374,7 @@ &cpu_thermal { coefficients = <(-487) 410040>; + thermal-sensors = <&thermal>; }; &dsi0 { diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 3f1b96dc7faa..f5d19cc1f4f2 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -496,6 +496,7 @@ CONFIG_IMX_THERMAL=y CONFIG_ROCKCHIP_THERMAL=y CONFIG_RCAR_THERMAL=y CONFIG_ARMADA_THERMAL=y +CONFIG_BCM2711_THERMAL=m CONFIG_BCM2835_THERMAL=m CONFIG_BRCMSTB_THERMAL=m CONFIG_ST_THERMAL_MEMMAP=y diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 6a83ba2aea3e..b2f667307f82 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -442,6 +442,7 @@ CONFIG_ROCKCHIP_THERMAL=m CONFIG_RCAR_THERMAL=y CONFIG_RCAR_GEN3_THERMAL=y CONFIG_ARMADA_THERMAL=y +CONFIG_BCM2711_THERMAL=m CONFIG_BCM2835_THERMAL=m CONFIG_BRCMSTB_THERMAL=m CONFIG_EXYNOS_THERMAL=y 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 thermal_cooling_device *cdev; + char dev_name[THERMAL_NAME_LENGTH]; + int id, ret; + + idle_cdev = kzalloc(sizeof(*idle_cdev), GFP_KERNEL); + if (!idle_cdev) { + ret = -ENOMEM; + goto out; + } + + id = ida_simple_get(&cpuidle_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto out_kfree; + } + + ii_dev = idle_inject_register(drv->cpumask); + if (!ii_dev) { + ret = -EINVAL; + goto out_id; + } + + idle_inject_set_duration(ii_dev, TICK_USEC, TICK_USEC); + + idle_cdev->ii_dev = ii_dev; + + snprintf(dev_name, sizeof(dev_name), "thermal-idle-%d", id); + + cdev = thermal_of_cooling_device_register(np, dev_name, idle_cdev, + &cpuidle_cooling_ops); + if (IS_ERR(cdev)) { + ret = PTR_ERR(cdev); + goto out_unregister; + } + + return 0; + +out_unregister: + idle_inject_unregister(ii_dev); +out_id: + ida_simple_remove(&cpuidle_ida, id); +out_kfree: + kfree(idle_cdev); +out: + return ret; +} + +/** + * cpuidle_cooling_register - Idle cooling device initialization function + * @drv: a cpuidle driver structure pointer + * + * 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_cooling_register(struct cpuidle_driver *drv) +{ + return cpuidle_of_cooling_register(NULL, drv); +} diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index 372dbbaaafb8..21d4d6e6409a 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -152,8 +152,8 @@ static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data) db8500_thermal_update_config(th, idx, THERMAL_TREND_RAISING, next_low, next_high); - dev_info(&th->tz->device, - "PRCMU set max %ld, min %ld\n", next_high, next_low); + dev_dbg(&th->tz->device, + "PRCMU set max %ld, min %ld\n", next_high, next_low); } else if (idx == num_points - 1) /* So we roof out 1 degree over the max point */ th->interpolated_temp = db8500_thermal_points[idx] + 1; diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index ef59256887ff..a87d4fa031c8 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -53,6 +53,7 @@ static DEFINE_IDA(devfreq_ida); * 'utilization' (which is 'busy_time / 'total_time'). * The 'res_util' range is from 100 to (power_table[state] * 100) * for the corresponding 'state'. + * @capped_state: index to cooling state with in dynamic power budget */ struct devfreq_cooling_device { int id; @@ -587,7 +588,7 @@ EXPORT_SYMBOL_GPL(devfreq_cooling_register); /** * devfreq_cooling_unregister() - Unregister devfreq cooling device. - * @dfc: Pointer to devfreq cooling device to unregister. + * @cdev: Pointer to devfreq cooling device to unregister. */ void devfreq_cooling_unregister(struct thermal_cooling_device *cdev) { diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c index afd99f668c65..aaa07180ab48 100644 --- a/drivers/thermal/fair_share.c +++ b/drivers/thermal/fair_share.c @@ -58,8 +58,8 @@ static long get_target_state(struct thermal_zone_device *tz, /** * fair_share_throttle - throttles devices associated with the given zone - * @tz - thermal_zone_device - * @trip - trip point index + * @tz: thermal_zone_device + * @trip: trip point index * * Throttling Logic: This uses three parameters to calculate the new * throttle state of the cooling devices associated with the given zone. diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index b831fc77cf64..991a1c54296d 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -71,8 +71,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) /** * bang_bang_control - controls devices associated with the given zone - * @tz - thermal_zone_device - * @trip - the trip point + * @tz: thermal_zone_device + * @trip: the trip point * * Regulation Logic: a two point regulation, deliver cooling state depending * on the previous state shown in this diagram: diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c index 89a015387283..b1fd34516e28 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c @@ -42,6 +42,9 @@ /* IceLake thermal reporting device */ #define PCI_DEVICE_ID_PROC_ICL_THERMAL 0x8a03 +/* JasperLake thermal reporting device */ +#define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4503 + #define DRV_NAME "proc_thermal" struct power_config { @@ -724,6 +727,7 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_GLK_THERMAL)}, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_ICL_THERMAL), .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_JSL_THERMAL)}, { 0, }, }; diff --git a/drivers/thermal/intel/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c index 4f0bb8f502e1..ed75a0c603e7 100644 --- a/drivers/thermal/intel/intel_pch_thermal.c +++ b/drivers/thermal/intel/intel_pch_thermal.c @@ -23,6 +23,7 @@ #define PCH_THERMAL_DID_SKL_H 0xA131 /* Skylake PCH 100 series */ #define PCH_THERMAL_DID_CNL 0x9Df9 /* CNL PCH */ #define PCH_THERMAL_DID_CNL_H 0xA379 /* CNL-H PCH */ +#define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */ /* Wildcat Point-LP PCH Thermal registers */ #define WPT_TEMP 0x0000 /* Temperature */ @@ -272,6 +273,7 @@ enum board_ids { board_wpt, board_skl, board_cnl, + board_cml, }; static const struct board_info { @@ -294,6 +296,10 @@ static const struct board_info { .name = "pch_cannonlake", .ops = &pch_dev_ops_wpt, }, + [board_cml] = { + .name = "pch_cometlake", + .ops = &pch_dev_ops_wpt, + } }; static int intel_pch_thermal_probe(struct pci_dev *pdev, @@ -365,7 +371,7 @@ static void intel_pch_thermal_remove(struct pci_dev *pdev) thermal_zone_device_unregister(ptd->tzd); iounmap(ptd->hw_base); pci_set_drvdata(pdev, NULL); - pci_release_region(pdev, 0); + pci_release_regions(pdev); pci_disable_device(pdev); } @@ -398,6 +404,8 @@ static const struct pci_device_id intel_pch_thermal_id[] = { .driver_data = board_cnl, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H), .driver_data = board_cnl, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H), + .driver_data = board_cml, }, { 0, }, }; MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id); diff --git a/drivers/thermal/max77620_thermal.c b/drivers/thermal/max77620_thermal.c index 88fd0fbe0cfa..82d06c7411eb 100644 --- a/drivers/thermal/max77620_thermal.c +++ b/drivers/thermal/max77620_thermal.c @@ -33,7 +33,7 @@ struct max77620_therm_info { /** * max77620_thermal_read_temp: Read PMIC die temperatue. * @data: Device specific data. - * temp: Temperature in millidegrees Celsius + * @temp: Temperature in millidegrees Celsius * * The actual temperature of PMIC die is not available from PMIC. * PMIC only tells the status if it has crossed or not the threshold level diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index acf4854cbb8b..76e30603d4d5 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -358,7 +358,7 @@ static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, }; static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 }; static const int mt7622_tc_offset[MT7622_NUM_CONTROLLER] = { 0x0, }; -/** +/* * The MT8173 thermal controller has four banks. Each bank can read up to * four temperature sensors simultaneously. The MT8173 has a total of 5 * temperature sensors. We use each bank to measure a certain area of the @@ -400,7 +400,7 @@ static const struct mtk_thermal_data mt8173_thermal_data = { .sensor_mux_values = mt8173_mux_values, }; -/** +/* * The MT2701 thermal controller has one bank, which can read up to * three temperature sensors simultaneously. The MT2701 has a total of 3 * temperature sensors. @@ -430,7 +430,7 @@ static const struct mtk_thermal_data mt2701_thermal_data = { .sensor_mux_values = mt2701_mux_values, }; -/** +/* * The MT2712 thermal controller has one bank, which can read up to * four temperature sensors simultaneously. The MT2712 has a total of 4 * temperature sensors. @@ -484,7 +484,7 @@ static const struct mtk_thermal_data mt7622_thermal_data = { .sensor_mux_values = mt7622_mux_values, }; -/** +/* * The MT8183 thermal controller has one bank for the current SW framework. * The MT8183 has a total of 6 temperature sensors. * There are two thermal controller to control the six sensor. @@ -495,7 +495,6 @@ static const struct mtk_thermal_data mt7622_thermal_data = { * data, and this indeed needs the temperatures of the individual banks * for making better decisions. */ - static const struct mtk_thermal_data mt8183_thermal_data = { .auxadc_channel = MT8183_TEMP_AUXADC_CHANNEL, .num_banks = MT8183_NUM_SENSORS_PER_ZONE, @@ -519,7 +518,8 @@ static const struct mtk_thermal_data mt8183_thermal_data = { /** * raw_to_mcelsius - convert a raw ADC value to mcelsius - * @mt: The thermal controller + * @mt: The thermal controller + * @sensno: sensor number * @raw: raw ADC value * * This converts the raw ADC value to mcelsius using the SoC specific diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index dc5093be553e..ef0baa954ff0 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -493,7 +493,7 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, if (!dev || !dev->of_node) { of_node_put(np); - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENODEV); } sensor_np = of_node_get(dev->of_node); @@ -754,7 +754,7 @@ end: return ret; } -/** +/* * It maps 'enum thermal_trip_type' found in include/linux/thermal.h * into the device tree binding of 'trip', property type. */ @@ -977,7 +977,7 @@ free_tz: return ERR_PTR(ret); } -static inline void of_thermal_free_zone(struct __thermal_zone *tz) +static __init void of_thermal_free_zone(struct __thermal_zone *tz) { struct __thermal_bind_params *tbp; int i, j; @@ -999,6 +999,38 @@ static inline void of_thermal_free_zone(struct __thermal_zone *tz) } /** + * of_thermal_destroy_zones - remove all zones parsed and allocated resources + * + * Finds all zones parsed and added to the thermal framework and remove them + * from the system, together with their resources. + * + */ +static __init void of_thermal_destroy_zones(void) +{ + struct device_node *np, *child; + + np = of_find_node_by_name(NULL, "thermal-zones"); + if (!np) { + pr_debug("unable to find thermal zones\n"); + return; + } + + for_each_available_child_of_node(np, child) { + struct thermal_zone_device *zone; + + zone = thermal_zone_get_zone_by_name(child->name); + if (IS_ERR(zone)) + continue; + + thermal_zone_device_unregister(zone); + kfree(zone->tzp); + kfree(zone->ops); + of_thermal_free_zone(zone->devdata); + } + of_node_put(np); +} + +/** * of_parse_thermal_zones - parse device tree thermal data * * Initialization function that can be called by machine initialization @@ -1087,35 +1119,3 @@ exit_free: return -ENOMEM; } - -/** - * of_thermal_destroy_zones - remove all zones parsed and allocated resources - * - * Finds all zones parsed and added to the thermal framework and remove them - * from the system, together with their resources. - * - */ -void of_thermal_destroy_zones(void) -{ - struct device_node *np, *child; - - np = of_find_node_by_name(NULL, "thermal-zones"); - if (!np) { - pr_debug("unable to find thermal zones\n"); - return; - } - - for_each_available_child_of_node(np, child) { - struct thermal_zone_device *zone; - - zone = thermal_zone_get_zone_by_name(child->name); - if (IS_ERR(zone)) - continue; - - thermal_zone_device_unregister(zone); - kfree(zone->tzp); - kfree(zone->ops); - of_thermal_free_zone(zone->devdata); - } - of_node_put(np); -} diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 45e9fcb172cc..874bc46e6c73 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -9,9 +9,12 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/regmap.h> +#include <linux/sizes.h> #include <linux/thermal.h> #include "thermal_core.h" +#include "thermal_hwmon.h" #define SITES_MAX 16 #define TMR_DISABLE 0x0 @@ -24,129 +27,80 @@ #define TMU_VER1 0x1 #define TMU_VER2 0x2 -/* - * QorIQ TMU Registers - */ -struct qoriq_tmu_site_regs { - u32 tritsr; /* Immediate Temperature Site Register */ - u32 tratsr; /* Average Temperature Site Register */ - u8 res0[0x8]; -}; +#define REGS_TMR 0x000 /* Mode Register */ +#define TMR_DISABLE 0x0 +#define TMR_ME 0x80000000 +#define TMR_ALPF 0x0c000000 +#define TMR_MSITE_ALL GENMASK(15, 0) -struct qoriq_tmu_regs_v1 { - u32 tmr; /* Mode Register */ - u32 tsr; /* Status Register */ - u32 tmtmir; /* Temperature measurement interval Register */ - u8 res0[0x14]; - u32 tier; /* Interrupt Enable Register */ - u32 tidr; /* Interrupt Detect Register */ - u32 tiscr; /* Interrupt Site Capture Register */ - u32 ticscr; /* Interrupt Critical Site Capture Register */ - u8 res1[0x10]; - u32 tmhtcrh; /* High Temperature Capture Register */ - u32 tmhtcrl; /* Low Temperature Capture Register */ - u8 res2[0x8]; - u32 tmhtitr; /* High Temperature Immediate Threshold */ - u32 tmhtatr; /* High Temperature Average Threshold */ - u32 tmhtactr; /* High Temperature Average Crit Threshold */ - u8 res3[0x24]; - u32 ttcfgr; /* Temperature Configuration Register */ - u32 tscfgr; /* Sensor Configuration Register */ - u8 res4[0x78]; - struct qoriq_tmu_site_regs site[SITES_MAX]; - u8 res5[0x9f8]; - u32 ipbrr0; /* IP Block Revision Register 0 */ - u32 ipbrr1; /* IP Block Revision Register 1 */ - u8 res6[0x310]; - u32 ttrcr[4]; /* Temperature Range Control Register */ -}; +#define REGS_TMTMIR 0x008 /* Temperature measurement interval Register */ +#define TMTMIR_DEFAULT 0x0000000f -struct qoriq_tmu_regs_v2 { - u32 tmr; /* Mode Register */ - u32 tsr; /* Status Register */ - u32 tmsr; /* monitor site register */ - u32 tmtmir; /* Temperature measurement interval Register */ - u8 res0[0x10]; - u32 tier; /* Interrupt Enable Register */ - u32 tidr; /* Interrupt Detect Register */ - u8 res1[0x8]; - u32 tiiscr; /* interrupt immediate site capture register */ - u32 tiascr; /* interrupt average site capture register */ - u32 ticscr; /* Interrupt Critical Site Capture Register */ - u32 res2; - u32 tmhtcr; /* monitor high temperature capture register */ - u32 tmltcr; /* monitor low temperature capture register */ - u32 tmrtrcr; /* monitor rising temperature rate capture register */ - u32 tmftrcr; /* monitor falling temperature rate capture register */ - u32 tmhtitr; /* High Temperature Immediate Threshold */ - u32 tmhtatr; /* High Temperature Average Threshold */ - u32 tmhtactr; /* High Temperature Average Crit Threshold */ - u32 res3; - u32 tmltitr; /* monitor low temperature immediate threshold */ - u32 tmltatr; /* monitor low temperature average threshold register */ - u32 tmltactr; /* monitor low temperature average critical threshold */ - u32 res4; - u32 tmrtrctr; /* monitor rising temperature rate critical threshold */ - u32 tmftrctr; /* monitor falling temperature rate critical threshold*/ - u8 res5[0x8]; - u32 ttcfgr; /* Temperature Configuration Register */ - u32 tscfgr; /* Sensor Configuration Register */ - u8 res6[0x78]; - struct qoriq_tmu_site_regs site[SITES_MAX]; - u8 res7[0x9f8]; - u32 ipbrr0; /* IP Block Revision Register 0 */ - u32 ipbrr1; /* IP Block Revision Register 1 */ - u8 res8[0x300]; - u32 teumr0; - u32 teumr1; - u32 teumr2; - u32 res9; - u32 ttrcr[4]; /* Temperature Range Control Register */ -}; +#define REGS_V2_TMSR 0x008 /* monitor site register */ -struct qoriq_tmu_data; +#define REGS_V2_TMTMIR 0x00c /* Temperature measurement interval Register */ + +#define REGS_TIER 0x020 /* Interrupt Enable Register */ +#define TIER_DISABLE 0x0 + + +#define REGS_TTCFGR 0x080 /* Temperature Configuration Register */ +#define REGS_TSCFGR 0x084 /* Sensor Configuration Register */ + +#define REGS_TRITSR(n) (0x100 + 16 * (n)) /* Immediate Temperature + * Site Register + */ +#define TRITSR_V BIT(31) +#define REGS_TTRnCR(n) (0xf10 + 4 * (n)) /* Temperature Range n + * Control Register + */ +#define REGS_IPBRR(n) (0xbf8 + 4 * (n)) /* IP Block Revision + * Register n + */ +#define REGS_V2_TEUMR(n) (0xf00 + 4 * (n)) /* * Thermal zone data */ struct qoriq_sensor { - struct thermal_zone_device *tzd; - struct qoriq_tmu_data *qdata; int id; }; struct qoriq_tmu_data { int ver; - struct qoriq_tmu_regs_v1 __iomem *regs; - struct qoriq_tmu_regs_v2 __iomem *regs_v2; + struct regmap *regmap; struct clk *clk; - bool little_endian; - struct qoriq_sensor *sensor[SITES_MAX]; + struct qoriq_sensor sensor[SITES_MAX]; }; -static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr) +static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s) { - if (p->little_endian) - iowrite32(val, addr); - else - iowrite32be(val, addr); -} - -static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) -{ - if (p->little_endian) - return ioread32(addr); - else - return ioread32be(addr); + return container_of(s, struct qoriq_tmu_data, sensor[s->id]); } static int tmu_get_temp(void *p, int *temp) { struct qoriq_sensor *qsensor = p; - struct qoriq_tmu_data *qdata = qsensor->qdata; + struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor); u32 val; + /* + * REGS_TRITSR(id) has the following layout: + * + * 31 ... 7 6 5 4 3 2 1 0 + * V TEMP + * + * Where V bit signifies if the measurement is ready and is + * within sensor range. TEMP is an 8 bit value representing + * temperature in C. + */ + if (regmap_read_poll_timeout(qdata->regmap, + REGS_TRITSR(qsensor->id), + val, + val & TRITSR_V, + USEC_PER_MSEC, + 10 * USEC_PER_MSEC)) + return -ENODATA; - val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr); *temp = (val & 0xff) * 1000; return 0; @@ -156,84 +110,82 @@ static const struct thermal_zone_of_device_ops tmu_tz_ops = { .get_temp = tmu_get_temp, }; -static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) +static int qoriq_tmu_register_tmu_zone(struct device *dev, + struct qoriq_tmu_data *qdata) { - struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev); - int id, sites = 0; + int id; + + if (qdata->ver == TMU_VER1) { + regmap_write(qdata->regmap, REGS_TMR, + TMR_MSITE_ALL | TMR_ME | TMR_ALPF); + } else { + regmap_write(qdata->regmap, REGS_V2_TMSR, TMR_MSITE_ALL); + regmap_write(qdata->regmap, REGS_TMR, TMR_ME | TMR_ALPF_V2); + } for (id = 0; id < SITES_MAX; id++) { - qdata->sensor[id] = devm_kzalloc(&pdev->dev, - sizeof(struct qoriq_sensor), GFP_KERNEL); - if (!qdata->sensor[id]) - return -ENOMEM; - - qdata->sensor[id]->id = id; - qdata->sensor[id]->qdata = qdata; - qdata->sensor[id]->tzd = devm_thermal_zone_of_sensor_register( - &pdev->dev, id, qdata->sensor[id], &tmu_tz_ops); - if (IS_ERR(qdata->sensor[id]->tzd)) { - if (PTR_ERR(qdata->sensor[id]->tzd) == -ENODEV) + struct thermal_zone_device *tzd; + struct qoriq_sensor *sensor = &qdata->sensor[id]; + int ret; + + sensor->id = id; + + tzd = devm_thermal_zone_of_sensor_register(dev, id, + sensor, + &tmu_tz_ops); + ret = PTR_ERR_OR_ZERO(tzd); + if (ret) { + if (ret == -ENODEV) continue; - else - return PTR_ERR(qdata->sensor[id]->tzd); + + regmap_write(qdata->regmap, REGS_TMR, TMR_DISABLE); + return ret; } - if (qdata->ver == TMU_VER1) - sites |= 0x1 << (15 - id); - else - sites |= 0x1 << id; - } + if (devm_thermal_add_hwmon_sysfs(tzd)) + dev_warn(dev, + "Failed to add hwmon sysfs attributes\n"); - /* Enable monitoring */ - if (sites != 0) { - if (qdata->ver == TMU_VER1) { - tmu_write(qdata, sites | TMR_ME | TMR_ALPF, - &qdata->regs->tmr); - } else { - tmu_write(qdata, sites, &qdata->regs_v2->tmsr); - tmu_write(qdata, TMR_ME | TMR_ALPF_V2, - &qdata->regs_v2->tmr); - } } return 0; } -static int qoriq_tmu_calibration(struct platform_device *pdev) +static int qoriq_tmu_calibration(struct device *dev, + struct qoriq_tmu_data *data) { int i, val, len; u32 range[4]; const u32 *calibration; - struct device_node *np = pdev->dev.of_node; - struct qoriq_tmu_data *data = platform_get_drvdata(pdev); + struct device_node *np = dev->of_node; len = of_property_count_u32_elems(np, "fsl,tmu-range"); if (len < 0 || len > 4) { - dev_err(&pdev->dev, "invalid range data.\n"); + dev_err(dev, "invalid range data.\n"); return len; } val = of_property_read_u32_array(np, "fsl,tmu-range", range, len); if (val != 0) { - dev_err(&pdev->dev, "failed to read range data.\n"); + dev_err(dev, "failed to read range data.\n"); return val; } /* Init temperature range registers */ for (i = 0; i < len; i++) - tmu_write(data, range[i], &data->regs->ttrcr[i]); + regmap_write(data->regmap, REGS_TTRnCR(i), range[i]); calibration = of_get_property(np, "fsl,tmu-calibration", &len); if (calibration == NULL || len % 8) { - dev_err(&pdev->dev, "invalid calibration data.\n"); + dev_err(dev, "invalid calibration data.\n"); return -ENODEV; } for (i = 0; i < len; i += 8, calibration += 2) { val = of_read_number(calibration, 1); - tmu_write(data, val, &data->regs->ttcfgr); + regmap_write(data->regmap, REGS_TTCFGR, val); val = of_read_number(calibration + 1, 1); - tmu_write(data, val, &data->regs->tscfgr); + regmap_write(data->regmap, REGS_TSCFGR, val); } return 0; @@ -242,76 +194,117 @@ static int qoriq_tmu_calibration(struct platform_device *pdev) static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) { /* Disable interrupt, using polling instead */ - tmu_write(data, TIER_DISABLE, &data->regs->tier); + regmap_write(data->regmap, REGS_TIER, TIER_DISABLE); /* Set update_interval */ + if (data->ver == TMU_VER1) { - tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir); + regmap_write(data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT); } else { - tmu_write(data, TMTMIR_DEFAULT, &data->regs_v2->tmtmir); - tmu_write(data, TEUMR0_V2, &data->regs_v2->teumr0); + regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT); + regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2); } /* Disable monitoring */ - tmu_write(data, TMR_DISABLE, &data->regs->tmr); + regmap_write(data->regmap, REGS_TMR, TMR_DISABLE); } +static const struct regmap_range qoriq_yes_ranges[] = { + regmap_reg_range(REGS_TMR, REGS_TSCFGR), + regmap_reg_range(REGS_TTRnCR(0), REGS_TTRnCR(3)), + regmap_reg_range(REGS_V2_TEUMR(0), REGS_V2_TEUMR(2)), + regmap_reg_range(REGS_IPBRR(0), REGS_IPBRR(1)), + /* Read only registers below */ + regmap_reg_range(REGS_TRITSR(0), REGS_TRITSR(15)), +}; + +static const struct regmap_access_table qoriq_wr_table = { + .yes_ranges = qoriq_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges) - 1, +}; + +static const struct regmap_access_table qoriq_rd_table = { + .yes_ranges = qoriq_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges), +}; + static int qoriq_tmu_probe(struct platform_device *pdev) { int ret; u32 ver; struct qoriq_tmu_data *data; struct device_node *np = pdev->dev.of_node; - - data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data), + struct device *dev = &pdev->dev; + const bool little_endian = of_property_read_bool(np, "little-endian"); + const enum regmap_endian format_endian = + little_endian ? REGMAP_ENDIAN_LITTLE : REGMAP_ENDIAN_BIG; + const struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .rd_table = &qoriq_rd_table, + .wr_table = &qoriq_wr_table, + .val_format_endian = format_endian, + .max_register = SZ_4K, + }; + void __iomem *base; + + data = devm_kzalloc(dev, sizeof(struct qoriq_tmu_data), GFP_KERNEL); if (!data) return -ENOMEM; - platform_set_drvdata(pdev, data); - - data->little_endian = of_property_read_bool(np, "little-endian"); + base = devm_platform_ioremap_resource(pdev, 0); + ret = PTR_ERR_OR_ZERO(base); + if (ret) { + dev_err(dev, "Failed to get memory region\n"); + return ret; + } - data->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->regs)) { - dev_err(&pdev->dev, "Failed to get memory region\n"); - return PTR_ERR(data->regs); + data->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + ret = PTR_ERR_OR_ZERO(data->regmap); + if (ret) { + dev_err(dev, "Failed to init regmap (%d)\n", ret); + return ret; } - data->clk = devm_clk_get_optional(&pdev->dev, NULL); + data->clk = devm_clk_get_optional(dev, NULL); if (IS_ERR(data->clk)) return PTR_ERR(data->clk); ret = clk_prepare_enable(data->clk); if (ret) { - dev_err(&pdev->dev, "Failed to enable clock\n"); + dev_err(dev, "Failed to enable clock\n"); return ret; } /* version register offset at: 0xbf8 on both v1 and v2 */ - ver = tmu_read(data, &data->regs->ipbrr0); + ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver); + if (ret) { + dev_err(&pdev->dev, "Failed to read IP block version\n"); + return ret; + } data->ver = (ver >> 8) & 0xff; - if (data->ver == TMU_VER2) - data->regs_v2 = (void __iomem *)data->regs; qoriq_tmu_init_device(data); /* TMU initialization */ - ret = qoriq_tmu_calibration(pdev); /* TMU calibration */ + ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */ if (ret < 0) goto err; - ret = qoriq_tmu_register_tmu_zone(pdev); + ret = qoriq_tmu_register_tmu_zone(dev, data); if (ret < 0) { - dev_err(&pdev->dev, "Failed to register sensors\n"); + dev_err(dev, "Failed to register sensors\n"); ret = -ENODEV; goto err; } + platform_set_drvdata(pdev, data); + return 0; err: clk_disable_unprepare(data->clk); - platform_set_drvdata(pdev, NULL); return ret; } @@ -321,24 +314,21 @@ static int qoriq_tmu_remove(struct platform_device *pdev) struct qoriq_tmu_data *data = platform_get_drvdata(pdev); /* Disable monitoring */ - tmu_write(data, TMR_DISABLE, &data->regs->tmr); + regmap_write(data->regmap, REGS_TMR, TMR_DISABLE); clk_disable_unprepare(data->clk); - platform_set_drvdata(pdev, NULL); - return 0; } static int __maybe_unused qoriq_tmu_suspend(struct device *dev) { - u32 tmr; struct qoriq_tmu_data *data = dev_get_drvdata(dev); + int ret; - /* Disable monitoring */ - tmr = tmu_read(data, &data->regs->tmr); - tmr &= ~TMR_ME; - tmu_write(data, tmr, &data->regs->tmr); + ret = regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, 0); + if (ret) + return ret; clk_disable_unprepare(data->clk); @@ -347,7 +337,6 @@ static int __maybe_unused qoriq_tmu_suspend(struct device *dev) static int __maybe_unused qoriq_tmu_resume(struct device *dev) { - u32 tmr; int ret; struct qoriq_tmu_data *data = dev_get_drvdata(dev); @@ -356,11 +345,7 @@ static int __maybe_unused qoriq_tmu_resume(struct device *dev) return ret; /* Enable monitoring */ - tmr = tmu_read(data, &data->regs->tmr); - tmr |= TMR_ME; - tmu_write(data, tmr, &data->regs->tmr); - - return 0; + return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME); } static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 1460cf9d9f1c..72877bdc072d 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -182,9 +182,7 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) tsc->coef.a2); mcelsius = FIXPT_TO_MCELSIUS(val); - /* Make sure we are inside specifications */ - if ((mcelsius < MCELSIUS(-40)) || (mcelsius > MCELSIUS(125))) - return -EIO; + /* Guaranteed operating range is -40C to 125C. */ /* Round value to device granularity setting */ *temp = rcar_gen3_thermal_round(mcelsius); diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index d0873de718da..8f1aafa2044e 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -219,7 +219,7 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) * to get stable temperature. * see "Usage Notes" on datasheet */ - udelay(300); + usleep_range(300, 400); new = rcar_thermal_read(priv, THSSR) & CTEMP; if (new == old) { @@ -275,12 +275,7 @@ static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, tmp = MCELSIUS((priv->ctemp * 5) - 60); mutex_unlock(&priv->lock); - if ((tmp < MCELSIUS(-45)) || (tmp > MCELSIUS(125))) { - struct device *dev = rcar_priv_to_dev(priv); - - dev_err(dev, "it couldn't measure temperature correctly\n"); - return -EIO; - } + /* Guaranteed operating range is -45C to 125C. */ *temp = tmp; diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 343c2f5c5a25..7c1a8bccdcba 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -19,7 +19,7 @@ #include <linux/mfd/syscon.h> #include <linux/pinctrl/consumer.h> -/** +/* * If the temperature over a period of time High, * the resulting TSHUT gave CRU module,let it reset the entire chip, * or via GPIO give PMIC. @@ -29,7 +29,7 @@ enum tshut_mode { TSHUT_MODE_GPIO, }; -/** +/* * The system Temperature Sensors tshut(tshut) polarity * the bit 8 is tshut polarity. * 0: low active, 1: high active @@ -39,7 +39,7 @@ enum tshut_polarity { TSHUT_HIGH_ACTIVE, }; -/** +/* * The system has two Temperature Sensors. * sensor0 is for CPU, and sensor1 is for GPU. */ @@ -48,7 +48,7 @@ enum sensor_id { SENSOR_GPU, }; -/** +/* * The conversion table has the adc value and temperature. * ADC_DECREMENT: the adc value is of diminishing.(e.g. rk3288_code_table) * ADC_INCREMENT: the adc value is incremental.(e.g. rk3368_code_table) @@ -58,6 +58,8 @@ enum adc_sort_mode { ADC_INCREMENT, }; +#include "thermal_hwmon.h" + /** * The max sensors is two in rockchip SoCs. * Two sensors: CPU and GPU sensor. @@ -80,13 +82,14 @@ struct chip_tsadc_table { /** * struct rockchip_tsadc_chip - hold the private data of tsadc chip - * @chn_id[SOC_MAX_SENSORS]: the sensor id of chip correspond to the channel + * @chn_id: array of sensor ids of chip corresponding to the channel * @chn_num: the channel number of tsadc chip * @tshut_temp: the hardware-controlled shutdown temperature value * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) * @initialize: SoC special initialize tsadc controller method * @irq_ack: clear the interrupt + * @control: enable/disable method for the tsadc controller * @get_temp: get the temperature * @set_alarm_temp: set the high temperature interrupt * @set_tshut_temp: set the hardware-controlled shutdown temperature @@ -139,7 +142,7 @@ struct rockchip_thermal_sensor { * @chip: pointer to the platform/configuration data * @pdev: platform device of thermal * @reset: the reset controller of tsadc - * @sensors[SOC_MAX_SENSORS]: the thermal sensor + * @sensors: array of thermal sensors * @clk: the controller clock is divided by the exteral 24MHz * @pclk: the advanced peripherals bus clock * @grf: the general register file will be used to do static set by software @@ -590,6 +593,9 @@ static int rk_tsadcv2_code_to_temp(const struct chip_tsadc_table *table, /** * rk_tsadcv2_initialize - initialize TASDC Controller. + * @grf: the general register file will be used to do static set by software + * @regs: the base address of tsadc controller + * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) * * (1) Set TSADC_V2_AUTO_PERIOD: * Configure the interleave between every two accessing of @@ -624,6 +630,9 @@ static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs, /** * rk_tsadcv3_initialize - initialize TASDC Controller. + * @grf: the general register file will be used to do static set by software + * @regs: the base address of tsadc controller + * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) * * (1) The tsadc control power sequence. * @@ -723,6 +732,8 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable) /** * rk_tsadcv3_control - the tsadc controller is enabled or disabled. + * @regs: the base address of tsadc controller + * @enable: boolean flag to enable the controller * * NOTE: TSADC controller works at auto mode, and some SoCs need set the * tsadc_q_sel bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output @@ -1206,6 +1217,7 @@ rockchip_thermal_register_sensor(struct platform_device *pdev, /** * Reset TSADC Controller, reset all tsadc registers. + * @reset: the reset controller of tsadc */ static void rockchip_thermal_reset_controller(struct reset_control *reset) { @@ -1321,8 +1333,15 @@ static int rockchip_thermal_probe(struct platform_device *pdev) thermal->chip->control(thermal->regs, true); - for (i = 0; i < thermal->chip->chn_num; i++) + for (i = 0; i < thermal->chip->chn_num; i++) { rockchip_thermal_toggle_sensor(&thermal->sensors[i], true); + thermal->sensors[i].tzd->tzp->no_hwmon = false; + error = thermal_add_hwmon_sysfs(thermal->sensors[i].tzd); + if (error) + dev_warn(&pdev->dev, + "failed to register sensor %d with hwmon: %d\n", + i, error); + } platform_set_drvdata(pdev, thermal); @@ -1344,6 +1363,7 @@ static int rockchip_thermal_remove(struct platform_device *pdev) for (i = 0; i < thermal->chip->chn_num; i++) { struct rockchip_thermal_sensor *sensor = &thermal->sensors[i]; + thermal_remove_hwmon_sysfs(sensor->tzd); rockchip_thermal_toggle_sensor(sensor, false); } diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig index fe0d2ba51392..f4eff5a41a84 100644 --- a/drivers/thermal/samsung/Kconfig +++ b/drivers/thermal/samsung/Kconfig @@ -5,7 +5,7 @@ config EXYNOS_THERMAL depends on HAS_IOMEM help If you say yes here you get support for the TMU (Thermal Management - Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises + Unit) driver for Samsung Exynos series of SoCs. This driver initialises the TMU, reports temperature and handles cooling action if defined. This driver uses the Exynos core thermal APIs and TMU configuration data from the supported SoCs. diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index fb2c55123a99..fd4a17812f33 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit) + * exynos_tmu.c - Samsung Exynos TMU (Thermal Management Unit) * * Copyright (C) 2014 Samsung Electronics * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> @@ -138,7 +138,7 @@ enum soc_type { /** * struct exynos_tmu_data : A structure to hold the private data of the TMU - driver + * driver * @id: identifier of the one instance of the TMU controller. * @base: base address of the single instance of the TMU controller. * @base_second: base address of the common registers of the TMU controller. @@ -162,8 +162,11 @@ enum soc_type { * 0 < reference_voltage <= 31 * @regulator: pointer to the TMU regulator structure. * @reg_conf: pointer to structure to register with core thermal. + * @tzd: pointer to thermal_zone_device structure * @ntrip: number of supported trip points. * @enabled: current status of TMU device + * @tmu_set_trip_temp: SoC specific method to set trip (rising threshold) + * @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold) * @tmu_initialize: SoC specific TMU initialization method * @tmu_control: SoC specific TMU control method * @tmu_read: SoC specific TMU temperature read method @@ -1183,7 +1186,7 @@ static struct platform_driver exynos_tmu_driver = { module_platform_driver(exynos_tmu_driver); -MODULE_DESCRIPTION("EXYNOS TMU Driver"); +MODULE_DESCRIPTION("Exynos TMU Driver"); MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:exynos-tmu"); diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index cf9ddc52f30e..1cc5e6c5709e 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -30,7 +30,7 @@ #define DTS_DR_OFFSET 0x1C #define DTS_SR_OFFSET 0x20 #define DTS_ITENR_OFFSET 0x24 -#define DTS_CIFR_OFFSET 0x28 +#define DTS_ICIFR_OFFSET 0x28 /* DTS_CFGR1 register mask definitions */ #define HSREF_CLK_DIV_MASK GENMASK(30, 24) @@ -51,10 +51,16 @@ /* DTS_DR register mask definitions */ #define TS1_MFREQ_MASK GENMASK(15, 0) +/* DTS_ITENR register mask definitions */ +#define ITENR_MASK (GENMASK(2, 0) | GENMASK(6, 4)) + +/* DTS_ICIFR register mask definitions */ +#define ICIFR_MASK (GENMASK(2, 0) | GENMASK(6, 4)) + /* Less significant bit position definitions */ #define TS1_T0_POS 16 -#define TS1_SMP_TIME_POS 16 #define TS1_HITTHD_POS 16 +#define TS1_LITTHD_POS 0 #define HSREF_CLK_DIV_POS 24 /* DTS_CFGR1 bit definitions */ @@ -76,58 +82,59 @@ #define ONE_MHZ 1000000 #define POLL_TIMEOUT 5000 #define STARTUP_TIME 40 -#define TS1_T0_VAL0 30 -#define TS1_T0_VAL1 130 +#define TS1_T0_VAL0 30000 /* 30 celsius */ +#define TS1_T0_VAL1 130000 /* 130 celsius */ #define NO_HW_TRIG 0 - -/* The Thermal Framework expects millidegrees */ -#define mcelsius(temp) ((temp) * 1000) - -/* The Sensor expects oC degrees */ -#define celsius(temp) ((temp) / 1000) +#define SAMPLING_TIME 15 struct stm_thermal_sensor { struct device *dev; struct thermal_zone_device *th_dev; enum thermal_device_mode mode; struct clk *clk; - int high_temp; - int low_temp; - int temp_critical; - int temp_passive; unsigned int low_temp_enabled; - int num_trips; + unsigned int high_temp_enabled; int irq; - unsigned int irq_enabled; void __iomem *base; int t0, fmt0, ramp_coeff; }; -static irqreturn_t stm_thermal_alarm_irq(int irq, void *sdata) +static int stm_enable_irq(struct stm_thermal_sensor *sensor) { - struct stm_thermal_sensor *sensor = sdata; + u32 value; - disable_irq_nosync(irq); - sensor->irq_enabled = false; + dev_dbg(sensor->dev, "low:%d high:%d\n", sensor->low_temp_enabled, + sensor->high_temp_enabled); - return IRQ_WAKE_THREAD; + /* Disable IT generation for low and high thresholds */ + value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); + value &= ~(LOW_THRESHOLD | HIGH_THRESHOLD); + + if (sensor->low_temp_enabled) + value |= HIGH_THRESHOLD; + + if (sensor->high_temp_enabled) + value |= LOW_THRESHOLD; + + /* Enable interrupts */ + writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET); + + return 0; } -static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata) +static irqreturn_t stm_thermal_irq_handler(int irq, void *sdata) { - u32 value; struct stm_thermal_sensor *sensor = sdata; - /* read IT reason in SR and clear flags */ - value = readl_relaxed(sensor->base + DTS_SR_OFFSET); + dev_dbg(sensor->dev, "sr:%d\n", + readl_relaxed(sensor->base + DTS_SR_OFFSET)); - if ((value & LOW_THRESHOLD) == LOW_THRESHOLD) - writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); + thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); - if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD) - writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); + stm_enable_irq(sensor); - thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); + /* Acknoledge all DTS irqs */ + writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET); return IRQ_HANDLED; } @@ -160,6 +167,8 @@ static int stm_sensor_power_on(struct stm_thermal_sensor *sensor) writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); + sensor->mode = THERMAL_DEVICE_ENABLED; + return 0; } @@ -167,6 +176,8 @@ static int stm_sensor_power_off(struct stm_thermal_sensor *sensor) { u32 value; + sensor->mode = THERMAL_DEVICE_DISABLED; + /* Stop measuring */ value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); value &= ~TS1_START; @@ -263,60 +274,17 @@ static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor, int temp, u32 *th) { int freqM; - u32 sampling_time; - - /* Retrieve the number of periods to sample */ - sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) & - TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS; /* Figure out the CLK_PTAT frequency for a given temperature */ - freqM = ((temp - sensor->t0) * sensor->ramp_coeff) - + sensor->fmt0; - - dev_dbg(sensor->dev, "%s: freqM for threshold = %d Hz", - __func__, freqM); + freqM = ((temp - sensor->t0) * sensor->ramp_coeff) / 1000 + + sensor->fmt0; /* Figure out the threshold sample number */ - *th = clk_get_rate(sensor->clk); + *th = clk_get_rate(sensor->clk) * SAMPLING_TIME / freqM; if (!*th) return -EINVAL; - *th = *th / freqM; - - *th *= sampling_time; - - return 0; -} - -static int stm_thermal_set_threshold(struct stm_thermal_sensor *sensor) -{ - u32 value, th; - int ret; - - value = readl_relaxed(sensor->base + DTS_ITR1_OFFSET); - - /* Erase threshold content */ - value &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK); - - /* Retrieve the sample threshold number th for a given temperature */ - ret = stm_thermal_calculate_threshold(sensor, sensor->high_temp, &th); - if (ret) - return ret; - - value |= th & TS1_LITTHD_MASK; - - if (sensor->low_temp_enabled) { - /* Retrieve the sample threshold */ - ret = stm_thermal_calculate_threshold(sensor, sensor->low_temp, - &th); - if (ret) - return ret; - - value |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS)); - } - - /* Write value on the Low interrupt threshold */ - writel_relaxed(value, sensor->base + DTS_ITR1_OFFSET); + dev_dbg(sensor->dev, "freqM=%d Hz, threshold=0x%x", freqM, *th); return 0; } @@ -326,77 +294,57 @@ static int stm_disable_irq(struct stm_thermal_sensor *sensor) { u32 value; - /* Disable IT generation for low and high thresholds */ + /* Disable IT generation */ value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); - writel_relaxed(value & ~(LOW_THRESHOLD | HIGH_THRESHOLD), - sensor->base + DTS_ITENR_OFFSET); - - dev_dbg(sensor->dev, "%s: IT disabled on sensor side", __func__); - - return 0; -} - -/* Enable temperature interrupt */ -static int stm_enable_irq(struct stm_thermal_sensor *sensor) -{ - u32 value; - - /* - * Code below enables High temperature threshold using a low threshold - * sampling value - */ - - /* Make sure LOW_THRESHOLD IT is clear before enabling */ - writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); - - /* Enable IT generation for low threshold */ - value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); - value |= LOW_THRESHOLD; - - /* Enable the low temperature threshold if needed */ - if (sensor->low_temp_enabled) { - /* Make sure HIGH_THRESHOLD IT is clear before enabling */ - writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); - - /* Enable IT generation for high threshold */ - value |= HIGH_THRESHOLD; - } - - /* Enable thresholds */ + value &= ~ITENR_MASK; writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET); - dev_dbg(sensor->dev, "%s: IT enabled on sensor side", __func__); - return 0; } -static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor) +static int stm_thermal_set_trips(void *data, int low, int high) { + struct stm_thermal_sensor *sensor = data; + u32 itr1, th; int ret; - sensor->mode = THERMAL_DEVICE_DISABLED; + dev_dbg(sensor->dev, "set trips %d <--> %d\n", low, high); - ret = stm_sensor_power_off(sensor); - if (ret) - return ret; + /* Erase threshold content */ + itr1 = readl_relaxed(sensor->base + DTS_ITR1_OFFSET); + itr1 &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK); - ret = stm_disable_irq(sensor); - if (ret) - return ret; + /* + * Disable low-temp if "low" is too small. As per thermal framework + * API, we use -INT_MAX rather than INT_MIN. + */ - ret = stm_thermal_set_threshold(sensor); - if (ret) - return ret; + if (low > -INT_MAX) { + sensor->low_temp_enabled = 1; + /* add 0.5 of hysteresis due to measurement error */ + ret = stm_thermal_calculate_threshold(sensor, low - 500, &th); + if (ret) + return ret; - ret = stm_enable_irq(sensor); - if (ret) - return ret; + itr1 |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS)); + } else { + sensor->low_temp_enabled = 0; + } - ret = stm_sensor_power_on(sensor); - if (ret) - return ret; + /* Disable high-temp if "high" is too big. */ + if (high < INT_MAX) { + sensor->high_temp_enabled = 1; + ret = stm_thermal_calculate_threshold(sensor, high, &th); + if (ret) + return ret; - sensor->mode = THERMAL_DEVICE_ENABLED; + itr1 |= (TS1_LITTHD_MASK & (th << TS1_LITTHD_POS)); + } else { + sensor->high_temp_enabled = 0; + } + + /* Write new threshod values*/ + writel_relaxed(itr1, sensor->base + DTS_ITR1_OFFSET); return 0; } @@ -405,76 +353,26 @@ static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor) static int stm_thermal_get_temp(void *data, int *temp) { struct stm_thermal_sensor *sensor = data; - u32 sampling_time; + u32 periods; int freqM, ret; if (sensor->mode != THERMAL_DEVICE_ENABLED) return -EAGAIN; - /* Retrieve the number of samples */ - ret = readl_poll_timeout(sensor->base + DTS_DR_OFFSET, freqM, - (freqM & TS1_MFREQ_MASK), STARTUP_TIME, - POLL_TIMEOUT); - + /* Retrieve the number of periods sampled */ + ret = readl_relaxed_poll_timeout(sensor->base + DTS_DR_OFFSET, periods, + (periods & TS1_MFREQ_MASK), + STARTUP_TIME, POLL_TIMEOUT); if (ret) return ret; - if (!freqM) - return -ENODATA; - - /* Retrieve the number of periods sampled */ - sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) & - TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS; - - /* Figure out the number of samples per period */ - freqM /= sampling_time; - /* Figure out the CLK_PTAT frequency */ - freqM = clk_get_rate(sensor->clk) / freqM; + freqM = (clk_get_rate(sensor->clk) * SAMPLING_TIME) / periods; if (!freqM) return -EINVAL; - dev_dbg(sensor->dev, "%s: freqM=%d\n", __func__, freqM); - /* Figure out the temperature in mili celsius */ - *temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) / - sensor->ramp_coeff)); - - dev_dbg(sensor->dev, "%s: temperature = %d millicelsius", - __func__, *temp); - - /* Update thresholds */ - if (sensor->num_trips > 1) { - /* Update alarm threshold value to next higher trip point */ - if (sensor->high_temp == sensor->temp_passive && - celsius(*temp) >= sensor->temp_passive) { - sensor->high_temp = sensor->temp_critical; - sensor->low_temp = sensor->temp_passive; - sensor->low_temp_enabled = true; - ret = stm_thermal_update_threshold(sensor); - if (ret) - return ret; - } - - if (sensor->high_temp == sensor->temp_critical && - celsius(*temp) < sensor->temp_passive) { - sensor->high_temp = sensor->temp_passive; - sensor->low_temp_enabled = false; - ret = stm_thermal_update_threshold(sensor); - if (ret) - return ret; - } - - /* - * Re-enable alarm IRQ if temperature below critical - * temperature - */ - if (!sensor->irq_enabled && - (celsius(*temp) < sensor->temp_critical)) { - sensor->irq_enabled = true; - enable_irq(sensor->irq); - } - } + *temp = (freqM - sensor->fmt0) * 1000 / sensor->ramp_coeff + sensor->t0; return 0; } @@ -493,8 +391,8 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor) } ret = devm_request_threaded_irq(dev, sensor->irq, - stm_thermal_alarm_irq, - stm_thermal_alarm_irq_thread, + NULL, + stm_thermal_irq_handler, IRQF_ONESHOT, dev->driver->name, sensor); if (ret) { @@ -503,8 +401,6 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor) return ret; } - sensor->irq_enabled = true; - dev_dbg(dev, "%s: thermal IRQ registered", __func__); return 0; @@ -514,6 +410,8 @@ static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor) { int ret; + stm_disable_irq(sensor); + ret = stm_sensor_power_off(sensor); if (ret) return ret; @@ -526,7 +424,6 @@ static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor) static int stm_thermal_prepare(struct stm_thermal_sensor *sensor) { int ret; - struct device *dev = sensor->dev; ret = clk_prepare_enable(sensor->clk); if (ret) @@ -540,26 +437,8 @@ static int stm_thermal_prepare(struct stm_thermal_sensor *sensor) if (ret) goto thermal_unprepare; - /* Set threshold(s) for IRQ */ - ret = stm_thermal_set_threshold(sensor); - if (ret) - goto thermal_unprepare; - - ret = stm_enable_irq(sensor); - if (ret) - goto thermal_unprepare; - - ret = stm_sensor_power_on(sensor); - if (ret) { - dev_err(dev, "%s: failed to power on sensor\n", __func__); - goto irq_disable; - } - return 0; -irq_disable: - stm_disable_irq(sensor); - thermal_unprepare: clk_disable_unprepare(sensor->clk); @@ -576,8 +455,6 @@ static int stm_thermal_suspend(struct device *dev) if (ret) return ret; - sensor->mode = THERMAL_DEVICE_DISABLED; - return 0; } @@ -590,7 +467,12 @@ static int stm_thermal_resume(struct device *dev) if (ret) return ret; - sensor->mode = THERMAL_DEVICE_ENABLED; + ret = stm_sensor_power_on(sensor); + if (ret) + return ret; + + thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); + stm_enable_irq(sensor); return 0; } @@ -600,6 +482,7 @@ SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume); static const struct thermal_zone_of_device_ops stm_tz_ops = { .get_temp = stm_thermal_get_temp, + .set_trips = stm_thermal_set_trips, }; static const struct of_device_id stm_thermal_of_match[] = { @@ -612,9 +495,8 @@ static int stm_thermal_probe(struct platform_device *pdev) { struct stm_thermal_sensor *sensor; struct resource *res; - const struct thermal_trip *trip; void __iomem *base; - int ret, i; + int ret; if (!pdev->dev.of_node) { dev_err(&pdev->dev, "%s: device tree node not found\n", @@ -645,10 +527,23 @@ static int stm_thermal_probe(struct platform_device *pdev) return PTR_ERR(sensor->clk); } - /* Register IRQ into GIC */ - ret = stm_register_irq(sensor); - if (ret) + stm_disable_irq(sensor); + + /* Clear irq flags */ + writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET); + + /* Configure and enable HW sensor */ + ret = stm_thermal_prepare(sensor); + if (ret) { + dev_err(&pdev->dev, "Error preprare sensor: %d\n", ret); + return ret; + } + + ret = stm_sensor_power_on(sensor); + if (ret) { + dev_err(&pdev->dev, "Error power on sensor: %d\n", ret); return ret; + } sensor->th_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, sensor, @@ -661,53 +556,12 @@ static int stm_thermal_probe(struct platform_device *pdev) return ret; } - if (!sensor->th_dev->ops->get_crit_temp) { - /* Critical point must be provided */ - ret = -EINVAL; - goto err_tz; - } - - ret = sensor->th_dev->ops->get_crit_temp(sensor->th_dev, - &sensor->temp_critical); - if (ret) { - dev_err(&pdev->dev, - "Not able to read critical_temp: %d\n", ret); + /* Register IRQ into GIC */ + ret = stm_register_irq(sensor); + if (ret) goto err_tz; - } - - sensor->temp_critical = celsius(sensor->temp_critical); - - /* Set thresholds for IRQ */ - sensor->high_temp = sensor->temp_critical; - - trip = of_thermal_get_trip_points(sensor->th_dev); - sensor->num_trips = of_thermal_get_ntrips(sensor->th_dev); - - /* Find out passive temperature if it exists */ - for (i = (sensor->num_trips - 1); i >= 0; i--) { - if (trip[i].type == THERMAL_TRIP_PASSIVE) { - sensor->temp_passive = celsius(trip[i].temperature); - /* Update high temperature threshold */ - sensor->high_temp = sensor->temp_passive; - } - } - /* - * Ensure low_temp_enabled flag is disabled. - * By disabling low_temp_enabled, low threshold IT will not be - * configured neither enabled because it is not needed as high - * threshold is set on the lowest temperature trip point after - * probe. - */ - sensor->low_temp_enabled = false; - - /* Configure and enable HW sensor */ - ret = stm_thermal_prepare(sensor); - if (ret) { - dev_err(&pdev->dev, - "Not able to enable sensor: %d\n", ret); - goto err_tz; - } + stm_enable_irq(sensor); /* * Thermal_zone doesn't enable hwmon as default, @@ -718,8 +572,6 @@ static int stm_thermal_probe(struct platform_device *pdev) if (ret) goto err_tz; - sensor->mode = THERMAL_DEVICE_ENABLED; - dev_info(&pdev->dev, "%s: Driver initialized successfully\n", __func__); diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index 6e051cbd824f..2ae7198d3067 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -174,8 +174,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) /** * step_wise_throttle - throttles devices associated with the given zone - * @tz - thermal_zone_device - * @trip - trip point index + * @tz: thermal_zone_device + * @trip: trip point index * * Throttling Logic: This uses the trend of the thermal zone to throttle. * If the thermal zone is 'heating up' this throttles all the cooling diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c new file mode 100644 index 000000000000..74d73be16496 --- /dev/null +++ b/drivers/thermal/sun8i_thermal.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Thermal sensor driver for Allwinner SOC + * Copyright (C) 2019 Yangtao Li + * + * Based on the work of Icenowy Zheng <icenowy@aosc.io> + * Based on the work of Ondrej Jirman <megous@megous.com> + * Based on the work of Josef Gajdusek <atx@atx.name> + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/thermal.h> + +#include "thermal_hwmon.h" + +#define MAX_SENSOR_NUM 4 + +#define FT_TEMP_MASK GENMASK(11, 0) +#define TEMP_CALIB_MASK GENMASK(11, 0) +#define CALIBRATE_DEFAULT 0x800 + +#define SUN8I_THS_CTRL0 0x00 +#define SUN8I_THS_CTRL2 0x40 +#define SUN8I_THS_IC 0x44 +#define SUN8I_THS_IS 0x48 +#define SUN8I_THS_MFC 0x70 +#define SUN8I_THS_TEMP_CALIB 0x74 +#define SUN8I_THS_TEMP_DATA 0x80 + +#define SUN50I_THS_CTRL0 0x00 +#define SUN50I_H6_THS_ENABLE 0x04 +#define SUN50I_H6_THS_PC 0x08 +#define SUN50I_H6_THS_DIC 0x10 +#define SUN50I_H6_THS_DIS 0x20 +#define SUN50I_H6_THS_MFC 0x30 +#define SUN50I_H6_THS_TEMP_CALIB 0xa0 +#define SUN50I_H6_THS_TEMP_DATA 0xc0 + +#define SUN8I_THS_CTRL0_T_ACQ0(x) (GENMASK(15, 0) & (x)) +#define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16) +#define SUN8I_THS_DATA_IRQ_STS(x) BIT(x + 8) + +#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16) +#define SUN50I_THS_FILTER_EN BIT(2) +#define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x)) +#define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12) +#define SUN50I_H6_THS_DATA_IRQ_STS(x) BIT(x) + +/* millidegree celsius */ + +struct tsensor { + struct ths_device *tmdev; + struct thermal_zone_device *tzd; + int id; +}; + +struct ths_thermal_chip { + bool has_mod_clk; + bool has_bus_clk_reset; + int sensor_num; + int offset; + int scale; + int ft_deviation; + int temp_data_base; + int (*calibrate)(struct ths_device *tmdev, + u16 *caldata, int callen); + int (*init)(struct ths_device *tmdev); + int (*irq_ack)(struct ths_device *tmdev); + int (*calc_temp)(struct ths_device *tmdev, + int id, int reg); +}; + +struct ths_device { + const struct ths_thermal_chip *chip; + struct device *dev; + struct regmap *regmap; + struct reset_control *reset; + struct clk *bus_clk; + struct clk *mod_clk; + struct tsensor sensor[MAX_SENSOR_NUM]; +}; + +/* Temp Unit: millidegree Celsius */ +static int sun8i_ths_calc_temp(struct ths_device *tmdev, + int id, int reg) +{ + return tmdev->chip->offset - (reg * tmdev->chip->scale / 10); +} + +static int sun50i_h5_calc_temp(struct ths_device *tmdev, + int id, int reg) +{ + if (reg >= 0x500) + return -1191 * reg / 10 + 223000; + else if (!id) + return -1452 * reg / 10 + 259000; + else + return -1590 * reg / 10 + 276000; +} + +static int sun8i_ths_get_temp(void *data, int *temp) +{ + struct tsensor *s = data; + struct ths_device *tmdev = s->tmdev; + int val = 0; + + regmap_read(tmdev->regmap, tmdev->chip->temp_data_base + + 0x4 * s->id, &val); + + /* ths have no data yet */ + if (!val) + return -EAGAIN; + + *temp = tmdev->chip->calc_temp(tmdev, s->id, val); + /* + * According to the original sdk, there are some platforms(rarely) + * that add a fixed offset value after calculating the temperature + * value. We can't simply put it on the formula for calculating the + * temperature above, because the formula for calculating the + * temperature above is also used when the sensor is calibrated. If + * do this, the correct calibration formula is hard to know. + */ + *temp += tmdev->chip->ft_deviation; + + return 0; +} + +static const struct thermal_zone_of_device_ops ths_ops = { + .get_temp = sun8i_ths_get_temp, +}; + +static const struct regmap_config config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, + .max_register = 0xfc, +}; + +static int sun8i_h3_irq_ack(struct ths_device *tmdev) +{ + int i, state, ret = 0; + + regmap_read(tmdev->regmap, SUN8I_THS_IS, &state); + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + if (state & SUN8I_THS_DATA_IRQ_STS(i)) { + regmap_write(tmdev->regmap, SUN8I_THS_IS, + SUN8I_THS_DATA_IRQ_STS(i)); + ret |= BIT(i); + } + } + + return ret; +} + +static int sun50i_h6_irq_ack(struct ths_device *tmdev) +{ + int i, state, ret = 0; + + regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state); + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) { + regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS, + SUN50I_H6_THS_DATA_IRQ_STS(i)); + ret |= BIT(i); + } + } + + return ret; +} + +static irqreturn_t sun8i_irq_thread(int irq, void *data) +{ + struct ths_device *tmdev = data; + int i, state; + + state = tmdev->chip->irq_ack(tmdev); + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + if (state & BIT(i)) + thermal_zone_device_update(tmdev->sensor[i].tzd, + THERMAL_EVENT_UNSPECIFIED); + } + + return IRQ_HANDLED; +} + +static int sun8i_h3_ths_calibrate(struct ths_device *tmdev, + u16 *caldata, int callen) +{ + int i; + + if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num) + return -EINVAL; + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + int offset = (i % 2) << 4; + + regmap_update_bits(tmdev->regmap, + SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)), + 0xfff << offset, + caldata[i] << offset); + } + + return 0; +} + +static int sun50i_h6_ths_calibrate(struct ths_device *tmdev, + u16 *caldata, int callen) +{ + struct device *dev = tmdev->dev; + int i, ft_temp; + + if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num) + return -EINVAL; + + /* + * efuse layout: + * + * 0 11 16 32 + * +-------+-------+-------+ + * |temp| |sensor0|sensor1| + * +-------+-------+-------+ + * + * The calibration data on the H6 is the ambient temperature and + * sensor values that are filled during the factory test stage. + * + * The unit of stored FT temperature is 0.1 degreee celusis. + * + * We need to calculate a delta between measured and caluclated + * register values and this will become a calibration offset. + */ + ft_temp = (caldata[0] & FT_TEMP_MASK) * 100; + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + int sensor_reg = caldata[i + 1]; + int cdata, offset; + int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg); + + /* + * Calibration data is CALIBRATE_DEFAULT - (calculated + * temperature from sensor reading at factory temperature + * minus actual factory temperature) * 14.88 (scale from + * temperature to register values) + */ + cdata = CALIBRATE_DEFAULT - + ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale); + if (cdata & ~TEMP_CALIB_MASK) { + /* + * Calibration value more than 12-bit, but calibration + * register is 12-bit. In this case, ths hardware can + * still work without calibration, although the data + * won't be so accurate. + */ + dev_warn(dev, "sensor%d is not calibrated.\n", i); + continue; + } + + offset = (i % 2) * 16; + regmap_update_bits(tmdev->regmap, + SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4), + 0xfff << offset, + cdata << offset); + } + + return 0; +} + +static int sun8i_ths_calibrate(struct ths_device *tmdev) +{ + struct nvmem_cell *calcell; + struct device *dev = tmdev->dev; + u16 *caldata; + size_t callen; + int ret = 0; + + calcell = devm_nvmem_cell_get(dev, "calibration"); + if (IS_ERR(calcell)) { + if (PTR_ERR(calcell) == -EPROBE_DEFER) + return -EPROBE_DEFER; + /* + * Even if the external calibration data stored in sid is + * not accessible, the THS hardware can still work, although + * the data won't be so accurate. + * + * The default value of calibration register is 0x800 for + * every sensor, and the calibration value is usually 0x7xx + * or 0x8xx, so they won't be away from the default value + * for a lot. + * + * So here we do not return error if the calibartion data is + * not available, except the probe needs deferring. + */ + goto out; + } + + caldata = nvmem_cell_read(calcell, &callen); + if (IS_ERR(caldata)) { + ret = PTR_ERR(caldata); + goto out; + } + + tmdev->chip->calibrate(tmdev, caldata, callen); + + kfree(caldata); +out: + return ret; +} + +static int sun8i_ths_resource_init(struct ths_device *tmdev) +{ + struct device *dev = tmdev->dev; + struct platform_device *pdev = to_platform_device(dev); + void __iomem *base; + int ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + tmdev->regmap = devm_regmap_init_mmio(dev, base, &config); + if (IS_ERR(tmdev->regmap)) + return PTR_ERR(tmdev->regmap); + + if (tmdev->chip->has_bus_clk_reset) { + tmdev->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(tmdev->reset)) + return PTR_ERR(tmdev->reset); + + tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(tmdev->bus_clk)) + return PTR_ERR(tmdev->bus_clk); + } + + if (tmdev->chip->has_mod_clk) { + tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod"); + if (IS_ERR(tmdev->mod_clk)) + return PTR_ERR(tmdev->mod_clk); + } + + ret = reset_control_deassert(tmdev->reset); + if (ret) + return ret; + + ret = clk_prepare_enable(tmdev->bus_clk); + if (ret) + goto assert_reset; + + ret = clk_set_rate(tmdev->mod_clk, 24000000); + if (ret) + goto bus_disable; + + ret = clk_prepare_enable(tmdev->mod_clk); + if (ret) + goto bus_disable; + + ret = sun8i_ths_calibrate(tmdev); + if (ret) + goto mod_disable; + + return 0; + +mod_disable: + clk_disable_unprepare(tmdev->mod_clk); +bus_disable: + clk_disable_unprepare(tmdev->bus_clk); +assert_reset: + reset_control_assert(tmdev->reset); + + return ret; +} + +static int sun8i_h3_thermal_init(struct ths_device *tmdev) +{ + int val; + + /* average over 4 samples */ + regmap_write(tmdev->regmap, SUN8I_THS_MFC, + SUN50I_THS_FILTER_EN | + SUN50I_THS_FILTER_TYPE(1)); + /* + * clkin = 24MHz + * filter_samples = 4 + * period = 0.25s + * + * x = period * clkin / 4096 / filter_samples - 1 + * = 365 + */ + val = GENMASK(7 + tmdev->chip->sensor_num, 8); + regmap_write(tmdev->regmap, SUN8I_THS_IC, + SUN50I_H6_THS_PC_TEMP_PERIOD(365) | val); + /* + * T_acq = 20us + * clkin = 24MHz + * + * x = T_acq * clkin - 1 + * = 479 + */ + regmap_write(tmdev->regmap, SUN8I_THS_CTRL0, + SUN8I_THS_CTRL0_T_ACQ0(479)); + val = GENMASK(tmdev->chip->sensor_num - 1, 0); + regmap_write(tmdev->regmap, SUN8I_THS_CTRL2, + SUN8I_THS_CTRL2_T_ACQ1(479) | val); + + return 0; +} + +/* + * Without this undocummented value, the returned temperatures would + * be higher than real ones by about 20C. + */ +#define SUN50I_H6_CTRL0_UNK 0x0000002f + +static int sun50i_h6_thermal_init(struct ths_device *tmdev) +{ + int val; + + /* + * T_acq = 20us + * clkin = 24MHz + * + * x = T_acq * clkin - 1 + * = 479 + */ + regmap_write(tmdev->regmap, SUN50I_THS_CTRL0, + SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479)); + /* average over 4 samples */ + regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC, + SUN50I_THS_FILTER_EN | + SUN50I_THS_FILTER_TYPE(1)); + /* + * clkin = 24MHz + * filter_samples = 4 + * period = 0.25s + * + * x = period * clkin / 4096 / filter_samples - 1 + * = 365 + */ + regmap_write(tmdev->regmap, SUN50I_H6_THS_PC, + SUN50I_H6_THS_PC_TEMP_PERIOD(365)); + /* enable sensor */ + val = GENMASK(tmdev->chip->sensor_num - 1, 0); + regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val); + /* thermal data interrupt enable */ + val = GENMASK(tmdev->chip->sensor_num - 1, 0); + regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val); + + return 0; +} + +static int sun8i_ths_register(struct ths_device *tmdev) +{ + int i; + + for (i = 0; i < tmdev->chip->sensor_num; i++) { + tmdev->sensor[i].tmdev = tmdev; + tmdev->sensor[i].id = i; + tmdev->sensor[i].tzd = + devm_thermal_zone_of_sensor_register(tmdev->dev, + i, + &tmdev->sensor[i], + &ths_ops); + if (IS_ERR(tmdev->sensor[i].tzd)) + return PTR_ERR(tmdev->sensor[i].tzd); + + if (devm_thermal_add_hwmon_sysfs(tmdev->sensor[i].tzd)) + dev_warn(tmdev->dev, + "Failed to add hwmon sysfs attributes\n"); + } + + return 0; +} + +static int sun8i_ths_probe(struct platform_device *pdev) +{ + struct ths_device *tmdev; + struct device *dev = &pdev->dev; + int ret, irq; + + tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL); + if (!tmdev) + return -ENOMEM; + + tmdev->dev = dev; + tmdev->chip = of_device_get_match_data(&pdev->dev); + if (!tmdev->chip) + return -EINVAL; + + platform_set_drvdata(pdev, tmdev); + + ret = sun8i_ths_resource_init(tmdev); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = tmdev->chip->init(tmdev); + if (ret) + return ret; + + ret = sun8i_ths_register(tmdev); + if (ret) + return ret; + + /* + * Avoid entering the interrupt handler, the thermal device is not + * registered yet, we deffer the registration of the interrupt to + * the end. + */ + ret = devm_request_threaded_irq(dev, irq, NULL, + sun8i_irq_thread, + IRQF_ONESHOT, "ths", tmdev); + if (ret) + return ret; + + return 0; +} + +static int sun8i_ths_remove(struct platform_device *pdev) +{ + struct ths_device *tmdev = platform_get_drvdata(pdev); + + clk_disable_unprepare(tmdev->mod_clk); + clk_disable_unprepare(tmdev->bus_clk); + reset_control_assert(tmdev->reset); + + return 0; +} + +static const struct ths_thermal_chip sun8i_a83t_ths = { + .sensor_num = 3, + .scale = 705, + .offset = 191668, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct ths_thermal_chip sun8i_h3_ths = { + .sensor_num = 1, + .scale = 1211, + .offset = 217000, + .has_mod_clk = true, + .has_bus_clk_reset = true, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct ths_thermal_chip sun8i_r40_ths = { + .sensor_num = 2, + .offset = 251086, + .scale = 1130, + .has_mod_clk = true, + .has_bus_clk_reset = true, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct ths_thermal_chip sun50i_a64_ths = { + .sensor_num = 3, + .offset = 260890, + .scale = 1170, + .has_mod_clk = true, + .has_bus_clk_reset = true, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct ths_thermal_chip sun50i_h5_ths = { + .sensor_num = 2, + .has_mod_clk = true, + .has_bus_clk_reset = true, + .temp_data_base = SUN8I_THS_TEMP_DATA, + .calibrate = sun8i_h3_ths_calibrate, + .init = sun8i_h3_thermal_init, + .irq_ack = sun8i_h3_irq_ack, + .calc_temp = sun50i_h5_calc_temp, +}; + +static const struct ths_thermal_chip sun50i_h6_ths = { + .sensor_num = 2, + .has_bus_clk_reset = true, + .ft_deviation = 7000, + .offset = 187744, + .scale = 672, + .temp_data_base = SUN50I_H6_THS_TEMP_DATA, + .calibrate = sun50i_h6_ths_calibrate, + .init = sun50i_h6_thermal_init, + .irq_ack = sun50i_h6_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + +static const struct of_device_id of_ths_match[] = { + { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths }, + { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths }, + { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths }, + { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths }, + { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths }, + { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, of_ths_match); + +static struct platform_driver ths_driver = { + .probe = sun8i_ths_probe, + .remove = sun8i_ths_remove, + .driver = { + .name = "sun8i-thermal", + .of_match_table = of_ths_match, + }, +}; +module_platform_driver(ths_driver); + +MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 5acaad3a594f..66e0639da4bf 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -360,7 +360,7 @@ static struct soctherm_oc_irq_chip_data soc_irq_cdata; /** * ccroc_writel() - writes a value to a CCROC register * @ts: pointer to a struct tegra_soctherm - * @v: the value to write + * @value: the value to write * @reg: the register offset * * Writes @v to @reg. No return value. @@ -435,6 +435,7 @@ static int tegra_thermctl_get_temp(void *data, int *out_temp) /** * enforce_temp_range() - check and enforce temperature range [min, max] + * @dev: struct device * of the SOC_THERM instance * @trip_temp: the trip temperature to check * * Checks and enforces the permitted temperature range that SOC_THERM @@ -747,6 +748,8 @@ static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp) /** * tegra_soctherm_set_hwtrips() - set HW trip point from DT data * @dev: struct device * of the SOC_THERM instance + * @sg: pointer to the sensor group to set the thermtrip temperature for + * @tz: struct thermal_zone_device * * * Configure the SOC_THERM HW trip points, setting "THERMTRIP" * "THROTTLE" trip points , using "thermtrips", "critical" or "hot" @@ -931,6 +934,7 @@ static irqreturn_t soctherm_thermal_isr_thread(int irq, void *dev_id) /** * soctherm_oc_intr_enable() - Enables the soctherm over-current interrupt + * @ts: pointer to a struct tegra_soctherm * @alarm: The soctherm throttle id * @enable: Flag indicating enable the soctherm over-current * interrupt or disable it @@ -1156,7 +1160,7 @@ static void soctherm_oc_irq_enable(struct irq_data *data) /** * soctherm_oc_irq_disable() - Disables overcurrent interrupt requests - * @irq_data: The interrupt request information + * @data: The interrupt request information * * Clears the interrupt request enable bit of the overcurrent * interrupt request chip data. @@ -1206,6 +1210,7 @@ static int soctherm_oc_irq_map(struct irq_domain *h, unsigned int virq, /** * soctherm_irq_domain_xlate_twocell() - xlate for soctherm interrupts * @d: Interrupt request domain + * @ctrlr: Controller device tree node * @intspec: Array of u32s from DTs "interrupt" property * @intsize: Number of values inside the intspec array * @out_hwirq: HW IRQ value associated with this interrupt @@ -1681,6 +1686,7 @@ err: /** * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations * and register them as cooling devices. + * @pdev: Pointer to platform_device struct */ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) { @@ -1751,6 +1757,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) /** * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config + * @ts: pointer to a struct tegra_soctherm * @level: describing the level LOW/MED/HIGH of throttling * * It's necessary to set up the CPU-local CCROC NV_THERM instance with @@ -1798,6 +1805,7 @@ static void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level) /** * throttlectl_cpu_level_select() - program CPU pulse skipper config + * @ts: pointer to a struct tegra_soctherm * @throt: the LIGHT/HEAVY of throttle event id * * Pulse skippers are used to throttle clock frequencies. This @@ -1841,6 +1849,7 @@ static void throttlectl_cpu_level_select(struct tegra_soctherm *ts, /** * throttlectl_cpu_mn() - program CPU pulse skipper configuration + * @ts: pointer to a struct tegra_soctherm * @throt: the LIGHT/HEAVY of throttle event id * * Pulse skippers are used to throttle clock frequencies. This @@ -1874,6 +1883,7 @@ static void throttlectl_cpu_mn(struct tegra_soctherm *ts, /** * throttlectl_gpu_level_select() - selects throttling level for GPU + * @ts: pointer to a struct tegra_soctherm * @throt: the LIGHT/HEAVY of throttle event id * * This function programs soctherm's interface to GK20a NV_THERM to select @@ -1918,6 +1928,7 @@ static int soctherm_oc_cfg_program(struct tegra_soctherm *ts, /** * soctherm_throttle_program() - programs pulse skippers' configuration + * @ts: pointer to a struct tegra_soctherm * @throt: the LIGHT/HEAVY of the throttle event id. * * Pulse skippers are used to throttle clock frequencies. diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index ae5743c9a894..73665c3ccfe0 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -76,13 +76,17 @@ static int gadc_thermal_read_linear_lookup_table(struct device *dev, struct gadc_thermal_info *gti) { struct device_node *np = dev->of_node; + enum iio_chan_type chan_type; int ntable; int ret; ntable = of_property_count_elems_of_size(np, "temperature-lookup-table", sizeof(u32)); if (ntable <= 0) { - dev_notice(dev, "no lookup table, assuming DAC channel returns milliCelcius\n"); + ret = iio_get_channel_type(gti->channel, &chan_type); + if (ret || chan_type != IIO_TEMP) + dev_notice(dev, + "no lookup table, assuming DAC channel returns milliCelcius\n"); return 0; } @@ -124,13 +128,6 @@ static int gadc_thermal_probe(struct platform_device *pdev) if (!gti) return -ENOMEM; - ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti); - if (ret < 0) - return ret; - - gti->dev = &pdev->dev; - platform_set_drvdata(pdev, gti); - gti->channel = devm_iio_channel_get(&pdev->dev, "sensor-channel"); if (IS_ERR(gti->channel)) { ret = PTR_ERR(gti->channel); @@ -139,6 +136,13 @@ static int gadc_thermal_probe(struct platform_device *pdev) return ret; } + ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti); + if (ret < 0) + return ret; + + gti->dev = &pdev->dev; + platform_set_drvdata(pdev, gti); + gti->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, gti, &gadc_thermal_ops); if (IS_ERR(gti->tz_dev)) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 207b0cda70da..a9bf00e91d64 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -92,14 +92,12 @@ thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, /* device tree support */ #ifdef CONFIG_THERMAL_OF int of_parse_thermal_zones(void); -void of_thermal_destroy_zones(void); int of_thermal_get_ntrips(struct thermal_zone_device *); bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *); #else static inline int of_parse_thermal_zones(void) { return 0; } -static inline void of_thermal_destroy_zones(void) { } static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz) { return 0; diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index dd5d8ee37928..c8d2620f2e42 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -248,3 +248,31 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) kfree(hwmon); } EXPORT_SYMBOL_GPL(thermal_remove_hwmon_sysfs); + +static void devm_thermal_hwmon_release(struct device *dev, void *res) +{ + thermal_remove_hwmon_sysfs(*(struct thermal_zone_device **)res); +} + +int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) +{ + struct thermal_zone_device **ptr; + int ret; + + ptr = devres_alloc(devm_thermal_hwmon_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = thermal_add_hwmon_sysfs(tz); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = tz; + devres_add(&tz->device, ptr); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs); diff --git a/drivers/thermal/thermal_hwmon.h b/drivers/thermal/thermal_hwmon.h index a160b9d62dd0..1a9d65f6a6a8 100644 --- a/drivers/thermal/thermal_hwmon.h +++ b/drivers/thermal/thermal_hwmon.h @@ -17,6 +17,7 @@ #ifdef CONFIG_THERMAL_HWMON int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz); +int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz); void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz); #else static inline int @@ -25,6 +26,12 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) return 0; } +static inline int +devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) +{ + return 0; +} + static inline void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { diff --git a/drivers/thermal/user_space.c b/drivers/thermal/user_space.c index 962873fd9242..293cffd9c8ad 100644 --- a/drivers/thermal/user_space.c +++ b/drivers/thermal/user_space.c @@ -17,8 +17,8 @@ /** * notify_user_space - Notifies user space about thermal events - * @tz - thermal_zone_device - * @trip - trip point index + * @tz: thermal_zone_device + * @trip: trip point index * * This function notifies the user space through UEvents. */ diff --git a/drivers/thermal/zx2967_thermal.c b/drivers/thermal/zx2967_thermal.c index 7c8a82c8e1e9..8e3a2d3c2f9a 100644 --- a/drivers/thermal/zx2967_thermal.c +++ b/drivers/thermal/zx2967_thermal.c @@ -45,6 +45,7 @@ * @clk_topcrm: topcrm clk structure * @clk_apb: apb clk structure * @regs: pointer to base address of the thermal sensor + * @dev: struct device pointer */ struct zx2967_thermal_priv { diff --git a/include/dt-bindings/thermal/thermal_exynos.h b/include/dt-bindings/thermal/thermal_exynos.h index 642e4e7f4084..52fcb51dda3c 100644 --- a/include/dt-bindings/thermal/thermal_exynos.h +++ b/include/dt-bindings/thermal/thermal_exynos.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * thermal_exynos.h - Samsung EXYNOS TMU device tree definitions + * thermal_exynos.h - Samsung Exynos TMU device tree definitions * * Copyright (C) 2014 Samsung Electronics * Lukasz Majewski <l.majewski@samsung.com> diff --git a/include/linux/clock_cooling.h b/include/linux/clock_cooling.h index b5cebf766e02..4b0a69863656 100644 --- a/include/linux/clock_cooling.h +++ b/include/linux/clock_cooling.h @@ -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/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h index b74732535e4b..65501d8f9778 100644 --- a/include/linux/cpu_cooling.h +++ b/include/linux/cpu_cooling.h @@ -19,7 +19,7 @@ struct cpufreq_policy; -#ifdef CONFIG_CPU_THERMAL +#ifdef CONFIG_CPU_FREQ_THERMAL /** * cpufreq_cooling_register - function to create cpufreq cooling device. * @policy: cpufreq policy. @@ -40,7 +40,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev); struct thermal_cooling_device * of_cpufreq_cooling_register(struct cpufreq_policy *policy); -#else /* !CONFIG_CPU_THERMAL */ +#else /* !CONFIG_CPU_FREQ_THERMAL */ static inline struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy) { @@ -58,6 +58,24 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) { return NULL; } -#endif /* CONFIG_CPU_THERMAL */ +#endif /* CONFIG_CPU_FREQ_THERMAL */ + +struct cpuidle_driver; + +#ifdef CONFIG_CPU_IDLE_THERMAL +int cpuidle_cooling_register(struct cpuidle_driver *drv); +int cpuidle_of_cooling_register(struct device_node *np, + struct cpuidle_driver *drv); +#else /* CONFIG_CPU_IDLE_THERMAL */ +static inline int cpuidle_cooling_register(struct cpuidle_driver *drv) +{ + return 0; +} +static inline int cpuidle_of_cooling_register(struct device_node *np, + struct cpuidle_driver *drv) +{ + return 0; +} +#endif /* CONFIG_CPU_IDLE_THERMAL */ #endif /* __CPU_COOLING_H__ */ |