summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml116
-rw-r--r--Documentation/devicetree/bindings/thermal/thermal-idle.yaml145
-rw-r--r--Documentation/devicetree/bindings/thermal/thermal-sensor.yaml72
-rw-r--r--Documentation/devicetree/bindings/thermal/thermal-zones.yaml341
-rw-r--r--Documentation/devicetree/bindings/thermal/ti,am654-thermal.yaml56
-rw-r--r--MAINTAINERS9
-rw-r--r--drivers/cpuidle/cpuidle-arm.c3
-rw-r--r--drivers/cpuidle/cpuidle-psci.c3
-rw-r--r--drivers/powercap/idle_inject.c16
-rw-r--r--drivers/thermal/Kconfig14
-rw-r--r--drivers/thermal/Makefile11
-rw-r--r--drivers/thermal/clock_cooling.c3
-rw-r--r--drivers/thermal/cpufreq_cooling.c10
-rw-r--r--drivers/thermal/cpuidle_cooling.c63
-rw-r--r--drivers/thermal/devfreq_cooling.c70
-rw-r--r--drivers/thermal/gov_fair_share.c (renamed from drivers/thermal/fair_share.c)0
-rw-r--r--drivers/thermal/gov_power_allocator.c (renamed from drivers/thermal/power_allocator.c)0
-rw-r--r--drivers/thermal/gov_step_wise.c (renamed from drivers/thermal/step_wise.c)0
-rw-r--r--drivers/thermal/gov_user_space.c (renamed from drivers/thermal/user_space.c)2
-rw-r--r--drivers/thermal/imx8mm_thermal.c2
-rw-r--r--drivers/thermal/imx_sc_thermal.c4
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c223
-rw-r--r--drivers/thermal/k3_bandgap.c264
-rw-r--r--drivers/thermal/qcom/Makefile4
-rw-r--r--drivers/thermal/qcom/tsens-common.c843
-rw-r--r--drivers/thermal/qcom/tsens.c838
-rw-r--r--drivers/thermal/qcom/tsens.h5
-rw-r--r--drivers/thermal/qoriq_thermal.c26
-rw-r--r--drivers/thermal/rcar_thermal.c9
-rw-r--r--drivers/thermal/rockchip_thermal.c4
-rw-r--r--drivers/thermal/st/st_thermal_memmap.c4
-rw-r--r--drivers/thermal/st/stm_thermal.c4
-rw-r--r--drivers/thermal/thermal_core.c12
-rw-r--r--drivers/thermal/thermal_core.h52
-rw-r--r--drivers/thermal/thermal_helpers.c16
-rw-r--r--drivers/thermal/thermal_hwmon.c6
-rw-r--r--drivers/thermal/thermal_of.c (renamed from drivers/thermal/of-thermal.c)10
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.c5
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-thermal-common.c6
-rw-r--r--include/linux/cpu_cooling.h12
-rw-r--r--include/linux/idle_inject.h4
-rw-r--r--include/linux/thermal.h84
42 files changed, 2303 insertions, 1068 deletions
diff --git a/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml b/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml
new file mode 100644
index 000000000000..5145883d932e
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml
@@ -0,0 +1,116 @@
+# SPDX-License-Identifier: (GPL-2.0)
+# Copyright 2020 Linaro Ltd.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/thermal-cooling-devices.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Thermal cooling device binding
+
+maintainers:
+ - Amit Kucheria <amitk@kernel.org>
+
+description: |
+ Thermal management is achieved in devicetree by describing the sensor hardware
+ and the software abstraction of cooling devices and thermal zones required to
+ take appropriate action to mitigate thermal overload.
+
+ The following node types are used to completely describe a thermal management
+ system in devicetree:
+ - thermal-sensor: device that measures temperature, has SoC-specific bindings
+ - cooling-device: device used to dissipate heat either passively or actively
+ - thermal-zones: a container of the following node types used to describe all
+ thermal data for the platform
+
+ This binding describes the cooling devices.
+
+ There are essentially two ways to provide control on power dissipation:
+ - Passive cooling: by means of regulating device performance. A typical
+ passive cooling mechanism is a CPU that has dynamic voltage and frequency
+ scaling (DVFS), and uses lower frequencies as cooling states.
+ - Active cooling: by means of activating devices in order to remove the
+ dissipated heat, e.g. regulating fan speeds.
+
+ Any cooling device has a range of cooling states (i.e. different levels of
+ heat dissipation). They also have a way to determine the state of cooling in
+ which the device is. For example, a fan's cooling states correspond to the
+ different fan speeds possible. Cooling states are referred to by single
+ unsigned integers, where larger numbers mean greater heat dissipation. The
+ precise set of cooling states associated with a device should be defined in
+ a particular device's binding.
+
+select: true
+
+properties:
+ "#cooling-cells":
+ description:
+ Must be 2, in order to specify minimum and maximum cooling state used in
+ the cooling-maps reference. The first cell is the minimum cooling state
+ and the second cell is the maximum cooling state requested.
+ const: 2
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/thermal/thermal.h>
+
+ // Example 1: Cpufreq cooling device on CPU0
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "qcom,kryo385";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ cpu-idle-states = <&LITTLE_CPU_SLEEP_0
+ &LITTLE_CPU_SLEEP_1
+ &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <607>;
+ dynamic-power-coefficient = <100>;
+ qcom,freq-domain = <&cpufreq_hw 0>;
+ #cooling-cells = <2>;
+ next-level-cache = <&L2_0>;
+ L2_0: l2-cache {
+ compatible = "cache";
+ next-level-cache = <&L3_0>;
+ L3_0: l3-cache {
+ compatible = "cache";
+ };
+ };
+ };
+
+ /* ... */
+
+ };
+
+ /* ... */
+
+ thermal-zones {
+ cpu0-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&tsens0 1>;
+
+ trips {
+ cpu0_alert0: trip-point0 {
+ temperature = <90000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu0_alert0>;
+ /* Corresponds to 1000MHz in OPP table */
+ cooling-device = <&CPU0 5 5>;
+ };
+ };
+ };
+
+ /* ... */
+ };
+...
diff --git a/Documentation/devicetree/bindings/thermal/thermal-idle.yaml b/Documentation/devicetree/bindings/thermal/thermal-idle.yaml
new file mode 100644
index 000000000000..7a922f540934
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/thermal-idle.yaml
@@ -0,0 +1,145 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright 2020 Linaro Ltd.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/thermal-idle.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Thermal idle cooling device binding
+
+maintainers:
+ - Daniel Lezcano <daniel.lezcano@linaro.org>
+
+description: |
+ The thermal idle cooling device allows the system to passively
+ mitigate the temperature on the device by injecting idle cycles,
+ forcing it to cool down.
+
+ This binding describes the thermal idle node.
+
+properties:
+ $nodename:
+ const: thermal-idle
+ description: |
+ A thermal-idle node describes the idle cooling device properties to
+ cool down efficiently the attached thermal zone.
+
+ '#cooling-cells':
+ const: 2
+ description: |
+ Must be 2, in order to specify minimum and maximum cooling state used in
+ the cooling-maps reference. The first cell is the minimum cooling state
+ and the second cell is the maximum cooling state requested.
+
+ duration-us:
+ description: |
+ The idle duration in microsecond the device should cool down.
+
+ exit-latency-us:
+ description: |
+ The exit latency constraint in microsecond for the injected
+ idle state for the device. It is the latency constraint to
+ apply when selecting an idle state from among all the present
+ ones.
+
+required:
+ - '#cooling-cells'
+
+examples:
+ - |
+ #include <dt-bindings/thermal/thermal.h>
+
+ // Example: Combining idle cooling device on big CPUs with cpufreq cooling device
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ /* ... */
+
+ cpu_b0: cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a72";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ capacity-dmips-mhz = <1024>;
+ dynamic-power-coefficient = <436>;
+ #cooling-cells = <2>; /* min followed by max */
+ cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP>;
+ thermal-idle {
+ #cooling-cells = <2>;
+ duration-us = <10000>;
+ exit-latency-us = <500>;
+ };
+ };
+
+ cpu_b1: cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a72";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ capacity-dmips-mhz = <1024>;
+ dynamic-power-coefficient = <436>;
+ #cooling-cells = <2>; /* min followed by max */
+ cpu-idle-states = <&CPU_SLEEP &CLUSTER_SLEEP>;
+ thermal-idle {
+ #cooling-cells = <2>;
+ duration-us = <10000>;
+ exit-latency-us = <500>;
+ };
+ };
+
+ /* ... */
+
+ };
+
+ /* ... */
+
+ thermal_zones {
+ cpu_thermal: cpu {
+ polling-delay-passive = <100>;
+ polling-delay = <1000>;
+
+ /* ... */
+
+ trips {
+ cpu_alert0: cpu_alert0 {
+ temperature = <65000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu_alert1: cpu_alert1 {
+ temperature = <70000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu_alert2: cpu_alert2 {
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu_crit: cpu_crit {
+ temperature = <95000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert1>;
+ cooling-device = <&{/cpus/cpu@100/thermal-idle} 0 15 >,
+ <&{/cpus/cpu@101/thermal-idle} 0 15>;
+ };
+
+ map1 {
+ trip = <&cpu_alert2>;
+ cooling-device =
+ <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/thermal/thermal-sensor.yaml b/Documentation/devicetree/bindings/thermal/thermal-sensor.yaml
new file mode 100644
index 000000000000..fcd25a0af38c
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/thermal-sensor.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0)
+# Copyright 2020 Linaro Ltd.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/thermal-sensor.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Thermal sensor binding
+
+maintainers:
+ - Amit Kucheria <amitk@kernel.org>
+
+description: |
+ Thermal management is achieved in devicetree by describing the sensor hardware
+ and the software abstraction of thermal zones required to take appropriate
+ action to mitigate thermal overloads.
+
+ The following node types are used to completely describe a thermal management
+ system in devicetree:
+ - thermal-sensor: device that measures temperature, has SoC-specific bindings
+ - cooling-device: device used to dissipate heat either passively or actively
+ - thermal-zones: a container of the following node types used to describe all
+ thermal data for the platform
+
+ This binding describes the thermal-sensor.
+
+ Thermal sensor devices provide temperature sensing capabilities on thermal
+ zones. Typical devices are I2C ADC converters and bandgaps. Thermal sensor
+ devices may control one or more internal sensors.
+
+properties:
+ "#thermal-sensor-cells":
+ description:
+ Used to uniquely identify a thermal sensor instance within an IC. Will be
+ 0 on sensor nodes with only a single sensor and at least 1 on nodes
+ containing several internal sensors.
+ enum: [0, 1]
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ // Example 1: SDM845 TSENS
+ soc: soc@0 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ /* ... */
+
+ tsens0: thermal-sensor@c263000 {
+ compatible = "qcom,sdm845-tsens", "qcom,tsens-v2";
+ reg = <0 0x0c263000 0 0x1ff>, /* TM */
+ <0 0x0c222000 0 0x1ff>; /* SROT */
+ #qcom,sensors = <13>;
+ interrupts = <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 508 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "uplow", "critical";
+ #thermal-sensor-cells = <1>;
+ };
+
+ tsens1: thermal-sensor@c265000 {
+ compatible = "qcom,sdm845-tsens", "qcom,tsens-v2";
+ reg = <0 0x0c265000 0 0x1ff>, /* TM */
+ <0 0x0c223000 0 0x1ff>; /* SROT */
+ #qcom,sensors = <8>;
+ interrupts = <GIC_SPI 507 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 509 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "uplow", "critical";
+ #thermal-sensor-cells = <1>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/thermal/thermal-zones.yaml b/Documentation/devicetree/bindings/thermal/thermal-zones.yaml
new file mode 100644
index 000000000000..b8515d3eeaa2
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/thermal-zones.yaml
@@ -0,0 +1,341 @@
+# SPDX-License-Identifier: (GPL-2.0)
+# Copyright 2020 Linaro Ltd.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/thermal-zones.yaml#
+$schema: http://devicetree.org/meta-schemas/base.yaml#
+
+title: Thermal zone binding
+
+maintainers:
+ - Amit Kucheria <amitk@kernel.org>
+
+description: |
+ Thermal management is achieved in devicetree by describing the sensor hardware
+ and the software abstraction of cooling devices and thermal zones required to
+ take appropriate action to mitigate thermal overloads.
+
+ The following node types are used to completely describe a thermal management
+ system in devicetree:
+ - thermal-sensor: device that measures temperature, has SoC-specific bindings
+ - cooling-device: device used to dissipate heat either passively or actively
+ - thermal-zones: a container of the following node types used to describe all
+ thermal data for the platform
+
+ This binding describes the thermal-zones.
+
+ The polling-delay properties of a thermal-zone are bound to the maximum dT/dt
+ (temperature derivative over time) in two situations for a thermal zone:
+ 1. when passive cooling is activated (polling-delay-passive)
+ 2. when the zone just needs to be monitored (polling-delay) or when
+ active cooling is activated.
+
+ The maximum dT/dt is highly bound to hardware power consumption and
+ dissipation capability. The delays should be chosen to account for said
+ max dT/dt, such that a device does not cross several trip boundaries
+ unexpectedly between polls. Choosing the right polling delays shall avoid
+ having the device in temperature ranges that may damage the silicon structures
+ and reduce silicon lifetime.
+
+properties:
+ $nodename:
+ const: thermal-zones
+ description:
+ A /thermal-zones node is required in order to use the thermal framework to
+ manage input from the various thermal zones in the system in order to
+ mitigate thermal overload conditions. It does not represent a real device
+ in the system, but acts as a container to link a thermal sensor device,
+ platform-data regarding temperature thresholds and the mitigation actions
+ to take when the temperature crosses those thresholds.
+
+patternProperties:
+ "^[a-zA-Z][a-zA-Z0-9\\-]{1,12}-thermal$":
+ type: object
+ description:
+ Each thermal zone node contains information about how frequently it
+ must be checked, the sensor responsible for reporting temperature for
+ this zone, one sub-node containing the various trip points for this
+ zone and one sub-node containing all the zone cooling-maps.
+
+ properties:
+ polling-delay:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ The maximum number of milliseconds to wait between polls when
+ checking this thermal zone. Setting this to 0 disables the polling
+ timers setup by the thermal framework and assumes that the thermal
+ sensors in this zone support interrupts.
+
+ polling-delay-passive:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ The maximum number of milliseconds to wait between polls when
+ checking this thermal zone while doing passive cooling. Setting
+ this to 0 disables the polling timers setup by the thermal
+ framework and assumes that the thermal sensors in this zone
+ support interrupts.
+
+ thermal-sensors:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ maxItems: 1
+ description:
+ The thermal sensor phandle and sensor specifier used to monitor this
+ thermal zone.
+
+ coefficients:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description:
+ An array of integers containing the coefficients of a linear equation
+ that binds all the sensors listed in this thermal zone.
+
+ The linear equation used is as follows,
+ z = c0 * x0 + c1 * x1 + ... + c(n-1) * x(n-1) + cn
+ where c0, c1, .., cn are the coefficients.
+
+ Coefficients default to 1 in case this property is not specified. The
+ coefficients are ordered and are matched with sensors by means of the
+ sensor ID. Additional coefficients are interpreted as constant offset.
+
+ sustainable-power:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ An estimate of the sustainable power (in mW) that this thermal zone
+ can dissipate at the desired control temperature. For reference, the
+ sustainable power of a 4-inch phone is typically 2000mW, while on a
+ 10-inch tablet is around 4500mW.
+
+ trips:
+ type: object
+ description:
+ This node describes a set of points in the temperature domain at
+ which the thermal framework needs to take action. The actions to
+ be taken are defined in another node called cooling-maps.
+
+ patternProperties:
+ "^[a-zA-Z][a-zA-Z0-9\\-_]{0,63}$":
+ type: object
+
+ properties:
+ temperature:
+ $ref: /schemas/types.yaml#/definitions/int32
+ minimum: -273000
+ maximum: 200000
+ description:
+ An integer expressing the trip temperature in millicelsius.
+
+ hysteresis:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ An unsigned integer expressing the hysteresis delta with
+ respect to the trip temperature property above, also in
+ millicelsius. Any cooling action initiated by the framework is
+ maintained until the temperature falls below
+ (trip temperature - hysteresis). This potentially prevents a
+ situation where the trip gets constantly triggered soon after
+ cooling action is removed.
+
+ type:
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - active # enable active cooling e.g. fans
+ - passive # enable passive cooling e.g. throttling cpu
+ - hot # send notification to driver
+ - critical # send notification to driver, trigger shutdown
+ description: |
+ There are four valid trip types: active, passive, hot,
+ critical.
+
+ The critical trip type is used to set the maximum
+ temperature threshold above which the HW becomes
+ unstable and underlying firmware might even trigger a
+ reboot. Hitting the critical threshold triggers a system
+ shutdown.
+
+ The hot trip type can be used to send a notification to
+ the thermal driver (if a .notify callback is registered).
+ The action to be taken is left to the driver.
+
+ The passive trip type can be used to slow down HW e.g. run
+ the CPU, GPU, bus at a lower frequency.
+
+ The active trip type can be used to control other HW to
+ help in cooling e.g. fans can be sped up or slowed down
+
+ required:
+ - temperature
+ - hysteresis
+ - type
+ additionalProperties: false
+
+ additionalProperties: false
+
+ cooling-maps:
+ type: object
+ description:
+ This node describes the action to be taken when a thermal zone
+ crosses one of the temperature thresholds described in the trips
+ node. The action takes the form of a mapping relation between a
+ trip and the target cooling device state.
+
+ patternProperties:
+ "^map[-a-zA-Z0-9]*$":
+ type: object
+
+ properties:
+ trip:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ A phandle of a trip point node within this thermal zone.
+
+ cooling-device:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ A list of cooling device phandles along with the minimum
+ and maximum cooling state specifiers for each cooling
+ device. Using the THERMAL_NO_LIMIT (-1UL) constant in the
+ cooling-device phandle limit specifier lets the framework
+ use the minimum and maximum cooling state for that cooling
+ device automatically.
+
+ contribution:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 100
+ description:
+ The percentage contribution of the cooling devices at the
+ specific trip temperature referenced in this map
+ to this thermal zone
+
+ required:
+ - trip
+ - cooling-device
+ additionalProperties: false
+
+ required:
+ - polling-delay
+ - polling-delay-passive
+ - thermal-sensors
+ - trips
+ additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/thermal/thermal.h>
+
+ // Example 1: SDM845 TSENS
+ soc: soc@0 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ /* ... */
+
+ tsens0: thermal-sensor@c263000 {
+ compatible = "qcom,sdm845-tsens", "qcom,tsens-v2";
+ reg = <0 0x0c263000 0 0x1ff>, /* TM */
+ <0 0x0c222000 0 0x1ff>; /* SROT */
+ #qcom,sensors = <13>;
+ interrupts = <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 508 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "uplow", "critical";
+ #thermal-sensor-cells = <1>;
+ };
+
+ tsens1: thermal-sensor@c265000 {
+ compatible = "qcom,sdm845-tsens", "qcom,tsens-v2";
+ reg = <0 0x0c265000 0 0x1ff>, /* TM */
+ <0 0x0c223000 0 0x1ff>; /* SROT */
+ #qcom,sensors = <8>;
+ interrupts = <GIC_SPI 507 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 509 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "uplow", "critical";
+ #thermal-sensor-cells = <1>;
+ };
+ };
+
+ /* ... */
+
+ thermal-zones {
+ cpu0-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&tsens0 1>;
+
+ trips {
+ cpu0_alert0: trip-point0 {
+ temperature = <90000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu0_alert1: trip-point1 {
+ temperature = <95000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu0_crit: cpu_crit {
+ temperature = <110000>;
+ hysteresis = <1000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&cpu0_alert0>;
+ /* Corresponds to 1400MHz in OPP table */
+ cooling-device = <&CPU0 3 3>, <&CPU1 3 3>,
+ <&CPU2 3 3>, <&CPU3 3 3>;
+ };
+
+ map1 {
+ trip = <&cpu0_alert1>;
+ /* Corresponds to 1000MHz in OPP table */
+ cooling-device = <&CPU0 5 5>, <&CPU1 5 5>,
+ <&CPU2 5 5>, <&CPU3 5 5>;
+ };
+ };
+ };
+
+ /* ... */
+
+ cluster0-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&tsens0 5>;
+
+ trips {
+ cluster0_alert0: trip-point0 {
+ temperature = <90000>;
+ hysteresis = <2000>;
+ type = "hot";
+ };
+ cluster0_crit: cluster0_crit {
+ temperature = <110000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ /* ... */
+
+ gpu-top-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <1000>;
+
+ thermal-sensors = <&tsens0 11>;
+
+ trips {
+ gpu1_alert0: trip-point0 {
+ temperature = <90000>;
+ hysteresis = <2000>;
+ type = "hot";
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/thermal/ti,am654-thermal.yaml b/Documentation/devicetree/bindings/thermal/ti,am654-thermal.yaml
new file mode 100644
index 000000000000..25b9209c2e5d
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/ti,am654-thermal.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/ti,am654-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments AM654 VTM (DTS) binding
+
+maintainers:
+ - Keerthy <j-keerthy@ti.com>
+
+properties:
+ compatible:
+ const: ti,am654-vtm
+
+ reg:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ "#thermal-sensor-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - power-domains
+ - "#thermal-sensor-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/soc/ti,sci_pm_domain.h>
+ vtm: thermal@42050000 {
+ compatible = "ti,am654-vtm";
+ reg = <0x0 0x42050000 0x0 0x25c>;
+ power-domains = <&k3_pds 80 TI_SCI_PD_EXCLUSIVE>;
+ #thermal-sensor-cells = <1>;
+ };
+
+ mpu0_thermal: mpu0_thermal {
+ polling-delay-passive = <250>; /* milliseconds */
+ polling-delay = <500>; /* milliseconds */
+ thermal-sensors = <&vtm0 0>;
+
+ trips {
+ mpu0_crit: mpu0_crit {
+ temperature = <125000>; /* milliCelsius */
+ hysteresis = <2000>; /* milliCelsius */
+ type = "critical";
+ };
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index b5cbbc2e7982..56d7d27fc114 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14556,6 +14556,15 @@ F: Documentation/devicetree/bindings/i2c/renesas,iic.txt
F: drivers/i2c/busses/i2c-rcar.c
F: drivers/i2c/busses/i2c-sh_mobile.c
+RENESAS R-CAR THERMAL DRIVERS
+M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
+L: linux-renesas-soc@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt
+F: Documentation/devicetree/bindings/thermal/rcar-thermal.txt
+F: drivers/thermal/rcar_gen3_thermal.c
+F: drivers/thermal/rcar_thermal.c
+
RENESAS RIIC DRIVER
M: Chris Brandt <chris.brandt@renesas.com>
S: Supported
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index 9e5156d39627..8c758920d699 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -8,6 +8,7 @@
#define pr_fmt(fmt) "CPUidle arm: " fmt
+#include <linux/cpu_cooling.h>
#include <linux/cpuidle.h>
#include <linux/cpumask.h>
#include <linux/cpu_pm.h>
@@ -124,6 +125,8 @@ static int __init arm_idle_init_cpu(int cpu)
if (ret)
goto out_kfree_drv;
+ cpuidle_cooling_register(drv);
+
return 0;
out_kfree_drv:
diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c
index d0fb585073c6..3806f911b61c 100644
--- a/drivers/cpuidle/cpuidle-psci.c
+++ b/drivers/cpuidle/cpuidle-psci.c
@@ -9,6 +9,7 @@
#define pr_fmt(fmt) "CPUidle PSCI: " fmt
#include <linux/cpuhotplug.h>
+#include <linux/cpu_cooling.h>
#include <linux/cpuidle.h>
#include <linux/cpumask.h>
#include <linux/cpu_pm.h>
@@ -319,6 +320,8 @@ static int __init psci_idle_init_cpu(int cpu)
if (ret)
goto out_kfree_drv;
+ cpuidle_cooling_register(drv);
+
return 0;
out_kfree_drv:
diff --git a/drivers/powercap/idle_inject.c b/drivers/powercap/idle_inject.c
index e9bbd3c42eef..c90f0990968b 100644
--- a/drivers/powercap/idle_inject.c
+++ b/drivers/powercap/idle_inject.c
@@ -61,12 +61,14 @@ struct idle_inject_thread {
* @timer: idle injection period timer
* @idle_duration_us: duration of CPU idle time to inject
* @run_duration_us: duration of CPU run time to allow
+ * @latency_us: max allowed latency
* @cpumask: mask of CPUs affected by idle injection
*/
struct idle_inject_device {
struct hrtimer timer;
unsigned int idle_duration_us;
unsigned int run_duration_us;
+ unsigned int latency_us;
unsigned long cpumask[];
};
@@ -138,7 +140,8 @@ static void idle_inject_fn(unsigned int cpu)
*/
iit->should_run = 0;
- play_idle(READ_ONCE(ii_dev->idle_duration_us));
+ play_idle_precise(READ_ONCE(ii_dev->idle_duration_us) * NSEC_PER_USEC,
+ READ_ONCE(ii_dev->latency_us) * NSEC_PER_USEC);
}
/**
@@ -170,6 +173,16 @@ void idle_inject_get_duration(struct idle_inject_device *ii_dev,
}
/**
+ * idle_inject_set_latency - set the maximum latency allowed
+ * @latency_us: set the latency requirement for the idle state
+ */
+void idle_inject_set_latency(struct idle_inject_device *ii_dev,
+ unsigned int latency_us)
+{
+ WRITE_ONCE(ii_dev->latency_us, latency_us);
+}
+
+/**
* idle_inject_start - start idle injections
* @ii_dev: idle injection control device structure
*
@@ -297,6 +310,7 @@ struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
cpumask_copy(to_cpumask(ii_dev->cpumask), cpumask);
hrtimer_init(&ii_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ii_dev->timer.function = idle_inject_timer_fn;
+ ii_dev->latency_us = UINT_MAX;
for_each_cpu(cpu, to_cpumask(ii_dev->cpumask)) {
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 91af271e9bb0..3eb2348e5242 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -273,6 +273,16 @@ config IMX8MM_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the passive
trip is crossed.
+config K3_THERMAL
+ tristate "Texas Instruments K3 thermal support"
+ depends on ARCH_K3 || COMPILE_TEST
+ help
+ If you say yes here you get thermal support for the Texas Instruments
+ K3 SoC family. The current chip supported is:
+ - AM654
+
+ This includes temperature reading functionality.
+
config MAX77620_THERMAL
tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
depends on MFD_MAX77620
@@ -285,8 +295,8 @@ config MAX77620_THERMAL
config QORIQ_THERMAL
tristate "QorIQ Thermal Monitoring Unit"
- depends on THERMAL_OF
- depends on HAS_IOMEM
+ depends on THERMAL_OF && HAS_IOMEM
+ depends on PPC_E500MC || SOC_LS1021A || ARCH_LAYERSCAPE || (ARCH_MXC && ARM64) || COMPILE_TEST
select REGMAP_MMIO
help
Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 8c8ed7b79915..0c8b84a09b9a 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -9,14 +9,14 @@ thermal_sys-y += thermal_core.o thermal_sysfs.o \
# interface to/from other layers providing sensors
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
-thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o
+thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o
# governors
-thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
+thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += gov_fair_share.o
thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o
-thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o
-thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
-thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o
+thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += gov_step_wise.o
+thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += gov_user_space.o
+thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += gov_power_allocator.o
# cpufreq cooling
thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpufreq_cooling.o
@@ -28,6 +28,7 @@ thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o
# devfreq cooling
thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
+obj-$(CONFIG_K3_THERMAL) += k3_bandgap.o
# platform thermal drivers
obj-y += broadcom/
obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o
diff --git a/drivers/thermal/clock_cooling.c b/drivers/thermal/clock_cooling.c
index 7cb3ae4b44ee..56cb1f46a428 100644
--- a/drivers/thermal/clock_cooling.c
+++ b/drivers/thermal/clock_cooling.c
@@ -12,15 +12,16 @@
* Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
*/
#include <linux/clk.h>
+#include <linux/clock_cooling.h>
#include <linux/cpufreq.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/thermal.h>
-#include <linux/clock_cooling.h>
/**
* struct clock_cooling_device - data for cooling device with clock
diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c
index e297e135c031..9e124020519f 100644
--- a/drivers/thermal/cpufreq_cooling.c
+++ b/drivers/thermal/cpufreq_cooling.c
@@ -10,17 +10,17 @@
* Viresh Kumar <viresh.kumar@linaro.org>
*
*/
-#include <linux/module.h>
-#include <linux/thermal.h>
+#include <linux/cpu.h>
#include <linux/cpufreq.h>
+#include <linux/cpu_cooling.h>
+#include <linux/energy_model.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/idr.h>
#include <linux/pm_opp.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
-#include <linux/cpu.h>
-#include <linux/cpu_cooling.h>
-#include <linux/energy_model.h>
+#include <linux/thermal.h>
#include <trace/events/thermal.h>
diff --git a/drivers/thermal/cpuidle_cooling.c b/drivers/thermal/cpuidle_cooling.c
index 0bb843246f59..78e3e8238116 100644
--- a/drivers/thermal/cpuidle_cooling.c
+++ b/drivers/thermal/cpuidle_cooling.c
@@ -5,11 +5,14 @@
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
*
*/
+#define pr_fmt(fmt) "cpuidle cooling: " fmt
+
#include <linux/cpu_cooling.h>
#include <linux/cpuidle.h>
#include <linux/err.h>
#include <linux/idle_inject.h>
#include <linux/idr.h>
+#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/thermal.h>
@@ -154,22 +157,25 @@ static struct thermal_cooling_device_ops cpuidle_cooling_ops = {
};
/**
- * cpuidle_of_cooling_register - Idle cooling device initialization function
+ * __cpuidle_cooling_register: register the cooling device
* @drv: a cpuidle driver structure pointer
- * @np: a node pointer to a device tree cooling device node
+ * @np: a device node structure pointer used for the thermal binding
*
- * This function is in charge of creating a cooling device per cpuidle
- * driver and register it to thermal framework.
+ * This function is in charge of allocating the cpuidle cooling device
+ * structure, the idle injection, initialize them and register the
+ * cooling device to the thermal framework.
*
- * Return: zero on success, or negative value corresponding to the
- * error detected in the underlying subsystems.
+ * Return: zero on success, a negative value returned by one of the
+ * underlying subsystem in case of error
*/
-int cpuidle_of_cooling_register(struct device_node *np,
- struct cpuidle_driver *drv)
+static int __cpuidle_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;
+ unsigned int idle_duration_us = TICK_USEC;
+ unsigned int latency_us = UINT_MAX;
char dev_name[THERMAL_NAME_LENGTH];
int id, ret;
@@ -191,7 +197,11 @@ int cpuidle_of_cooling_register(struct device_node *np,
goto out_id;
}
- idle_inject_set_duration(ii_dev, TICK_USEC, TICK_USEC);
+ of_property_read_u32(np, "duration-us", &idle_duration_us);
+ of_property_read_u32(np, "exit-latency-us", &latency_us);
+
+ idle_inject_set_duration(ii_dev, TICK_USEC, idle_duration_us);
+ idle_inject_set_latency(ii_dev, latency_us);
idle_cdev->ii_dev = ii_dev;
@@ -204,6 +214,9 @@ int cpuidle_of_cooling_register(struct device_node *np,
goto out_unregister;
}
+ pr_debug("%s: Idle injection set with idle duration=%u, latency=%u\n",
+ dev_name, idle_duration_us, latency_us);
+
return 0;
out_unregister:
@@ -221,12 +234,38 @@ out:
* @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.
+ * driver and register it to the 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)
+void cpuidle_cooling_register(struct cpuidle_driver *drv)
{
- return cpuidle_of_cooling_register(NULL, drv);
+ struct device_node *cooling_node;
+ struct device_node *cpu_node;
+ int cpu, ret;
+
+ for_each_cpu(cpu, drv->cpumask) {
+
+ cpu_node = of_cpu_device_node_get(cpu);
+
+ cooling_node = of_get_child_by_name(cpu_node, "thermal-idle");
+
+ of_node_put(cpu_node);
+
+ if (!cooling_node) {
+ pr_debug("'thermal-idle' node not found for cpu%d\n", cpu);
+ continue;
+ }
+
+ ret = __cpuidle_cooling_register(cooling_node, drv);
+
+ of_node_put(cooling_node);
+
+ if (ret) {
+ pr_err("Failed to register the cpuidle cooling device" \
+ "for cpu%d: %d\n", cpu, ret);
+ break;
+ }
+ }
}
diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c
index a87d4fa031c8..f7f32e98331b 100644
--- a/drivers/thermal/devfreq_cooling.c
+++ b/drivers/thermal/devfreq_cooling.c
@@ -24,11 +24,13 @@
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/pm_opp.h>
+#include <linux/pm_qos.h>
#include <linux/thermal.h>
#include <trace/events/thermal.h>
-#define SCALE_ERROR_MITIGATION 100
+#define HZ_PER_KHZ 1000
+#define SCALE_ERROR_MITIGATION 100
static DEFINE_IDA(devfreq_ida);
@@ -54,6 +56,8 @@ static DEFINE_IDA(devfreq_ida);
* 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
+ * @req_max_freq: PM QoS request for limiting the maximum frequency
+ * of the devfreq device.
*/
struct devfreq_cooling_device {
int id;
@@ -66,49 +70,9 @@ struct devfreq_cooling_device {
struct devfreq_cooling_power *power_ops;
u32 res_util;
int capped_state;
+ struct dev_pm_qos_request req_max_freq;
};
-/**
- * partition_enable_opps() - disable all opps above a given state
- * @dfc: Pointer to devfreq we are operating on
- * @cdev_state: cooling device state we're setting
- *
- * Go through the OPPs of the device, enabling all OPPs until
- * @cdev_state and disabling those frequencies above it.
- */
-static int partition_enable_opps(struct devfreq_cooling_device *dfc,
- unsigned long cdev_state)
-{
- int i;
- struct device *dev = dfc->devfreq->dev.parent;
-
- for (i = 0; i < dfc->freq_table_size; i++) {
- struct dev_pm_opp *opp;
- int ret = 0;
- unsigned int freq = dfc->freq_table[i];
- bool want_enable = i >= cdev_state ? true : false;
-
- opp = dev_pm_opp_find_freq_exact(dev, freq, !want_enable);
-
- if (PTR_ERR(opp) == -ERANGE)
- continue;
- else if (IS_ERR(opp))
- return PTR_ERR(opp);
-
- dev_pm_opp_put(opp);
-
- if (want_enable)
- ret = dev_pm_opp_enable(dev, freq);
- else
- ret = dev_pm_opp_disable(dev, freq);
-
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
@@ -135,7 +99,7 @@ static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev,
struct devfreq_cooling_device *dfc = cdev->devdata;
struct devfreq *df = dfc->devfreq;
struct device *dev = df->dev.parent;
- int ret;
+ unsigned long freq;
if (state == dfc->cooling_state)
return 0;
@@ -145,9 +109,10 @@ static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev,
if (state >= dfc->freq_table_size)
return -EINVAL;
- ret = partition_enable_opps(dfc, state);
- if (ret)
- return ret;
+ freq = dfc->freq_table[state];
+
+ dev_pm_qos_update_request(&dfc->req_max_freq,
+ DIV_ROUND_UP(freq, HZ_PER_KHZ));
dfc->cooling_state = state;
@@ -530,9 +495,15 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
if (err)
goto free_dfc;
- err = ida_simple_get(&devfreq_ida, 0, 0, GFP_KERNEL);
+ err = dev_pm_qos_add_request(df->dev.parent, &dfc->req_max_freq,
+ DEV_PM_QOS_MAX_FREQUENCY,
+ PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
if (err < 0)
goto free_tables;
+
+ err = ida_simple_get(&devfreq_ida, 0, 0, GFP_KERNEL);
+ if (err < 0)
+ goto remove_qos_req;
dfc->id = err;
snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d", dfc->id);
@@ -553,6 +524,10 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
release_ida:
ida_simple_remove(&devfreq_ida, dfc->id);
+
+remove_qos_req:
+ dev_pm_qos_remove_request(&dfc->req_max_freq);
+
free_tables:
kfree(dfc->power_table);
kfree(dfc->freq_table);
@@ -601,6 +576,7 @@ void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
thermal_cooling_device_unregister(dfc->cdev);
ida_simple_remove(&devfreq_ida, dfc->id);
+ dev_pm_qos_remove_request(&dfc->req_max_freq);
kfree(dfc->power_table);
kfree(dfc->freq_table);
diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/gov_fair_share.c
index aaa07180ab48..aaa07180ab48 100644
--- a/drivers/thermal/fair_share.c
+++ b/drivers/thermal/gov_fair_share.c
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/gov_power_allocator.c
index 44636475b2a3..44636475b2a3 100644
--- a/drivers/thermal/power_allocator.c
+++ b/drivers/thermal/gov_power_allocator.c
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/gov_step_wise.c
index 2ae7198d3067..2ae7198d3067 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/gov_step_wise.c
diff --git a/drivers/thermal/user_space.c b/drivers/thermal/gov_user_space.c
index 293cffd9c8ad..82a7198bbe71 100644
--- a/drivers/thermal/user_space.c
+++ b/drivers/thermal/gov_user_space.c
@@ -10,8 +10,8 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-#include <linux/thermal.h>
#include <linux/slab.h>
+#include <linux/thermal.h>
#include "thermal_core.h"
diff --git a/drivers/thermal/imx8mm_thermal.c b/drivers/thermal/imx8mm_thermal.c
index 0d60f8d7894f..e6061e26d4ac 100644
--- a/drivers/thermal/imx8mm_thermal.c
+++ b/drivers/thermal/imx8mm_thermal.c
@@ -54,7 +54,7 @@ struct imx8mm_tmu {
void __iomem *base;
struct clk *clk;
const struct thermal_soc_data *socdata;
- struct tmu_sensor sensors[0];
+ struct tmu_sensor sensors[];
};
static int imx8mm_tmu_get_temp(void *data, int *temp)
diff --git a/drivers/thermal/imx_sc_thermal.c b/drivers/thermal/imx_sc_thermal.c
index 8938ea81a525..b01d28eca7ee 100644
--- a/drivers/thermal/imx_sc_thermal.c
+++ b/drivers/thermal/imx_sc_thermal.c
@@ -14,6 +14,7 @@
#include <linux/thermal.h>
#include "thermal_core.h"
+#include "thermal_hwmon.h"
#define IMX_SC_MISC_FUNC_GET_TEMP 13
@@ -115,6 +116,9 @@ static int imx_sc_thermal_probe(struct platform_device *pdev)
ret = PTR_ERR(sensor->tzd);
break;
}
+
+ if (devm_thermal_add_hwmon_sysfs(sensor->tzd))
+ dev_warn(&pdev->dev, "failed to add hwmon sysfs attributes\n");
}
of_node_put(sensor_np);
diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
index ceef89c956bd..0b3a62655843 100644
--- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
@@ -13,6 +13,7 @@
#include "acpi_thermal_rel.h"
#define INT3400_THERMAL_TABLE_CHANGED 0x83
+#define INT3400_ODVP_CHANGED 0x88
enum int3400_thermal_uuid {
INT3400_THERMAL_PASSIVE_1,
@@ -41,8 +42,11 @@ static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
"BE84BABF-C4D4-403D-B495-3128FD44dAC1",
};
+struct odvp_attr;
+
struct int3400_thermal_priv {
struct acpi_device *adev;
+ struct platform_device *pdev;
struct thermal_zone_device *thermal;
int mode;
int art_count;
@@ -52,6 +56,36 @@ struct int3400_thermal_priv {
u8 uuid_bitmap;
int rel_misc_dev_res;
int current_uuid_index;
+ char *data_vault;
+ int odvp_count;
+ int *odvp;
+ struct odvp_attr *odvp_attrs;
+};
+
+static int evaluate_odvp(struct int3400_thermal_priv *priv);
+
+struct odvp_attr {
+ int odvp;
+ struct int3400_thermal_priv *priv;
+ struct kobj_attribute attr;
+};
+
+static ssize_t data_vault_read(struct file *file, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+{
+ memcpy(buf, attr->private + off, count);
+ return count;
+}
+
+static BIN_ATTR_RO(data_vault, 0);
+
+static struct bin_attribute *data_attributes[] = {
+ &bin_attr_data_vault,
+ NULL,
+};
+
+static const struct attribute_group data_attribute_group = {
+ .bin_attrs = data_attributes,
};
static ssize_t available_uuids_show(struct device *dev,
@@ -62,6 +96,9 @@ static ssize_t available_uuids_show(struct device *dev,
int i;
int length = 0;
+ if (!priv->uuid_bitmap)
+ return sprintf(buf, "UNKNOWN\n");
+
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
if (priv->uuid_bitmap & (1 << i))
if (PAGE_SIZE - length > 0)
@@ -79,11 +116,11 @@ static ssize_t current_uuid_show(struct device *dev,
{
struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
- if (priv->uuid_bitmap & (1 << priv->current_uuid_index))
- return sprintf(buf, "%s\n",
- int3400_thermal_uuids[priv->current_uuid_index]);
- else
+ if (priv->current_uuid_index == -1)
return sprintf(buf, "INVALID\n");
+
+ return sprintf(buf, "%s\n",
+ int3400_thermal_uuids[priv->current_uuid_index]);
}
static ssize_t current_uuid_store(struct device *dev,
@@ -94,9 +131,16 @@ static ssize_t current_uuid_store(struct device *dev,
int i;
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {
- if ((priv->uuid_bitmap & (1 << i)) &&
- !(strncmp(buf, int3400_thermal_uuids[i],
- sizeof(int3400_thermal_uuids[i]) - 1))) {
+ if (!strncmp(buf, int3400_thermal_uuids[i],
+ sizeof(int3400_thermal_uuids[i]) - 1)) {
+ /*
+ * If we have a list of supported UUIDs, make sure
+ * this one is supported.
+ */
+ if (priv->uuid_bitmap &&
+ !(priv->uuid_bitmap & (1 << i)))
+ return -EINVAL;
+
priv->current_uuid_index = i;
return count;
}
@@ -191,9 +235,110 @@ static int int3400_thermal_run_osc(acpi_handle handle,
result = -EPERM;
kfree(context.ret.pointer);
+
return result;
}
+static ssize_t odvp_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ struct odvp_attr *odvp_attr;
+
+ odvp_attr = container_of(attr, struct odvp_attr, attr);
+
+ return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);
+}
+
+static void cleanup_odvp(struct int3400_thermal_priv *priv)
+{
+ int i;
+
+ if (priv->odvp_attrs) {
+ for (i = 0; i < priv->odvp_count; i++) {
+ sysfs_remove_file(&priv->pdev->dev.kobj,
+ &priv->odvp_attrs[i].attr.attr);
+ kfree(priv->odvp_attrs[i].attr.attr.name);
+ }
+ kfree(priv->odvp_attrs);
+ }
+ kfree(priv->odvp);
+ priv->odvp_count = 0;
+}
+
+static int evaluate_odvp(struct int3400_thermal_priv *priv)
+{
+ struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj = NULL;
+ acpi_status status;
+ int i, ret;
+
+ status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp);
+ if (ACPI_FAILURE(status)) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ obj = odvp.pointer;
+ if (obj->type != ACPI_TYPE_PACKAGE) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ if (priv->odvp == NULL) {
+ priv->odvp_count = obj->package.count;
+ priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int),
+ GFP_KERNEL);
+ if (!priv->odvp) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+ }
+
+ if (priv->odvp_attrs == NULL) {
+ priv->odvp_attrs = kcalloc(priv->odvp_count,
+ sizeof(struct odvp_attr),
+ GFP_KERNEL);
+ if (!priv->odvp_attrs) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+ for (i = 0; i < priv->odvp_count; i++) {
+ struct odvp_attr *odvp = &priv->odvp_attrs[i];
+
+ sysfs_attr_init(&odvp->attr.attr);
+ odvp->priv = priv;
+ odvp->odvp = i;
+ odvp->attr.attr.name = kasprintf(GFP_KERNEL,
+ "odvp%d", i);
+
+ if (!odvp->attr.attr.name) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+ odvp->attr.attr.mode = 0444;
+ odvp->attr.show = odvp_show;
+ odvp->attr.store = NULL;
+ ret = sysfs_create_file(&priv->pdev->dev.kobj,
+ &odvp->attr.attr);
+ if (ret)
+ goto out_err;
+ }
+ }
+
+ for (i = 0; i < obj->package.count; i++) {
+ if (obj->package.elements[i].type == ACPI_TYPE_INTEGER)
+ priv->odvp[i] = obj->package.elements[i].integer.value;
+ }
+
+ kfree(obj);
+ return 0;
+
+out_err:
+ cleanup_odvp(priv);
+ kfree(obj);
+ return ret;
+}
+
static void int3400_notify(acpi_handle handle,
u32 event,
void *data)
@@ -217,6 +362,9 @@ static void int3400_notify(acpi_handle handle,
kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE,
thermal_prop);
break;
+ case INT3400_ODVP_CHANGED:
+ evaluate_odvp(priv);
+ break;
default:
/* Ignore unknown notification codes sent to INT3400 device */
break;
@@ -266,11 +414,16 @@ static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
priv->current_uuid_index,
enable);
}
+
+ evaluate_odvp(priv);
+
return result;
}
static struct thermal_zone_device_ops int3400_thermal_ops = {
.get_temp = int3400_thermal_get_temp,
+ .get_mode = int3400_thermal_get_mode,
+ .set_mode = int3400_thermal_set_mode,
};
static struct thermal_zone_params int3400_thermal_params = {
@@ -278,6 +431,32 @@ static struct thermal_zone_params int3400_thermal_params = {
.no_hwmon = true,
};
+static void int3400_setup_gddv(struct int3400_thermal_priv *priv)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+
+ status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL,
+ &buffer);
+ if (ACPI_FAILURE(status) || !buffer.length)
+ return;
+
+ obj = buffer.pointer;
+ if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1
+ || obj->package.elements[0].type != ACPI_TYPE_BUFFER) {
+ kfree(buffer.pointer);
+ return;
+ }
+
+ priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer,
+ obj->package.elements[0].buffer.length,
+ GFP_KERNEL);
+ bin_attr_data_vault.private = priv->data_vault;
+ bin_attr_data_vault.size = obj->package.elements[0].buffer.length;
+ kfree(buffer.pointer);
+}
+
static int int3400_thermal_probe(struct platform_device *pdev)
{
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
@@ -291,12 +470,17 @@ static int int3400_thermal_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ priv->pdev = pdev;
priv->adev = adev;
result = int3400_thermal_get_uuids(priv);
- if (result)
+
+ /* Missing IDSP isn't fatal */
+ if (result && result != -ENODEV)
goto free_priv;
+ priv->current_uuid_index = -1;
+
result = acpi_parse_art(priv->adev->handle, &priv->art_count,
&priv->arts, true);
if (result)
@@ -309,8 +493,9 @@ static int int3400_thermal_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- int3400_thermal_ops.get_mode = int3400_thermal_get_mode;
- int3400_thermal_ops.set_mode = int3400_thermal_set_mode;
+ int3400_setup_gddv(priv);
+
+ evaluate_odvp(priv);
priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
priv, &int3400_thermal_ops,
@@ -327,6 +512,13 @@ static int int3400_thermal_probe(struct platform_device *pdev)
if (result)
goto free_rel_misc;
+ if (priv->data_vault) {
+ result = sysfs_create_group(&pdev->dev.kobj,
+ &data_attribute_group);
+ if (result)
+ goto free_uuid;
+ }
+
result = acpi_install_notify_handler(
priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify,
(void *)priv);
@@ -336,6 +528,12 @@ static int int3400_thermal_probe(struct platform_device *pdev)
return 0;
free_sysfs:
+ cleanup_odvp(priv);
+ if (priv->data_vault) {
+ sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);
+ kfree(priv->data_vault);
+ }
+free_uuid:
sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
free_rel_misc:
if (!priv->rel_misc_dev_res)
@@ -357,11 +555,16 @@ static int int3400_thermal_remove(struct platform_device *pdev)
priv->adev->handle, ACPI_DEVICE_NOTIFY,
int3400_notify);
+ cleanup_odvp(priv);
+
if (!priv->rel_misc_dev_res)
acpi_thermal_rel_misc_device_remove(priv->adev->handle);
+ if (priv->data_vault)
+ sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);
sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
thermal_zone_device_unregister(priv->thermal);
+ kfree(priv->data_vault);
kfree(priv->trts);
kfree(priv->arts);
kfree(priv);
diff --git a/drivers/thermal/k3_bandgap.c b/drivers/thermal/k3_bandgap.c
new file mode 100644
index 000000000000..35f41e8a0b75
--- /dev/null
+++ b/drivers/thermal/k3_bandgap.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI Bandgap temperature sensor driver for K3 SoC Family
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/thermal.h>
+#include <linux/types.h>
+
+#define K3_VTM_DEVINFO_PWR0_OFFSET 0x4
+#define K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK 0xf0
+#define K3_VTM_TMPSENS0_CTRL_OFFSET 0x80
+#define K3_VTM_REGS_PER_TS 0x10
+#define K3_VTM_TS_STAT_DTEMP_MASK 0x3ff
+#define K3_VTM_TMPSENS_CTRL_CBIASSEL BIT(0)
+#define K3_VTM_TMPSENS_CTRL_SOC BIT(5)
+#define K3_VTM_TMPSENS_CTRL_CLRZ BIT(6)
+#define K3_VTM_TMPSENS_CTRL_CLKON_REQ BIT(7)
+
+#define K3_VTM_ADC_BEGIN_VAL 540
+#define K3_VTM_ADC_END_VAL 944
+
+static const int k3_adc_to_temp[] = {
+ -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
+ -37800, -37400, -37000, -36600, -36200, -35800, -35300, -34700, -34200,
+ -33800, -33400, -33000, -32600, -32200, -31800, -31400, -31000, -30600,
+ -30200, -29800, -29400, -29000, -28600, -28200, -27700, -27100, -26600,
+ -26200, -25800, -25400, -25000, -24600, -24200, -23800, -23400, -23000,
+ -22600, -22200, -21800, -21400, -21000, -20500, -19900, -19400, -19000,
+ -18600, -18200, -17800, -17400, -17000, -16600, -16200, -15800, -15400,
+ -15000, -14600, -14200, -13800, -13400, -13000, -12500, -11900, -11400,
+ -11000, -10600, -10200, -9800, -9400, -9000, -8600, -8200, -7800, -7400,
+ -7000, -6600, -6200, -5800, -5400, -5000, -4500, -3900, -3400, -3000,
+ -2600, -2200, -1800, -1400, -1000, -600, -200, 200, 600, 1000, 1400,
+ 1800, 2200, 2600, 3000, 3400, 3900, 4500, 5000, 5400, 5800, 6200, 6600,
+ 7000, 7400, 7800, 8200, 8600, 9000, 9400, 9800, 10200, 10600, 11000,
+ 11400, 11800, 12200, 12700, 13300, 13800, 14200, 14600, 15000, 15400,
+ 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18600, 19000, 19400,
+ 19800, 20200, 20600, 21000, 21400, 21900, 22500, 23000, 23400, 23800,
+ 24200, 24600, 25000, 25400, 25800, 26200, 26600, 27000, 27400, 27800,
+ 28200, 28600, 29000, 29400, 29800, 30200, 30600, 31000, 31400, 31900,
+ 32500, 33000, 33400, 33800, 34200, 34600, 35000, 35400, 35800, 36200,
+ 36600, 37000, 37400, 37800, 38200, 38600, 39000, 39400, 39800, 40200,
+ 40600, 41000, 41400, 41800, 42200, 42600, 43100, 43700, 44200, 44600,
+ 45000, 45400, 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600,
+ 49000, 49400, 49800, 50200, 50600, 51000, 51400, 51800, 52200, 52600,
+ 53000, 53400, 53800, 54200, 54600, 55000, 55400, 55900, 56500, 57000,
+ 57400, 57800, 58200, 58600, 59000, 59400, 59800, 60200, 60600, 61000,
+ 61400, 61800, 62200, 62600, 63000, 63400, 63800, 64200, 64600, 65000,
+ 65400, 65800, 66200, 66600, 67000, 67400, 67800, 68200, 68600, 69000,
+ 69400, 69800, 70200, 70600, 71000, 71500, 72100, 72600, 73000, 73400,
+ 73800, 74200, 74600, 75000, 75400, 75800, 76200, 76600, 77000, 77400,
+ 77800, 78200, 78600, 79000, 79400, 79800, 80200, 80600, 81000, 81400,
+ 81800, 82200, 82600, 83000, 83400, 83800, 84200, 84600, 85000, 85400,
+ 85800, 86200, 86600, 87000, 87400, 87800, 88200, 88600, 89000, 89400,
+ 89800, 90200, 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400,
+ 93800, 94200, 94600, 95000, 95400, 95800, 96200, 96600, 97000, 97500,
+ 98100, 98600, 99000, 99400, 99800, 100200, 100600, 101000, 101400,
+ 101800, 102200, 102600, 103000, 103400, 103800, 104200, 104600, 105000,
+ 105400, 105800, 106200, 106600, 107000, 107400, 107800, 108200, 108600,
+ 109000, 109400, 109800, 110200, 110600, 111000, 111400, 111800, 112200,
+ 112600, 113000, 113400, 113800, 114200, 114600, 115000, 115400, 115800,
+ 116200, 116600, 117000, 117400, 117800, 118200, 118600, 119000, 119400,
+ 119800, 120200, 120600, 121000, 121400, 121800, 122200, 122600, 123000,
+ 123400, 123800, 124200, 124600, 124900, 125000,
+};
+
+struct k3_bandgap {
+ void __iomem *base;
+ const struct k3_bandgap_data *conf;
+};
+
+/* common data structures */
+struct k3_thermal_data {
+ struct thermal_zone_device *tzd;
+ struct k3_bandgap *bgp;
+ int sensor_id;
+ u32 ctrl_offset;
+ u32 stat_offset;
+};
+
+static unsigned int vtm_get_best_value(unsigned int s0, unsigned int s1,
+ unsigned int s2)
+{
+ int d01 = abs(s0 - s1);
+ int d02 = abs(s0 - s2);
+ int d12 = abs(s1 - s2);
+
+ if (d01 <= d02 && d01 <= d12)
+ return (s0 + s1) / 2;
+
+ if (d02 <= d01 && d02 <= d12)
+ return (s0 + s2) / 2;
+
+ return (s1 + s2) / 2;
+}
+
+static int k3_bgp_read_temp(struct k3_thermal_data *devdata,
+ int *temp)
+{
+ struct k3_bandgap *bgp;
+ unsigned int dtemp, s0, s1, s2;
+
+ bgp = devdata->bgp;
+
+ /*
+ * Errata is applicable for am654 pg 1.0 silicon. There
+ * is a variation of the order for 8-10 degree centigrade.
+ * Work around that by getting the average of two closest
+ * readings out of three readings everytime we want to
+ * report temperatures.
+ *
+ * Errata workaround.
+ */
+ s0 = readl(bgp->base + devdata->stat_offset) &
+ K3_VTM_TS_STAT_DTEMP_MASK;
+ s1 = readl(bgp->base + devdata->stat_offset) &
+ K3_VTM_TS_STAT_DTEMP_MASK;
+ s2 = readl(bgp->base + devdata->stat_offset) &
+ K3_VTM_TS_STAT_DTEMP_MASK;
+ dtemp = vtm_get_best_value(s0, s1, s2);
+
+ if (dtemp < K3_VTM_ADC_BEGIN_VAL || dtemp > K3_VTM_ADC_END_VAL)
+ return -EINVAL;
+
+ *temp = k3_adc_to_temp[dtemp - K3_VTM_ADC_BEGIN_VAL];
+
+ return 0;
+}
+
+static int k3_thermal_get_temp(void *devdata, int *temp)
+{
+ struct k3_thermal_data *data = devdata;
+ int ret = 0;
+
+ ret = k3_bgp_read_temp(data, temp);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static const struct thermal_zone_of_device_ops k3_of_thermal_ops = {
+ .get_temp = k3_thermal_get_temp,
+};
+
+static const struct of_device_id of_k3_bandgap_match[];
+
+static int k3_bandgap_probe(struct platform_device *pdev)
+{
+ int ret = 0, cnt, val, id;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct k3_bandgap *bgp;
+ struct k3_thermal_data *data;
+
+ if (ARRAY_SIZE(k3_adc_to_temp) != (K3_VTM_ADC_END_VAL + 1 -
+ K3_VTM_ADC_BEGIN_VAL))
+ return -EINVAL;
+
+ bgp = devm_kzalloc(&pdev->dev, sizeof(*bgp), GFP_KERNEL);
+ if (!bgp)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ bgp->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(bgp->base))
+ return PTR_ERR(bgp->base);
+
+ pm_runtime_enable(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ return ret;
+ }
+
+ /* Get the sensor count in the VTM */
+ val = readl(bgp->base + K3_VTM_DEVINFO_PWR0_OFFSET);
+ cnt = val & K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK;
+ cnt >>= __ffs(K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK);
+
+ data = devm_kcalloc(dev, cnt, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Register the thermal sensors */
+ for (id = 0; id < cnt; id++) {
+ data[id].sensor_id = id;
+ data[id].bgp = bgp;
+ data[id].ctrl_offset = K3_VTM_TMPSENS0_CTRL_OFFSET +
+ id * K3_VTM_REGS_PER_TS;
+ data[id].stat_offset = data[id].ctrl_offset + 0x8;
+
+ val = readl(data[id].bgp->base + data[id].ctrl_offset);
+ val |= (K3_VTM_TMPSENS_CTRL_SOC |
+ K3_VTM_TMPSENS_CTRL_CLRZ |
+ K3_VTM_TMPSENS_CTRL_CLKON_REQ);
+ val &= ~K3_VTM_TMPSENS_CTRL_CBIASSEL;
+ writel(val, data[id].bgp->base + data[id].ctrl_offset);
+
+ data[id].tzd =
+ devm_thermal_zone_of_sensor_register(dev, id,
+ &data[id],
+ &k3_of_thermal_ops);
+ if (IS_ERR(data[id].tzd)) {
+ dev_err(dev, "thermal zone device is NULL\n");
+ ret = PTR_ERR(data[id].tzd);
+ goto err_alloc;
+ }
+ }
+
+ platform_set_drvdata(pdev, bgp);
+
+ return 0;
+
+err_alloc:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static int k3_bandgap_remove(struct platform_device *pdev)
+{
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id of_k3_bandgap_match[] = {
+ {
+ .compatible = "ti,am654-vtm",
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, of_k3_bandgap_match);
+
+static struct platform_driver k3_bandgap_sensor_driver = {
+ .probe = k3_bandgap_probe,
+ .remove = k3_bandgap_remove,
+ .driver = {
+ .name = "k3-soc-thermal",
+ .of_match_table = of_k3_bandgap_match,
+ },
+};
+
+module_platform_driver(k3_bandgap_sensor_driver);
+
+MODULE_DESCRIPTION("K3 bandgap temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile
index 7c8dc6e36693..ec86eef7f6a6 100644
--- a/drivers/thermal/qcom/Makefile
+++ b/drivers/thermal/qcom/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
-qcom_tsens-y += tsens.o tsens-common.o tsens-v0_1.o \
- tsens-8960.o tsens-v2.o tsens-v1.o
+qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \
+ tsens-8960.o
obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c
deleted file mode 100644
index 172545366636..000000000000
--- a/drivers/thermal/qcom/tsens-common.c
+++ /dev/null
@@ -1,843 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- */
-
-#include <linux/debugfs.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/nvmem-consumer.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include "tsens.h"
-
-/**
- * struct tsens_irq_data - IRQ status and temperature violations
- * @up_viol: upper threshold violated
- * @up_thresh: upper threshold temperature value
- * @up_irq_mask: mask register for upper threshold irqs
- * @up_irq_clear: clear register for uppper threshold irqs
- * @low_viol: lower threshold violated
- * @low_thresh: lower threshold temperature value
- * @low_irq_mask: mask register for lower threshold irqs
- * @low_irq_clear: clear register for lower threshold irqs
- * @crit_viol: critical threshold violated
- * @crit_thresh: critical threshold temperature value
- * @crit_irq_mask: mask register for critical threshold irqs
- * @crit_irq_clear: clear register for critical threshold irqs
- *
- * Structure containing data about temperature threshold settings and
- * irq status if they were violated.
- */
-struct tsens_irq_data {
- u32 up_viol;
- int up_thresh;
- u32 up_irq_mask;
- u32 up_irq_clear;
- u32 low_viol;
- int low_thresh;
- u32 low_irq_mask;
- u32 low_irq_clear;
- u32 crit_viol;
- u32 crit_thresh;
- u32 crit_irq_mask;
- u32 crit_irq_clear;
-};
-
-char *qfprom_read(struct device *dev, const char *cname)
-{
- struct nvmem_cell *cell;
- ssize_t data;
- char *ret;
-
- cell = nvmem_cell_get(dev, cname);
- if (IS_ERR(cell))
- return ERR_CAST(cell);
-
- ret = nvmem_cell_read(cell, &data);
- nvmem_cell_put(cell);
-
- return ret;
-}
-
-/*
- * Use this function on devices where slope and offset calculations
- * depend on calibration data read from qfprom. On others the slope
- * and offset values are derived from tz->tzp->slope and tz->tzp->offset
- * resp.
- */
-void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
- u32 *p2, u32 mode)
-{
- int i;
- int num, den;
-
- for (i = 0; i < priv->num_sensors; i++) {
- dev_dbg(priv->dev,
- "%s: sensor%d - data_point1:%#x data_point2:%#x\n",
- __func__, i, p1[i], p2[i]);
-
- priv->sensor[i].slope = SLOPE_DEFAULT;
- if (mode == TWO_PT_CALIB) {
- /*
- * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
- * temp_120_degc - temp_30_degc (x2 - x1)
- */
- num = p2[i] - p1[i];
- num *= SLOPE_FACTOR;
- den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
- priv->sensor[i].slope = num / den;
- }
-
- priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
- (CAL_DEGC_PT1 *
- priv->sensor[i].slope);
- dev_dbg(priv->dev, "%s: offset:%d\n", __func__, priv->sensor[i].offset);
- }
-}
-
-static inline u32 degc_to_code(int degc, const struct tsens_sensor *s)
-{
- u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR);
-
- pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc);
- return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE);
-}
-
-static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
-{
- int degc, num, den;
-
- num = (adc_code * SLOPE_FACTOR) - s->offset;
- den = s->slope;
-
- if (num > 0)
- degc = num + (den / 2);
- else if (num < 0)
- degc = num - (den / 2);
- else
- degc = num;
-
- degc /= den;
-
- return degc;
-}
-
-/**
- * tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
- * @s: Pointer to sensor struct
- * @field: Index into regmap_field array pointing to temperature data
- *
- * This function handles temperature returned in ADC code or deciCelsius
- * depending on IP version.
- *
- * Return: Temperature in milliCelsius on success, a negative errno will
- * be returned in error cases
- */
-static int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
-{
- struct tsens_priv *priv = s->priv;
- u32 resolution;
- u32 temp = 0;
- int ret;
-
- resolution = priv->fields[LAST_TEMP_0].msb -
- priv->fields[LAST_TEMP_0].lsb;
-
- ret = regmap_field_read(priv->rf[field], &temp);
- if (ret)
- return ret;
-
- /* Convert temperature from ADC code to milliCelsius */
- if (priv->feat->adc)
- return code_to_degc(temp, s) * 1000;
-
- /* deciCelsius -> milliCelsius along with sign extension */
- return sign_extend32(temp, resolution) * 100;
-}
-
-/**
- * tsens_mC_to_hw - Convert temperature to hardware register value
- * @s: Pointer to sensor struct
- * @temp: temperature in milliCelsius to be programmed to hardware
- *
- * This function outputs the value to be written to hardware in ADC code
- * or deciCelsius depending on IP version.
- *
- * Return: ADC code or temperature in deciCelsius.
- */
-static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp)
-{
- struct tsens_priv *priv = s->priv;
-
- /* milliC to adc code */
- if (priv->feat->adc)
- return degc_to_code(temp / 1000, s);
-
- /* milliC to deciC */
- return temp / 100;
-}
-
-static inline enum tsens_ver tsens_version(struct tsens_priv *priv)
-{
- return priv->feat->ver_major;
-}
-
-static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
- enum tsens_irq_type irq_type, bool enable)
-{
- u32 index = 0;
-
- switch (irq_type) {
- case UPPER:
- index = UP_INT_CLEAR_0 + hw_id;
- break;
- case LOWER:
- index = LOW_INT_CLEAR_0 + hw_id;
- break;
- case CRITICAL:
- /* No critical interrupts before v2 */
- return;
- }
- regmap_field_write(priv->rf[index], enable ? 0 : 1);
-}
-
-static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
- enum tsens_irq_type irq_type, bool enable)
-{
- u32 index_mask = 0, index_clear = 0;
-
- /*
- * To enable the interrupt flag for a sensor:
- * - clear the mask bit
- * To disable the interrupt flag for a sensor:
- * - Mask further interrupts for this sensor
- * - Write 1 followed by 0 to clear the interrupt
- */
- switch (irq_type) {
- case UPPER:
- index_mask = UP_INT_MASK_0 + hw_id;
- index_clear = UP_INT_CLEAR_0 + hw_id;
- break;
- case LOWER:
- index_mask = LOW_INT_MASK_0 + hw_id;
- index_clear = LOW_INT_CLEAR_0 + hw_id;
- break;
- case CRITICAL:
- index_mask = CRIT_INT_MASK_0 + hw_id;
- index_clear = CRIT_INT_CLEAR_0 + hw_id;
- break;
- }
-
- if (enable) {
- regmap_field_write(priv->rf[index_mask], 0);
- } else {
- regmap_field_write(priv->rf[index_mask], 1);
- regmap_field_write(priv->rf[index_clear], 1);
- regmap_field_write(priv->rf[index_clear], 0);
- }
-}
-
-/**
- * tsens_set_interrupt - Set state of an interrupt
- * @priv: Pointer to tsens controller private data
- * @hw_id: Hardware ID aka. sensor number
- * @irq_type: irq_type from enum tsens_irq_type
- * @enable: false = disable, true = enable
- *
- * Call IP-specific function to set state of an interrupt
- *
- * Return: void
- */
-static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
- enum tsens_irq_type irq_type, bool enable)
-{
- dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
- irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
- enable ? "en" : "dis");
- if (tsens_version(priv) > VER_1_X)
- tsens_set_interrupt_v2(priv, hw_id, irq_type, enable);
- else
- tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
-}
-
-/**
- * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold
- * @priv: Pointer to tsens controller private data
- * @hw_id: Hardware ID aka. sensor number
- * @d: Pointer to irq state data
- *
- * Return: 0 if threshold was not violated, 1 if it was violated and negative
- * errno in case of errors
- */
-static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
- struct tsens_irq_data *d)
-{
- int ret;
-
- ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol);
- if (ret)
- return ret;
- ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
- if (ret)
- return ret;
-
- if (priv->feat->crit_int) {
- ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id],
- &d->crit_viol);
- if (ret)
- return ret;
- }
-
- if (d->up_viol || d->low_viol || d->crit_viol)
- return 1;
-
- return 0;
-}
-
-static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
- const struct tsens_sensor *s,
- struct tsens_irq_data *d)
-{
- int ret;
-
- ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear);
- if (ret)
- return ret;
- ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear);
- if (ret)
- return ret;
- if (tsens_version(priv) > VER_1_X) {
- ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask);
- if (ret)
- return ret;
- ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
- if (ret)
- return ret;
- ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id],
- &d->crit_irq_clear);
- if (ret)
- return ret;
- ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id],
- &d->crit_irq_mask);
- if (ret)
- return ret;
-
- d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
- } else {
- /* No mask register on older TSENS */
- d->up_irq_mask = 0;
- d->low_irq_mask = 0;
- d->crit_irq_clear = 0;
- d->crit_irq_mask = 0;
- d->crit_thresh = 0;
- }
-
- d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
- d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
-
- dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
- hw_id, __func__,
- (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
- d->low_viol, d->up_viol, d->crit_viol,
- d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear,
- d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask);
- dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__,
- (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
- d->low_thresh, d->up_thresh, d->crit_thresh);
-
- return 0;
-}
-
-static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
-{
- if (ver > VER_1_X)
- return mask & (1 << hw_id);
-
- /* v1, v0.1 don't have a irq mask register */
- return 0;
-}
-
-/**
- * tsens_critical_irq_thread() - Threaded handler for critical interrupts
- * @irq: irq number
- * @data: tsens controller private data
- *
- * Check FSM watchdog bark status and clear if needed.
- * Check all sensors to find ones that violated their critical threshold limits.
- * Clear and then re-enable the interrupt.
- *
- * The level-triggered interrupt might deassert if the temperature returned to
- * within the threshold limits by the time the handler got scheduled. We
- * consider the irq to have been handled in that case.
- *
- * Return: IRQ_HANDLED
- */
-irqreturn_t tsens_critical_irq_thread(int irq, void *data)
-{
- struct tsens_priv *priv = data;
- struct tsens_irq_data d;
- int temp, ret, i;
- u32 wdog_status, wdog_count;
-
- if (priv->feat->has_watchdog) {
- ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS],
- &wdog_status);
- if (ret)
- return ret;
-
- if (wdog_status) {
- /* Clear WDOG interrupt */
- regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
- regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
- ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT],
- &wdog_count);
- if (ret)
- return ret;
- if (wdog_count)
- dev_dbg(priv->dev, "%s: watchdog count: %d\n",
- __func__, wdog_count);
-
- /* Fall through to handle critical interrupts if any */
- }
- }
-
- for (i = 0; i < priv->num_sensors; i++) {
- const struct tsens_sensor *s = &priv->sensor[i];
- u32 hw_id = s->hw_id;
-
- if (IS_ERR(s->tzd))
- continue;
- if (!tsens_threshold_violated(priv, hw_id, &d))
- continue;
- ret = get_temp_tsens_valid(s, &temp);
- if (ret) {
- dev_err(priv->dev, "[%u] %s: error reading sensor\n",
- hw_id, __func__);
- continue;
- }
-
- tsens_read_irq_state(priv, hw_id, s, &d);
- if (d.crit_viol &&
- !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) {
- /* Mask critical interrupts, unused on Linux */
- tsens_set_interrupt(priv, hw_id, CRITICAL, false);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-/**
- * tsens_irq_thread - Threaded interrupt handler for uplow interrupts
- * @irq: irq number
- * @data: tsens controller private data
- *
- * Check all sensors to find ones that violated their threshold limits. If the
- * temperature is still outside the limits, call thermal_zone_device_update() to
- * update the thresholds, else re-enable the interrupts.
- *
- * The level-triggered interrupt might deassert if the temperature returned to
- * within the threshold limits by the time the handler got scheduled. We
- * consider the irq to have been handled in that case.
- *
- * Return: IRQ_HANDLED
- */
-irqreturn_t tsens_irq_thread(int irq, void *data)
-{
- struct tsens_priv *priv = data;
- struct tsens_irq_data d;
- bool enable = true, disable = false;
- unsigned long flags;
- int temp, ret, i;
-
- for (i = 0; i < priv->num_sensors; i++) {
- bool trigger = false;
- const struct tsens_sensor *s = &priv->sensor[i];
- u32 hw_id = s->hw_id;
-
- if (IS_ERR(s->tzd))
- continue;
- if (!tsens_threshold_violated(priv, hw_id, &d))
- continue;
- ret = get_temp_tsens_valid(s, &temp);
- if (ret) {
- dev_err(priv->dev, "[%u] %s: error reading sensor\n", hw_id, __func__);
- continue;
- }
-
- spin_lock_irqsave(&priv->ul_lock, flags);
-
- tsens_read_irq_state(priv, hw_id, s, &d);
-
- if (d.up_viol &&
- !masked_irq(hw_id, d.up_irq_mask, tsens_version(priv))) {
- tsens_set_interrupt(priv, hw_id, UPPER, disable);
- if (d.up_thresh > temp) {
- dev_dbg(priv->dev, "[%u] %s: re-arm upper\n",
- hw_id, __func__);
- tsens_set_interrupt(priv, hw_id, UPPER, enable);
- } else {
- trigger = true;
- /* Keep irq masked */
- }
- } else if (d.low_viol &&
- !masked_irq(hw_id, d.low_irq_mask, tsens_version(priv))) {
- tsens_set_interrupt(priv, hw_id, LOWER, disable);
- if (d.low_thresh < temp) {
- dev_dbg(priv->dev, "[%u] %s: re-arm low\n",
- hw_id, __func__);
- tsens_set_interrupt(priv, hw_id, LOWER, enable);
- } else {
- trigger = true;
- /* Keep irq masked */
- }
- }
-
- spin_unlock_irqrestore(&priv->ul_lock, flags);
-
- if (trigger) {
- dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
- hw_id, __func__, temp);
- thermal_zone_device_update(s->tzd,
- THERMAL_EVENT_UNSPECIFIED);
- } else {
- dev_dbg(priv->dev, "[%u] %s: no violation: %d\n",
- hw_id, __func__, temp);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-int tsens_set_trips(void *_sensor, int low, int high)
-{
- struct tsens_sensor *s = _sensor;
- struct tsens_priv *priv = s->priv;
- struct device *dev = priv->dev;
- struct tsens_irq_data d;
- unsigned long flags;
- int high_val, low_val, cl_high, cl_low;
- u32 hw_id = s->hw_id;
-
- dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
- hw_id, __func__, low, high);
-
- cl_high = clamp_val(high, -40000, 120000);
- cl_low = clamp_val(low, -40000, 120000);
-
- high_val = tsens_mC_to_hw(s, cl_high);
- low_val = tsens_mC_to_hw(s, cl_low);
-
- spin_lock_irqsave(&priv->ul_lock, flags);
-
- tsens_read_irq_state(priv, hw_id, s, &d);
-
- /* Write the new thresholds and clear the status */
- regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val);
- regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val);
- tsens_set_interrupt(priv, hw_id, LOWER, true);
- tsens_set_interrupt(priv, hw_id, UPPER, true);
-
- spin_unlock_irqrestore(&priv->ul_lock, flags);
-
- dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
- hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
-
- return 0;
-}
-
-int tsens_enable_irq(struct tsens_priv *priv)
-{
- int ret;
- int val = tsens_version(priv) > VER_1_X ? 7 : 1;
-
- ret = regmap_field_write(priv->rf[INT_EN], val);
- if (ret < 0)
- dev_err(priv->dev, "%s: failed to enable interrupts\n", __func__);
-
- return ret;
-}
-
-void tsens_disable_irq(struct tsens_priv *priv)
-{
- regmap_field_write(priv->rf[INT_EN], 0);
-}
-
-int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
-{
- struct tsens_priv *priv = s->priv;
- int hw_id = s->hw_id;
- u32 temp_idx = LAST_TEMP_0 + hw_id;
- u32 valid_idx = VALID_0 + hw_id;
- u32 valid;
- int ret;
-
- ret = regmap_field_read(priv->rf[valid_idx], &valid);
- if (ret)
- return ret;
- while (!valid) {
- /* Valid bit is 0 for 6 AHB clock cycles.
- * At 19.2MHz, 1 AHB clock is ~60ns.
- * We should enter this loop very, very rarely.
- */
- ndelay(400);
- ret = regmap_field_read(priv->rf[valid_idx], &valid);
- if (ret)
- return ret;
- }
-
- /* Valid bit is set, OK to read the temperature */
- *temp = tsens_hw_to_mC(s, temp_idx);
-
- return 0;
-}
-
-int get_temp_common(const struct tsens_sensor *s, int *temp)
-{
- struct tsens_priv *priv = s->priv;
- int hw_id = s->hw_id;
- int last_temp = 0, ret;
-
- ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
- if (ret)
- return ret;
-
- *temp = code_to_degc(last_temp, s) * 1000;
-
- return 0;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int dbg_sensors_show(struct seq_file *s, void *data)
-{
- struct platform_device *pdev = s->private;
- struct tsens_priv *priv = platform_get_drvdata(pdev);
- int i;
-
- seq_printf(s, "max: %2d\nnum: %2d\n\n",
- priv->feat->max_sensors, priv->num_sensors);
-
- seq_puts(s, " id slope offset\n--------------------------\n");
- for (i = 0; i < priv->num_sensors; i++) {
- seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id,
- priv->sensor[i].slope, priv->sensor[i].offset);
- }
-
- return 0;
-}
-
-static int dbg_version_show(struct seq_file *s, void *data)
-{
- struct platform_device *pdev = s->private;
- struct tsens_priv *priv = platform_get_drvdata(pdev);
- u32 maj_ver, min_ver, step_ver;
- int ret;
-
- if (tsens_version(priv) > VER_0_1) {
- ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver);
- if (ret)
- return ret;
- ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver);
- if (ret)
- return ret;
- ret = regmap_field_read(priv->rf[VER_STEP], &step_ver);
- if (ret)
- return ret;
- seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver);
- } else {
- seq_puts(s, "0.1.0\n");
- }
-
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(dbg_version);
-DEFINE_SHOW_ATTRIBUTE(dbg_sensors);
-
-static void tsens_debug_init(struct platform_device *pdev)
-{
- struct tsens_priv *priv = platform_get_drvdata(pdev);
- struct dentry *root, *file;
-
- root = debugfs_lookup("tsens", NULL);
- if (!root)
- priv->debug_root = debugfs_create_dir("tsens", NULL);
- else
- priv->debug_root = root;
-
- file = debugfs_lookup("version", priv->debug_root);
- if (!file)
- debugfs_create_file("version", 0444, priv->debug_root,
- pdev, &dbg_version_fops);
-
- /* A directory for each instance of the TSENS IP */
- priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root);
- debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops);
-}
-#else
-static inline void tsens_debug_init(struct platform_device *pdev) {}
-#endif
-
-static const struct regmap_config tsens_config = {
- .name = "tm",
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
-};
-
-static const struct regmap_config tsens_srot_config = {
- .name = "srot",
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
-};
-
-int __init init_common(struct tsens_priv *priv)
-{
- void __iomem *tm_base, *srot_base;
- struct device *dev = priv->dev;
- u32 ver_minor;
- struct resource *res;
- u32 enabled;
- int ret, i, j;
- struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
-
- if (!op)
- return -EINVAL;
-
- if (op->num_resources > 1) {
- /* DT with separate SROT and TM address space */
- priv->tm_offset = 0;
- res = platform_get_resource(op, IORESOURCE_MEM, 1);
- srot_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(srot_base)) {
- ret = PTR_ERR(srot_base);
- goto err_put_device;
- }
-
- priv->srot_map = devm_regmap_init_mmio(dev, srot_base,
- &tsens_srot_config);
- if (IS_ERR(priv->srot_map)) {
- ret = PTR_ERR(priv->srot_map);
- goto err_put_device;
- }
- } else {
- /* old DTs where SROT and TM were in a contiguous 2K block */
- priv->tm_offset = 0x1000;
- }
-
- res = platform_get_resource(op, IORESOURCE_MEM, 0);
- tm_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(tm_base)) {
- ret = PTR_ERR(tm_base);
- goto err_put_device;
- }
-
- priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
- if (IS_ERR(priv->tm_map)) {
- ret = PTR_ERR(priv->tm_map);
- goto err_put_device;
- }
-
- if (tsens_version(priv) > VER_0_1) {
- for (i = VER_MAJOR; i <= VER_STEP; i++) {
- priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map,
- priv->fields[i]);
- if (IS_ERR(priv->rf[i]))
- return PTR_ERR(priv->rf[i]);
- }
- ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);
- if (ret)
- goto err_put_device;
- }
-
- priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
- priv->fields[TSENS_EN]);
- if (IS_ERR(priv->rf[TSENS_EN])) {
- ret = PTR_ERR(priv->rf[TSENS_EN]);
- goto err_put_device;
- }
- ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
- if (ret)
- goto err_put_device;
- if (!enabled) {
- dev_err(dev, "%s: device not enabled\n", __func__);
- ret = -ENODEV;
- goto err_put_device;
- }
-
- priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
- priv->fields[SENSOR_EN]);
- if (IS_ERR(priv->rf[SENSOR_EN])) {
- ret = PTR_ERR(priv->rf[SENSOR_EN]);
- goto err_put_device;
- }
- priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map,
- priv->fields[INT_EN]);
- if (IS_ERR(priv->rf[INT_EN])) {
- ret = PTR_ERR(priv->rf[INT_EN]);
- goto err_put_device;
- }
-
- /* This loop might need changes if enum regfield_ids is reordered */
- for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
- for (i = 0; i < priv->feat->max_sensors; i++) {
- int idx = j + i;
-
- priv->rf[idx] = devm_regmap_field_alloc(dev, priv->tm_map,
- priv->fields[idx]);
- if (IS_ERR(priv->rf[idx])) {
- ret = PTR_ERR(priv->rf[idx]);
- goto err_put_device;
- }
- }
- }
-
- if (priv->feat->crit_int) {
- /* Loop might need changes if enum regfield_ids is reordered */
- for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
- for (i = 0; i < priv->feat->max_sensors; i++) {
- int idx = j + i;
-
- priv->rf[idx] =
- devm_regmap_field_alloc(dev,
- priv->tm_map,
- priv->fields[idx]);
- if (IS_ERR(priv->rf[idx])) {
- ret = PTR_ERR(priv->rf[idx]);
- goto err_put_device;
- }
- }
- }
- }
-
- if (tsens_version(priv) > VER_1_X && ver_minor > 2) {
- /* Watchdog is present only on v2.3+ */
- priv->feat->has_watchdog = 1;
- for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
- priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map,
- priv->fields[i]);
- if (IS_ERR(priv->rf[i])) {
- ret = PTR_ERR(priv->rf[i]);
- goto err_put_device;
- }
- }
- /*
- * Watchdog is already enabled, unmask the bark.
- * Disable cycle completion monitoring
- */
- regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
- regmap_field_write(priv->rf[CC_MON_MASK], 1);
- }
-
- spin_lock_init(&priv->ul_lock);
- tsens_enable_irq(priv);
- tsens_debug_init(op);
-
-err_put_device:
- put_device(&op->dev);
- return ret;
-}
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index 2f77d235cf73..8d3e94d2a9ed 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -1,19 +1,857 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019, 2020, Linaro Ltd.
*/
#include <linux/debugfs.h>
#include <linux/err.h>
+#include <linux/io.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include "tsens.h"
+/**
+ * struct tsens_irq_data - IRQ status and temperature violations
+ * @up_viol: upper threshold violated
+ * @up_thresh: upper threshold temperature value
+ * @up_irq_mask: mask register for upper threshold irqs
+ * @up_irq_clear: clear register for uppper threshold irqs
+ * @low_viol: lower threshold violated
+ * @low_thresh: lower threshold temperature value
+ * @low_irq_mask: mask register for lower threshold irqs
+ * @low_irq_clear: clear register for lower threshold irqs
+ * @crit_viol: critical threshold violated
+ * @crit_thresh: critical threshold temperature value
+ * @crit_irq_mask: mask register for critical threshold irqs
+ * @crit_irq_clear: clear register for critical threshold irqs
+ *
+ * Structure containing data about temperature threshold settings and
+ * irq status if they were violated.
+ */
+struct tsens_irq_data {
+ u32 up_viol;
+ int up_thresh;
+ u32 up_irq_mask;
+ u32 up_irq_clear;
+ u32 low_viol;
+ int low_thresh;
+ u32 low_irq_mask;
+ u32 low_irq_clear;
+ u32 crit_viol;
+ u32 crit_thresh;
+ u32 crit_irq_mask;
+ u32 crit_irq_clear;
+};
+
+char *qfprom_read(struct device *dev, const char *cname)
+{
+ struct nvmem_cell *cell;
+ ssize_t data;
+ char *ret;
+
+ cell = nvmem_cell_get(dev, cname);
+ if (IS_ERR(cell))
+ return ERR_CAST(cell);
+
+ ret = nvmem_cell_read(cell, &data);
+ nvmem_cell_put(cell);
+
+ return ret;
+}
+
+/*
+ * Use this function on devices where slope and offset calculations
+ * depend on calibration data read from qfprom. On others the slope
+ * and offset values are derived from tz->tzp->slope and tz->tzp->offset
+ * resp.
+ */
+void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
+ u32 *p2, u32 mode)
+{
+ int i;
+ int num, den;
+
+ for (i = 0; i < priv->num_sensors; i++) {
+ dev_dbg(priv->dev,
+ "%s: sensor%d - data_point1:%#x data_point2:%#x\n",
+ __func__, i, p1[i], p2[i]);
+
+ priv->sensor[i].slope = SLOPE_DEFAULT;
+ if (mode == TWO_PT_CALIB) {
+ /*
+ * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
+ * temp_120_degc - temp_30_degc (x2 - x1)
+ */
+ num = p2[i] - p1[i];
+ num *= SLOPE_FACTOR;
+ den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
+ priv->sensor[i].slope = num / den;
+ }
+
+ priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
+ (CAL_DEGC_PT1 *
+ priv->sensor[i].slope);
+ dev_dbg(priv->dev, "%s: offset:%d\n", __func__,
+ priv->sensor[i].offset);
+ }
+}
+
+static inline u32 degc_to_code(int degc, const struct tsens_sensor *s)
+{
+ u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR);
+
+ pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc);
+ return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE);
+}
+
+static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
+{
+ int degc, num, den;
+
+ num = (adc_code * SLOPE_FACTOR) - s->offset;
+ den = s->slope;
+
+ if (num > 0)
+ degc = num + (den / 2);
+ else if (num < 0)
+ degc = num - (den / 2);
+ else
+ degc = num;
+
+ degc /= den;
+
+ return degc;
+}
+
+/**
+ * tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
+ * @s: Pointer to sensor struct
+ * @field: Index into regmap_field array pointing to temperature data
+ *
+ * This function handles temperature returned in ADC code or deciCelsius
+ * depending on IP version.
+ *
+ * Return: Temperature in milliCelsius on success, a negative errno will
+ * be returned in error cases
+ */
+static int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
+{
+ struct tsens_priv *priv = s->priv;
+ u32 resolution;
+ u32 temp = 0;
+ int ret;
+
+ resolution = priv->fields[LAST_TEMP_0].msb -
+ priv->fields[LAST_TEMP_0].lsb;
+
+ ret = regmap_field_read(priv->rf[field], &temp);
+ if (ret)
+ return ret;
+
+ /* Convert temperature from ADC code to milliCelsius */
+ if (priv->feat->adc)
+ return code_to_degc(temp, s) * 1000;
+
+ /* deciCelsius -> milliCelsius along with sign extension */
+ return sign_extend32(temp, resolution) * 100;
+}
+
+/**
+ * tsens_mC_to_hw - Convert temperature to hardware register value
+ * @s: Pointer to sensor struct
+ * @temp: temperature in milliCelsius to be programmed to hardware
+ *
+ * This function outputs the value to be written to hardware in ADC code
+ * or deciCelsius depending on IP version.
+ *
+ * Return: ADC code or temperature in deciCelsius.
+ */
+static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp)
+{
+ struct tsens_priv *priv = s->priv;
+
+ /* milliC to adc code */
+ if (priv->feat->adc)
+ return degc_to_code(temp / 1000, s);
+
+ /* milliC to deciC */
+ return temp / 100;
+}
+
+static inline enum tsens_ver tsens_version(struct tsens_priv *priv)
+{
+ return priv->feat->ver_major;
+}
+
+static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
+ enum tsens_irq_type irq_type, bool enable)
+{
+ u32 index = 0;
+
+ switch (irq_type) {
+ case UPPER:
+ index = UP_INT_CLEAR_0 + hw_id;
+ break;
+ case LOWER:
+ index = LOW_INT_CLEAR_0 + hw_id;
+ break;
+ case CRITICAL:
+ /* No critical interrupts before v2 */
+ return;
+ }
+ regmap_field_write(priv->rf[index], enable ? 0 : 1);
+}
+
+static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
+ enum tsens_irq_type irq_type, bool enable)
+{
+ u32 index_mask = 0, index_clear = 0;
+
+ /*
+ * To enable the interrupt flag for a sensor:
+ * - clear the mask bit
+ * To disable the interrupt flag for a sensor:
+ * - Mask further interrupts for this sensor
+ * - Write 1 followed by 0 to clear the interrupt
+ */
+ switch (irq_type) {
+ case UPPER:
+ index_mask = UP_INT_MASK_0 + hw_id;
+ index_clear = UP_INT_CLEAR_0 + hw_id;
+ break;
+ case LOWER:
+ index_mask = LOW_INT_MASK_0 + hw_id;
+ index_clear = LOW_INT_CLEAR_0 + hw_id;
+ break;
+ case CRITICAL:
+ index_mask = CRIT_INT_MASK_0 + hw_id;
+ index_clear = CRIT_INT_CLEAR_0 + hw_id;
+ break;
+ }
+
+ if (enable) {
+ regmap_field_write(priv->rf[index_mask], 0);
+ } else {
+ regmap_field_write(priv->rf[index_mask], 1);
+ regmap_field_write(priv->rf[index_clear], 1);
+ regmap_field_write(priv->rf[index_clear], 0);
+ }
+}
+
+/**
+ * tsens_set_interrupt - Set state of an interrupt
+ * @priv: Pointer to tsens controller private data
+ * @hw_id: Hardware ID aka. sensor number
+ * @irq_type: irq_type from enum tsens_irq_type
+ * @enable: false = disable, true = enable
+ *
+ * Call IP-specific function to set state of an interrupt
+ *
+ * Return: void
+ */
+static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
+ enum tsens_irq_type irq_type, bool enable)
+{
+ dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
+ irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
+ enable ? "en" : "dis");
+ if (tsens_version(priv) > VER_1_X)
+ tsens_set_interrupt_v2(priv, hw_id, irq_type, enable);
+ else
+ tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
+}
+
+/**
+ * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold
+ * @priv: Pointer to tsens controller private data
+ * @hw_id: Hardware ID aka. sensor number
+ * @d: Pointer to irq state data
+ *
+ * Return: 0 if threshold was not violated, 1 if it was violated and negative
+ * errno in case of errors
+ */
+static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
+ struct tsens_irq_data *d)
+{
+ int ret;
+
+ ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol);
+ if (ret)
+ return ret;
+ ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
+ if (ret)
+ return ret;
+
+ if (priv->feat->crit_int) {
+ ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id],
+ &d->crit_viol);
+ if (ret)
+ return ret;
+ }
+
+ if (d->up_viol || d->low_viol || d->crit_viol)
+ return 1;
+
+ return 0;
+}
+
+static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
+ const struct tsens_sensor *s,
+ struct tsens_irq_data *d)
+{
+ int ret;
+
+ ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear);
+ if (ret)
+ return ret;
+ ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear);
+ if (ret)
+ return ret;
+ if (tsens_version(priv) > VER_1_X) {
+ ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask);
+ if (ret)
+ return ret;
+ ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
+ if (ret)
+ return ret;
+ ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id],
+ &d->crit_irq_clear);
+ if (ret)
+ return ret;
+ ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id],
+ &d->crit_irq_mask);
+ if (ret)
+ return ret;
+
+ d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
+ } else {
+ /* No mask register on older TSENS */
+ d->up_irq_mask = 0;
+ d->low_irq_mask = 0;
+ d->crit_irq_clear = 0;
+ d->crit_irq_mask = 0;
+ d->crit_thresh = 0;
+ }
+
+ d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
+ d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
+
+ dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
+ hw_id, __func__,
+ (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
+ d->low_viol, d->up_viol, d->crit_viol,
+ d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear,
+ d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask);
+ dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__,
+ (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
+ d->low_thresh, d->up_thresh, d->crit_thresh);
+
+ return 0;
+}
+
+static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
+{
+ if (ver > VER_1_X)
+ return mask & (1 << hw_id);
+
+ /* v1, v0.1 don't have a irq mask register */
+ return 0;
+}
+
+/**
+ * tsens_critical_irq_thread() - Threaded handler for critical interrupts
+ * @irq: irq number
+ * @data: tsens controller private data
+ *
+ * Check FSM watchdog bark status and clear if needed.
+ * Check all sensors to find ones that violated their critical threshold limits.
+ * Clear and then re-enable the interrupt.
+ *
+ * The level-triggered interrupt might deassert if the temperature returned to
+ * within the threshold limits by the time the handler got scheduled. We
+ * consider the irq to have been handled in that case.
+ *
+ * Return: IRQ_HANDLED
+ */
+irqreturn_t tsens_critical_irq_thread(int irq, void *data)
+{
+ struct tsens_priv *priv = data;
+ struct tsens_irq_data d;
+ int temp, ret, i;
+ u32 wdog_status, wdog_count;
+
+ if (priv->feat->has_watchdog) {
+ ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS],
+ &wdog_status);
+ if (ret)
+ return ret;
+
+ if (wdog_status) {
+ /* Clear WDOG interrupt */
+ regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
+ regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
+ ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT],
+ &wdog_count);
+ if (ret)
+ return ret;
+ if (wdog_count)
+ dev_dbg(priv->dev, "%s: watchdog count: %d\n",
+ __func__, wdog_count);
+
+ /* Fall through to handle critical interrupts if any */
+ }
+ }
+
+ for (i = 0; i < priv->num_sensors; i++) {
+ const struct tsens_sensor *s = &priv->sensor[i];
+ u32 hw_id = s->hw_id;
+
+ if (IS_ERR(s->tzd))
+ continue;
+ if (!tsens_threshold_violated(priv, hw_id, &d))
+ continue;
+ ret = get_temp_tsens_valid(s, &temp);
+ if (ret) {
+ dev_err(priv->dev, "[%u] %s: error reading sensor\n",
+ hw_id, __func__);
+ continue;
+ }
+
+ tsens_read_irq_state(priv, hw_id, s, &d);
+ if (d.crit_viol &&
+ !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) {
+ /* Mask critical interrupts, unused on Linux */
+ tsens_set_interrupt(priv, hw_id, CRITICAL, false);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * tsens_irq_thread - Threaded interrupt handler for uplow interrupts
+ * @irq: irq number
+ * @data: tsens controller private data
+ *
+ * Check all sensors to find ones that violated their threshold limits. If the
+ * temperature is still outside the limits, call thermal_zone_device_update() to
+ * update the thresholds, else re-enable the interrupts.
+ *
+ * The level-triggered interrupt might deassert if the temperature returned to
+ * within the threshold limits by the time the handler got scheduled. We
+ * consider the irq to have been handled in that case.
+ *
+ * Return: IRQ_HANDLED
+ */
+irqreturn_t tsens_irq_thread(int irq, void *data)
+{
+ struct tsens_priv *priv = data;
+ struct tsens_irq_data d;
+ bool enable = true, disable = false;
+ unsigned long flags;
+ int temp, ret, i;
+
+ for (i = 0; i < priv->num_sensors; i++) {
+ bool trigger = false;
+ const struct tsens_sensor *s = &priv->sensor[i];
+ u32 hw_id = s->hw_id;
+
+ if (IS_ERR(s->tzd))
+ continue;
+ if (!tsens_threshold_violated(priv, hw_id, &d))
+ continue;
+ ret = get_temp_tsens_valid(s, &temp);
+ if (ret) {
+ dev_err(priv->dev, "[%u] %s: error reading sensor\n",
+ hw_id, __func__);
+ continue;
+ }
+
+ spin_lock_irqsave(&priv->ul_lock, flags);
+
+ tsens_read_irq_state(priv, hw_id, s, &d);
+
+ if (d.up_viol &&
+ !masked_irq(hw_id, d.up_irq_mask, tsens_version(priv))) {
+ tsens_set_interrupt(priv, hw_id, UPPER, disable);
+ if (d.up_thresh > temp) {
+ dev_dbg(priv->dev, "[%u] %s: re-arm upper\n",
+ hw_id, __func__);
+ tsens_set_interrupt(priv, hw_id, UPPER, enable);
+ } else {
+ trigger = true;
+ /* Keep irq masked */
+ }
+ } else if (d.low_viol &&
+ !masked_irq(hw_id, d.low_irq_mask, tsens_version(priv))) {
+ tsens_set_interrupt(priv, hw_id, LOWER, disable);
+ if (d.low_thresh < temp) {
+ dev_dbg(priv->dev, "[%u] %s: re-arm low\n",
+ hw_id, __func__);
+ tsens_set_interrupt(priv, hw_id, LOWER, enable);
+ } else {
+ trigger = true;
+ /* Keep irq masked */
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->ul_lock, flags);
+
+ if (trigger) {
+ dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
+ hw_id, __func__, temp);
+ thermal_zone_device_update(s->tzd,
+ THERMAL_EVENT_UNSPECIFIED);
+ } else {
+ dev_dbg(priv->dev, "[%u] %s: no violation: %d\n",
+ hw_id, __func__, temp);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+int tsens_set_trips(void *_sensor, int low, int high)
+{
+ struct tsens_sensor *s = _sensor;
+ struct tsens_priv *priv = s->priv;
+ struct device *dev = priv->dev;
+ struct tsens_irq_data d;
+ unsigned long flags;
+ int high_val, low_val, cl_high, cl_low;
+ u32 hw_id = s->hw_id;
+
+ dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
+ hw_id, __func__, low, high);
+
+ cl_high = clamp_val(high, -40000, 120000);
+ cl_low = clamp_val(low, -40000, 120000);
+
+ high_val = tsens_mC_to_hw(s, cl_high);
+ low_val = tsens_mC_to_hw(s, cl_low);
+
+ spin_lock_irqsave(&priv->ul_lock, flags);
+
+ tsens_read_irq_state(priv, hw_id, s, &d);
+
+ /* Write the new thresholds and clear the status */
+ regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val);
+ regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val);
+ tsens_set_interrupt(priv, hw_id, LOWER, true);
+ tsens_set_interrupt(priv, hw_id, UPPER, true);
+
+ spin_unlock_irqrestore(&priv->ul_lock, flags);
+
+ dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
+ hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
+
+ return 0;
+}
+
+int tsens_enable_irq(struct tsens_priv *priv)
+{
+ int ret;
+ int val = tsens_version(priv) > VER_1_X ? 7 : 1;
+
+ ret = regmap_field_write(priv->rf[INT_EN], val);
+ if (ret < 0)
+ dev_err(priv->dev, "%s: failed to enable interrupts\n",
+ __func__);
+
+ return ret;
+}
+
+void tsens_disable_irq(struct tsens_priv *priv)
+{
+ regmap_field_write(priv->rf[INT_EN], 0);
+}
+
+int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
+{
+ struct tsens_priv *priv = s->priv;
+ int hw_id = s->hw_id;
+ u32 temp_idx = LAST_TEMP_0 + hw_id;
+ u32 valid_idx = VALID_0 + hw_id;
+ u32 valid;
+ int ret;
+
+ ret = regmap_field_read(priv->rf[valid_idx], &valid);
+ if (ret)
+ return ret;
+ while (!valid) {
+ /* Valid bit is 0 for 6 AHB clock cycles.
+ * At 19.2MHz, 1 AHB clock is ~60ns.
+ * We should enter this loop very, very rarely.
+ */
+ ndelay(400);
+ ret = regmap_field_read(priv->rf[valid_idx], &valid);
+ if (ret)
+ return ret;
+ }
+
+ /* Valid bit is set, OK to read the temperature */
+ *temp = tsens_hw_to_mC(s, temp_idx);
+
+ return 0;
+}
+
+int get_temp_common(const struct tsens_sensor *s, int *temp)
+{
+ struct tsens_priv *priv = s->priv;
+ int hw_id = s->hw_id;
+ int last_temp = 0, ret;
+
+ ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
+ if (ret)
+ return ret;
+
+ *temp = code_to_degc(last_temp, s) * 1000;
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int dbg_sensors_show(struct seq_file *s, void *data)
+{
+ struct platform_device *pdev = s->private;
+ struct tsens_priv *priv = platform_get_drvdata(pdev);
+ int i;
+
+ seq_printf(s, "max: %2d\nnum: %2d\n\n",
+ priv->feat->max_sensors, priv->num_sensors);
+
+ seq_puts(s, " id slope offset\n--------------------------\n");
+ for (i = 0; i < priv->num_sensors; i++) {
+ seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id,
+ priv->sensor[i].slope, priv->sensor[i].offset);
+ }
+
+ return 0;
+}
+
+static int dbg_version_show(struct seq_file *s, void *data)
+{
+ struct platform_device *pdev = s->private;
+ struct tsens_priv *priv = platform_get_drvdata(pdev);
+ u32 maj_ver, min_ver, step_ver;
+ int ret;
+
+ if (tsens_version(priv) > VER_0_1) {
+ ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver);
+ if (ret)
+ return ret;
+ ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver);
+ if (ret)
+ return ret;
+ ret = regmap_field_read(priv->rf[VER_STEP], &step_ver);
+ if (ret)
+ return ret;
+ seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver);
+ } else {
+ seq_puts(s, "0.1.0\n");
+ }
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(dbg_version);
+DEFINE_SHOW_ATTRIBUTE(dbg_sensors);
+
+static void tsens_debug_init(struct platform_device *pdev)
+{
+ struct tsens_priv *priv = platform_get_drvdata(pdev);
+ struct dentry *root, *file;
+
+ root = debugfs_lookup("tsens", NULL);
+ if (!root)
+ priv->debug_root = debugfs_create_dir("tsens", NULL);
+ else
+ priv->debug_root = root;
+
+ file = debugfs_lookup("version", priv->debug_root);
+ if (!file)
+ debugfs_create_file("version", 0444, priv->debug_root,
+ pdev, &dbg_version_fops);
+
+ /* A directory for each instance of the TSENS IP */
+ priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root);
+ debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops);
+}
+#else
+static inline void tsens_debug_init(struct platform_device *pdev) {}
+#endif
+
+static const struct regmap_config tsens_config = {
+ .name = "tm",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static const struct regmap_config tsens_srot_config = {
+ .name = "srot",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+int __init init_common(struct tsens_priv *priv)
+{
+ void __iomem *tm_base, *srot_base;
+ struct device *dev = priv->dev;
+ u32 ver_minor;
+ struct resource *res;
+ u32 enabled;
+ int ret, i, j;
+ struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
+
+ if (!op)
+ return -EINVAL;
+
+ if (op->num_resources > 1) {
+ /* DT with separate SROT and TM address space */
+ priv->tm_offset = 0;
+ res = platform_get_resource(op, IORESOURCE_MEM, 1);
+ srot_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(srot_base)) {
+ ret = PTR_ERR(srot_base);
+ goto err_put_device;
+ }
+
+ priv->srot_map = devm_regmap_init_mmio(dev, srot_base,
+ &tsens_srot_config);
+ if (IS_ERR(priv->srot_map)) {
+ ret = PTR_ERR(priv->srot_map);
+ goto err_put_device;
+ }
+ } else {
+ /* old DTs where SROT and TM were in a contiguous 2K block */
+ priv->tm_offset = 0x1000;
+ }
+
+ res = platform_get_resource(op, IORESOURCE_MEM, 0);
+ tm_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(tm_base)) {
+ ret = PTR_ERR(tm_base);
+ goto err_put_device;
+ }
+
+ priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
+ if (IS_ERR(priv->tm_map)) {
+ ret = PTR_ERR(priv->tm_map);
+ goto err_put_device;
+ }
+
+ if (tsens_version(priv) > VER_0_1) {
+ for (i = VER_MAJOR; i <= VER_STEP; i++) {
+ priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map,
+ priv->fields[i]);
+ if (IS_ERR(priv->rf[i]))
+ return PTR_ERR(priv->rf[i]);
+ }
+ ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);
+ if (ret)
+ goto err_put_device;
+ }
+
+ priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
+ priv->fields[TSENS_EN]);
+ if (IS_ERR(priv->rf[TSENS_EN])) {
+ ret = PTR_ERR(priv->rf[TSENS_EN]);
+ goto err_put_device;
+ }
+ ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
+ if (ret)
+ goto err_put_device;
+ if (!enabled) {
+ dev_err(dev, "%s: device not enabled\n", __func__);
+ ret = -ENODEV;
+ goto err_put_device;
+ }
+
+ priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
+ priv->fields[SENSOR_EN]);
+ if (IS_ERR(priv->rf[SENSOR_EN])) {
+ ret = PTR_ERR(priv->rf[SENSOR_EN]);
+ goto err_put_device;
+ }
+ priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map,
+ priv->fields[INT_EN]);
+ if (IS_ERR(priv->rf[INT_EN])) {
+ ret = PTR_ERR(priv->rf[INT_EN]);
+ goto err_put_device;
+ }
+
+ /* This loop might need changes if enum regfield_ids is reordered */
+ for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
+ for (i = 0; i < priv->feat->max_sensors; i++) {
+ int idx = j + i;
+
+ priv->rf[idx] = devm_regmap_field_alloc(dev,
+ priv->tm_map,
+ priv->fields[idx]);
+ if (IS_ERR(priv->rf[idx])) {
+ ret = PTR_ERR(priv->rf[idx]);
+ goto err_put_device;
+ }
+ }
+ }
+
+ if (priv->feat->crit_int) {
+ /* Loop might need changes if enum regfield_ids is reordered */
+ for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
+ for (i = 0; i < priv->feat->max_sensors; i++) {
+ int idx = j + i;
+
+ priv->rf[idx] =
+ devm_regmap_field_alloc(dev,
+ priv->tm_map,
+ priv->fields[idx]);
+ if (IS_ERR(priv->rf[idx])) {
+ ret = PTR_ERR(priv->rf[idx]);
+ goto err_put_device;
+ }
+ }
+ }
+ }
+
+ if (tsens_version(priv) > VER_1_X && ver_minor > 2) {
+ /* Watchdog is present only on v2.3+ */
+ priv->feat->has_watchdog = 1;
+ for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
+ priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map,
+ priv->fields[i]);
+ if (IS_ERR(priv->rf[i])) {
+ ret = PTR_ERR(priv->rf[i]);
+ goto err_put_device;
+ }
+ }
+ /*
+ * Watchdog is already enabled, unmask the bark.
+ * Disable cycle completion monitoring
+ */
+ regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
+ regmap_field_write(priv->rf[CC_MON_MASK], 1);
+ }
+
+ spin_lock_init(&priv->ul_lock);
+ tsens_enable_irq(priv);
+ tsens_debug_init(op);
+
+err_put_device:
+ put_device(&op->dev);
+ return ret;
+}
+
static int tsens_get_temp(void *data, int *temp)
{
struct tsens_sensor *s = data;
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index 502acf0e6828..59d01162c66a 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -580,11 +580,6 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mo
int init_common(struct tsens_priv *priv);
int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
int get_temp_common(const struct tsens_sensor *s, int *temp);
-int tsens_enable_irq(struct tsens_priv *priv);
-void tsens_disable_irq(struct tsens_priv *priv);
-int tsens_set_trips(void *_sensor, int low, int high);
-irqreturn_t tsens_irq_thread(int irq, void *data);
-irqreturn_t tsens_critical_irq_thread(int irq, void *data);
/* TSENS target */
extern struct tsens_plat_data data_8960;
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
index 028a6bbf75dc..73049f9bea25 100644
--- a/drivers/thermal/qoriq_thermal.c
+++ b/drivers/thermal/qoriq_thermal.c
@@ -11,6 +11,7 @@
#include <linux/regmap.h>
#include <linux/sizes.h>
#include <linux/thermal.h>
+#include <linux/units.h>
#include "thermal_core.h"
#include "thermal_hwmon.h"
@@ -23,6 +24,7 @@
#define TMTMIR_DEFAULT 0x0000000f
#define TIER_DISABLE 0x0
#define TEUMR0_V2 0x51009c00
+#define TMSARA_V2 0xe
#define TMU_VER1 0x1
#define TMU_VER2 0x2
@@ -50,6 +52,9 @@
* Site Register
*/
#define TRITSR_V BIT(31)
+#define REGS_V2_TMSAR(n) (0x304 + 16 * (n)) /* TMU monitoring
+ * site adjustment register
+ */
#define REGS_TTRnCR(n) (0xf10 + 4 * (n)) /* Temperature Range n
* Control Register
*/
@@ -85,12 +90,21 @@ static int tmu_get_temp(void *p, int *temp)
/*
* REGS_TRITSR(id) has the following layout:
*
+ * For TMU Rev1:
* 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.
+ * temperature in Celsius.
+
+ * For TMU Rev2:
+ * 31 ... 8 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 9 bit value representing
+ * temperature in KelVin.
*/
if (regmap_read_poll_timeout(qdata->regmap,
REGS_TRITSR(qsensor->id),
@@ -100,7 +114,10 @@ static int tmu_get_temp(void *p, int *temp)
10 * USEC_PER_MSEC))
return -ENODATA;
- *temp = (val & 0xff) * 1000;
+ if (qdata->ver == TMU_VER1)
+ *temp = (val & GENMASK(7, 0)) * MILLIDEGREE_PER_DEGREE;
+ else
+ *temp = kelvin_to_millicelsius(val & GENMASK(8, 0));
return 0;
}
@@ -192,6 +209,8 @@ static int qoriq_tmu_calibration(struct device *dev,
static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
{
+ int i;
+
/* Disable interrupt, using polling instead */
regmap_write(data->regmap, REGS_TIER, TIER_DISABLE);
@@ -202,6 +221,8 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
} else {
regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT);
regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2);
+ for (i = 0; i < SITES_MAX; i++)
+ regmap_write(data->regmap, REGS_V2_TMSAR(i), TMSARA_V2);
}
/* Disable monitoring */
@@ -212,6 +233,7 @@ 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_V2_TMSAR(0), REGS_V2_TMSAR(15)),
regmap_reg_range(REGS_IPBRR(0), REGS_IPBRR(1)),
/* Read only registers below */
regmap_reg_range(REGS_TRITSR(0), REGS_TRITSR(15)),
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c
index e0c1f2409035..46aeb28b4e90 100644
--- a/drivers/thermal/rcar_thermal.c
+++ b/drivers/thermal/rcar_thermal.c
@@ -198,8 +198,8 @@ static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg,
static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
{
struct device *dev = rcar_priv_to_dev(priv);
- int i;
- u32 ctemp, old, new;
+ int old, new, ctemp = -EINVAL;
+ unsigned int i;
mutex_lock(&priv->lock);
@@ -209,7 +209,6 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
*/
rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL);
- ctemp = 0;
old = ~0;
for (i = 0; i < 128; i++) {
/*
@@ -227,7 +226,7 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
old = new;
}
- if (!ctemp) {
+ if (ctemp < 0) {
dev_err(dev, "thermal sensor was broken\n");
goto err_out_unlock;
}
@@ -248,7 +247,7 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
err_out_unlock:
mutex_unlock(&priv->lock);
- return ctemp ? ctemp : -EINVAL;
+ return ctemp;
}
static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv,
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index 7c1a8bccdcba..15a71ecc916c 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -1241,10 +1241,8 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
return -ENXIO;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (irq < 0)
return -EINVAL;
- }
thermal = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_thermal_data),
GFP_KERNEL);
diff --git a/drivers/thermal/st/st_thermal_memmap.c b/drivers/thermal/st/st_thermal_memmap.c
index a824b78dabf8..a0114452d11f 100644
--- a/drivers/thermal/st/st_thermal_memmap.c
+++ b/drivers/thermal/st/st_thermal_memmap.c
@@ -94,10 +94,8 @@ static int st_mmap_register_enable_irq(struct st_thermal_sensor *sensor)
int ret;
sensor->irq = platform_get_irq(pdev, 0);
- if (sensor->irq < 0) {
- dev_err(dev, "failed to register IRQ\n");
+ if (sensor->irq < 0)
return sensor->irq;
- }
ret = devm_request_threaded_irq(dev, sensor->irq,
NULL, st_mmap_thermal_trip_handler,
diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c
index 9314e3df6a42..331e2b768df5 100644
--- a/drivers/thermal/st/stm_thermal.c
+++ b/drivers/thermal/st/stm_thermal.c
@@ -385,10 +385,8 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor)
int ret;
sensor->irq = platform_get_irq(pdev, 0);
- if (sensor->irq < 0) {
- dev_err(dev, "%s: Unable to find IRQ\n", __func__);
+ if (sensor->irq < 0)
return sensor->irq;
- }
ret = devm_request_threaded_irq(dev, sensor->irq,
NULL,
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 9a321dc548c8..b71196eaf90e 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -9,9 +9,9 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/kdev_t.h>
#include <linux/idr.h>
@@ -27,10 +27,6 @@
#include "thermal_core.h"
#include "thermal_hwmon.h"
-MODULE_AUTHOR("Zhang Rui");
-MODULE_DESCRIPTION("Generic thermal management sysfs support");
-MODULE_LICENSE("GPL v2");
-
static DEFINE_IDA(thermal_tz_ida);
static DEFINE_IDA(thermal_cdev_ida);
@@ -447,12 +443,6 @@ static void update_temperature(struct thermal_zone_device *tz)
mutex_unlock(&tz->lock);
trace_thermal_temperature(tz);
- if (tz->last_temperature == THERMAL_TEMP_INVALID)
- dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d\n",
- tz->temperature);
- else
- dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n",
- tz->last_temperature, tz->temperature);
}
static void thermal_zone_device_init(struct thermal_zone_device *tz)
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index a9bf00e91d64..c95689586e19 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -12,6 +12,17 @@
#include <linux/device.h>
#include <linux/thermal.h>
+/* Default Thermal Governor */
+#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
+#define DEFAULT_THERMAL_GOVERNOR "step_wise"
+#elif defined(CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE)
+#define DEFAULT_THERMAL_GOVERNOR "fair_share"
+#elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
+#define DEFAULT_THERMAL_GOVERNOR "user_space"
+#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
+#define DEFAULT_THERMAL_GOVERNOR "power_allocator"
+#endif
+
/* Initial state of a cooling device during binding */
#define THERMAL_NO_TARGET -1UL
@@ -30,6 +41,44 @@ extern struct thermal_governor *__governor_thermal_table_end[];
__governor < __governor_thermal_table_end; \
__governor++)
+struct thermal_attr {
+ struct device_attribute attr;
+ char name[THERMAL_NAME_LENGTH];
+};
+
+static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
+{
+ return cdev->ops->get_requested_power && cdev->ops->state2power &&
+ cdev->ops->power2state;
+}
+
+int power_actor_get_max_power(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz, u32 *max_power);
+int power_actor_get_min_power(struct thermal_cooling_device *cdev,
+ struct thermal_zone_device *tz, u32 *min_power);
+int power_actor_set_power(struct thermal_cooling_device *cdev,
+ struct thermal_instance *ti, u32 power);
+/**
+ * struct thermal_trip - representation of a point in temperature domain
+ * @np: pointer to struct device_node that this trip point was created from
+ * @temperature: temperature value in miliCelsius
+ * @hysteresis: relative hysteresis in miliCelsius
+ * @type: trip point type
+ */
+struct thermal_trip {
+ struct device_node *np;
+ int temperature;
+ int hysteresis;
+ enum thermal_trip_type type;
+};
+
+int get_tz_trend(struct thermal_zone_device *tz, int trip);
+
+struct thermal_instance *
+get_thermal_instance(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev,
+ int trip);
+
/*
* This structure is used to describe the behavior of
* a certain cooling device on a certain trip point
@@ -69,6 +118,9 @@ void thermal_zone_device_unbind_exception(struct thermal_zone_device *,
int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
int thermal_build_list_of_policies(char *buf);
+/* Helpers */
+void thermal_zone_set_trips(struct thermal_zone_device *tz);
+
/* sysfs I/F */
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
void thermal_zone_destroy_device_groups(struct thermal_zone_device *);
diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c
index 2ba756af76b7..87b1256fa2f2 100644
--- a/drivers/thermal/thermal_helpers.c
+++ b/drivers/thermal/thermal_helpers.c
@@ -12,11 +12,12 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/sysfs.h>
#include <trace/events/thermal.h>
@@ -113,6 +114,18 @@ exit:
}
EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
+/**
+ * thermal_zone_set_trips - Computes the next trip points for the driver
+ * @tz: a pointer to a thermal zone device structure
+ *
+ * The function computes the next temperature boundaries by browsing
+ * the trip points. The result is the closer low and high trip points
+ * to the current temperature. These values are passed to the backend
+ * driver to let it set its own notification mechanism (usually an
+ * interrupt).
+ *
+ * It does not return a value
+ */
void thermal_zone_set_trips(struct thermal_zone_device *tz)
{
int low = -INT_MAX;
@@ -161,7 +174,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
exit:
mutex_unlock(&tz->lock);
}
-EXPORT_SYMBOL_GPL(thermal_zone_set_trips);
void thermal_cdev_update(struct thermal_cooling_device *cdev)
{
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index c8d2620f2e42..8b92e00ff236 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -10,10 +10,12 @@
* Copyright (C) 2013 Texas Instruments
* Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
*/
+#include <linux/err.h>
+#include <linux/export.h>
#include <linux/hwmon.h>
-#include <linux/thermal.h>
#include <linux/slab.h>
-#include <linux/err.h>
+#include <linux/thermal.h>
+
#include "thermal_hwmon.h"
/* hwmon sys I/F */
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/thermal_of.c
index 874a47d6923f..ddf88dbe7ba2 100644
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/thermal_of.c
@@ -8,13 +8,13 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/thermal.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
#include <linux/err.h>
#include <linux/export.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include <linux/types.h>
#include <linux/string.h>
#include "thermal_core.h"
diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
index 263b0420fbe4..ab19ceff6e2a 100644
--- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
@@ -772,10 +772,9 @@ static int ti_bandgap_talert_init(struct ti_bandgap *bgp,
int ret;
bgp->irq = platform_get_irq(pdev, 0);
- if (bgp->irq < 0) {
- dev_err(&pdev->dev, "get_irq failed\n");
+ if (bgp->irq < 0)
return bgp->irq;
- }
+
ret = request_threaded_irq(bgp->irq, NULL,
ti_bandgap_talert_irq_handler,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
index d3e959d01606..85776db4bf34 100644
--- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
+++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
@@ -169,7 +169,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
data = ti_bandgap_get_sensor_data(bgp, id);
- if (!data || IS_ERR(data))
+ if (!IS_ERR_OR_NULL(data))
data = ti_thermal_build_data(bgp, id);
if (!data)
@@ -196,7 +196,7 @@ int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
data = ti_bandgap_get_sensor_data(bgp, id);
- if (data && data->ti_thermal) {
+ if (!IS_ERR_OR_NULL(data) && data->ti_thermal) {
if (data->our_zone)
thermal_zone_device_unregister(data->ti_thermal);
}
@@ -262,7 +262,7 @@ int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
data = ti_bandgap_get_sensor_data(bgp, id);
- if (data) {
+ if (!IS_ERR_OR_NULL(data)) {
cpufreq_cooling_unregister(data->cool_dev);
if (data->policy)
cpufreq_cpu_put(data->policy);
diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
index 65501d8f9778..a3bdc8a98f2c 100644
--- a/include/linux/cpu_cooling.h
+++ b/include/linux/cpu_cooling.h
@@ -63,18 +63,10 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
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);
+void cpuidle_cooling_register(struct cpuidle_driver *drv);
#else /* CONFIG_CPU_IDLE_THERMAL */
-static inline int cpuidle_cooling_register(struct cpuidle_driver *drv)
+static inline void 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 */
diff --git a/include/linux/idle_inject.h b/include/linux/idle_inject.h
index a445cd1a36c5..91a8612b8bf9 100644
--- a/include/linux/idle_inject.h
+++ b/include/linux/idle_inject.h
@@ -26,4 +26,8 @@ void idle_inject_set_duration(struct idle_inject_device *ii_dev,
void idle_inject_get_duration(struct idle_inject_device *ii_dev,
unsigned int *run_duration_us,
unsigned int *idle_duration_us);
+
+void idle_inject_set_latency(struct idle_inject_device *ii_dev,
+ unsigned int latency_ns);
+
#endif /* __IDLE_INJECT_H__ */
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index c91b1e344d56..216185bb3014 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -32,20 +32,10 @@
/* use value, which < 0K, to indicate an invalid/uninitialized temperature */
#define THERMAL_TEMP_INVALID -274000
-/* Default Thermal Governor */
-#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
-#define DEFAULT_THERMAL_GOVERNOR "step_wise"
-#elif defined(CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE)
-#define DEFAULT_THERMAL_GOVERNOR "fair_share"
-#elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
-#define DEFAULT_THERMAL_GOVERNOR "user_space"
-#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
-#define DEFAULT_THERMAL_GOVERNOR "power_allocator"
-#endif
-
struct thermal_zone_device;
struct thermal_cooling_device;
struct thermal_instance;
+struct thermal_attr;
enum thermal_device_mode {
THERMAL_DEVICE_DISABLED = 0,
@@ -130,11 +120,6 @@ struct thermal_cooling_device {
struct list_head node;
};
-struct thermal_attr {
- struct device_attribute attr;
- char name[THERMAL_NAME_LENGTH];
-};
-
/**
* struct thermal_zone_device - structure for a thermal zone
* @id: unique id number for each thermal zone
@@ -347,21 +332,6 @@ struct thermal_zone_of_device_ops {
int (*set_trip_temp)(void *, int, int);
};
-/**
- * struct thermal_trip - representation of a point in temperature domain
- * @np: pointer to struct device_node that this trip point was created from
- * @temperature: temperature value in miliCelsius
- * @hysteresis: relative hysteresis in miliCelsius
- * @type: trip point type
- */
-
-struct thermal_trip {
- struct device_node *np;
- int temperature;
- int hysteresis;
- enum thermal_trip_type type;
-};
-
/* Function declarations */
#ifdef CONFIG_THERMAL_OF
int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
@@ -413,19 +383,7 @@ void devm_thermal_zone_of_sensor_unregister(struct device *dev,
#endif
-#if IS_ENABLED(CONFIG_THERMAL)
-static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
-{
- return cdev->ops->get_requested_power && cdev->ops->state2power &&
- cdev->ops->power2state;
-}
-
-int power_actor_get_max_power(struct thermal_cooling_device *,
- struct thermal_zone_device *tz, u32 *max_power);
-int power_actor_get_min_power(struct thermal_cooling_device *,
- struct thermal_zone_device *tz, u32 *min_power);
-int power_actor_set_power(struct thermal_cooling_device *,
- struct thermal_instance *, u32);
+#ifdef CONFIG_THERMAL
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
void *, struct thermal_zone_device_ops *,
struct thermal_zone_params *, int, int);
@@ -439,7 +397,6 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
struct thermal_cooling_device *);
void thermal_zone_device_update(struct thermal_zone_device *,
enum thermal_notify_event);
-void thermal_zone_set_trips(struct thermal_zone_device *);
struct thermal_cooling_device *thermal_cooling_device_register(const char *,
void *, const struct thermal_cooling_device_ops *);
@@ -457,24 +414,9 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
int thermal_zone_get_slope(struct thermal_zone_device *tz);
int thermal_zone_get_offset(struct thermal_zone_device *tz);
-int get_tz_trend(struct thermal_zone_device *, int);
-struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
- struct thermal_cooling_device *, int);
void thermal_cdev_update(struct thermal_cooling_device *);
void thermal_notify_framework(struct thermal_zone_device *, int);
#else
-static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
-{ return false; }
-static inline int power_actor_get_max_power(struct thermal_cooling_device *cdev,
- struct thermal_zone_device *tz, u32 *max_power)
-{ return 0; }
-static inline int power_actor_get_min_power(struct thermal_cooling_device *cdev,
- struct thermal_zone_device *tz,
- u32 *min_power)
-{ return -ENODEV; }
-static inline int power_actor_set_power(struct thermal_cooling_device *cdev,
- struct thermal_instance *tz, u32 power)
-{ return 0; }
static inline struct thermal_zone_device *thermal_zone_device_register(
const char *type, int trips, int mask, void *devdata,
struct thermal_zone_device_ops *ops,
@@ -484,21 +426,6 @@ static inline struct thermal_zone_device *thermal_zone_device_register(
static inline void thermal_zone_device_unregister(
struct thermal_zone_device *tz)
{ }
-static inline int thermal_zone_bind_cooling_device(
- struct thermal_zone_device *tz, int trip,
- struct thermal_cooling_device *cdev,
- unsigned long upper, unsigned long lower,
- unsigned int weight)
-{ return -ENODEV; }
-static inline int thermal_zone_unbind_cooling_device(
- struct thermal_zone_device *tz, int trip,
- struct thermal_cooling_device *cdev)
-{ return -ENODEV; }
-static inline void thermal_zone_device_update(struct thermal_zone_device *tz,
- enum thermal_notify_event event)
-{ }
-static inline void thermal_zone_set_trips(struct thermal_zone_device *tz)
-{ }
static inline struct thermal_cooling_device *
thermal_cooling_device_register(char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
@@ -530,12 +457,7 @@ static inline int thermal_zone_get_slope(
static inline int thermal_zone_get_offset(
struct thermal_zone_device *tz)
{ return -ENODEV; }
-static inline int get_tz_trend(struct thermal_zone_device *tz, int trip)
-{ return -ENODEV; }
-static inline struct thermal_instance *
-get_thermal_instance(struct thermal_zone_device *tz,
- struct thermal_cooling_device *cdev, int trip)
-{ return ERR_PTR(-ENODEV); }
+
static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
{ }
static inline void thermal_notify_framework(struct thermal_zone_device *tz,