Merge tag 'thermal-v5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 12 Jun 2020 21:10:21 +0000 (14:10 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 12 Jun 2020 21:10:21 +0000 (14:10 -0700)
Pull thermal updates from Daniel Lezcano:

 - Add the hwmon support on the i.MX SC (Anson Huang)

 - Thermal framework cleanups (self-encapsulation, pointless stubs,
   private structures) (Daniel Lezcano)

 - Use the PM QoS frequency changes for the devfreq cooling device
   (Matthias Kaehlcke)

 - Remove duplicate error messages from platform_get_irq() error
   handling (Markus Elfring)

 - Add support for the bandgap sensors (Keerthy)

 - Statically initialize .get_mode/.set_mode ops (Andrzej Pietrasiewicz)

 - Add Renesas R-Car maintainer entry (Niklas Söderlund)

 - Fix error checking after calling ti_bandgap_get_sensor_data() for the
   TI SoC thermal (Sudip Mukherjee)

 - Add latency constraint for the idle injection, the DT binding and the
   change the registering function (Daniel Lezcano)

 - Convert the thermal framework binding to the Yaml schema (Amit
   Kucheria)

 - Replace zero-length array with flexible-array on i.MX 8MM (Gustavo A.
   R. Silva)

 - Thermal framework cleanups (alphabetic order for heads, replace
   module.h by export.h, make file naming consistent) (Amit Kucheria)

 - Merge tsens-common into the tsens driver (Amit Kucheria)

 - Fix platform dependency for the Qoriq driver (Geert Uytterhoeven)

 - Clean up the rcar_thermal_update_temp() function in the rcar thermal
   driver (Niklas Söderlund)

 - Fix the TMSAR register for the TMUv2 on the Qoriq platform (Yuantian
   Tang)

 - Export GDDV, OEM vendor variables, and don't require IDSP for the
   int340x thermal driver - trivial conflicts fixed (Matthew Garrett)

* tag 'thermal-v5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (48 commits)
  thermal/int340x_thermal: Don't require IDSP to exist
  thermal/int340x_thermal: Export OEM vendor variables
  thermal/int340x_thermal: Export GDDV
  thermal: qoriq: Update the settings for TMUv2
  thermal: rcar_thermal: Clean up rcar_thermal_update_temp()
  thermal: qoriq: Add platform dependencies
  drivers: thermal: tsens: Merge tsens-common.c into tsens.c
  thermal/of: Rename of-thermal.c
  thermal/governors: Prefix all source files with gov_
  thermal/drivers/user_space: Sort headers alphabetically
  thermal/drivers/of-thermal: Sort headers alphabetically
  thermal/drivers/cpufreq_cooling: Replace module.h with export.h
  thermal/drivers/cpufreq_cooling: Sort headers alphabetically
  thermal/drivers/clock_cooling: Include export.h
  thermal/drivers/clock_cooling: Sort headers alphabetically
  thermal/drivers/thermal_hwmon: Include export.h
  thermal/drivers/thermal_hwmon: Sort headers alphabetically
  thermal/drivers/thermal_helpers: Include export.h
  thermal/drivers/thermal_helpers: Sort headers alphabetically
  thermal/core: Replace module.h with export.h
  ...

47 files changed:
Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/thermal-idle.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/thermal-sensor.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/thermal-zones.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/ti,am654-thermal.yaml [new file with mode: 0644]
MAINTAINERS
drivers/cpuidle/cpuidle-arm.c
drivers/cpuidle/cpuidle-psci.c
drivers/powercap/idle_inject.c
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/clock_cooling.c
drivers/thermal/cpufreq_cooling.c
drivers/thermal/cpuidle_cooling.c
drivers/thermal/devfreq_cooling.c
drivers/thermal/fair_share.c [deleted file]
drivers/thermal/gov_fair_share.c [new file with mode: 0644]
drivers/thermal/gov_power_allocator.c [new file with mode: 0644]
drivers/thermal/gov_step_wise.c [new file with mode: 0644]
drivers/thermal/gov_user_space.c [new file with mode: 0644]
drivers/thermal/imx8mm_thermal.c
drivers/thermal/imx_sc_thermal.c
drivers/thermal/intel/int340x_thermal/int3400_thermal.c
drivers/thermal/k3_bandgap.c [new file with mode: 0644]
drivers/thermal/of-thermal.c [deleted file]
drivers/thermal/power_allocator.c [deleted file]
drivers/thermal/qcom/Makefile
drivers/thermal/qcom/tsens-common.c [deleted file]
drivers/thermal/qcom/tsens.c
drivers/thermal/qcom/tsens.h
drivers/thermal/qoriq_thermal.c
drivers/thermal/rcar_thermal.c
drivers/thermal/rockchip_thermal.c
drivers/thermal/st/st_thermal_memmap.c
drivers/thermal/st/stm_thermal.c
drivers/thermal/step_wise.c [deleted file]
drivers/thermal/thermal_core.c
drivers/thermal/thermal_core.h
drivers/thermal/thermal_helpers.c
drivers/thermal/thermal_hwmon.c
drivers/thermal/thermal_of.c [new file with mode: 0644]
drivers/thermal/ti-soc-thermal/ti-bandgap.c
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
drivers/thermal/user_space.c [deleted file]
include/linux/cpu_cooling.h
include/linux/idle_inject.h
include/linux/thermal.h

diff --git a/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml b/Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml
new file mode 100644 (file)
index 0000000..5145883
--- /dev/null
@@ -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 (file)
index 0000000..7a922f5
--- /dev/null
@@ -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 (file)
index 0000000..fcd25a0
--- /dev/null
@@ -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 (file)
index 0000000..b8515d3
--- /dev/null
@@ -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 (file)
index 0000000..25b9209
--- /dev/null
@@ -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";
+                };
+        };
+    };
+...
index b5cbbc2..56d7d27 100644 (file)
@@ -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
index 9e5156d..8c75892 100644 (file)
@@ -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:
index d0fb585..3806f91 100644 (file)
@@ -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:
index e9bbd3c..c90f099 100644 (file)
@@ -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);
 }
 
 /**
@@ -169,6 +172,16 @@ void idle_inject_get_duration(struct idle_inject_device *ii_dev,
        *idle_duration_us = READ_ONCE(ii_dev->idle_duration_us);
 }
 
+/**
+ * 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)) {
 
index 91af271..3eb2348 100644 (file)
@@ -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.
index 8c8ed7b..0c8b84a 100644 (file)
@@ -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
index 7cb3ae4..56cb1f4 100644 (file)
  *  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
index e297e13..9e12402 100644 (file)
  *             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>
 
index 0bb8432..78e3e82 100644 (file)
@@ -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;
+               }
+       }
 }
index a87d4fa..f7f32e9 100644 (file)
 #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/fair_share.c
deleted file mode 100644 (file)
index aaa0718..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *  fair_share.c - A simple weight based Thermal governor
- *
- *  Copyright (C) 2012 Intel Corp
- *  Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
- *
- *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/thermal.h>
-#include <trace/events/thermal.h>
-
-#include "thermal_core.h"
-
-/**
- * get_trip_level: - obtains the current trip level for a zone
- * @tz:                thermal zone device
- */
-static int get_trip_level(struct thermal_zone_device *tz)
-{
-       int count = 0;
-       int trip_temp;
-       enum thermal_trip_type trip_type;
-
-       if (tz->trips == 0 || !tz->ops->get_trip_temp)
-               return 0;
-
-       for (count = 0; count < tz->trips; count++) {
-               tz->ops->get_trip_temp(tz, count, &trip_temp);
-               if (tz->temperature < trip_temp)
-                       break;
-       }
-
-       /*
-        * count > 0 only if temperature is greater than first trip
-        * point, in which case, trip_point = count - 1
-        */
-       if (count > 0) {
-               tz->ops->get_trip_type(tz, count - 1, &trip_type);
-               trace_thermal_zone_trip(tz, count - 1, trip_type);
-       }
-
-       return count;
-}
-
-static long get_target_state(struct thermal_zone_device *tz,
-               struct thermal_cooling_device *cdev, int percentage, int level)
-{
-       unsigned long max_state;
-
-       cdev->ops->get_max_state(cdev, &max_state);
-
-       return (long)(percentage * level * max_state) / (100 * tz->trips);
-}
-
-/**
- * fair_share_throttle - throttles devices associated with the given zone
- * @tz: thermal_zone_device
- * @trip: trip point index
- *
- * Throttling Logic: This uses three parameters to calculate the new
- * throttle state of the cooling devices associated with the given zone.
- *
- * Parameters used for Throttling:
- * P1. max_state: Maximum throttle state exposed by the cooling device.
- * P2. percentage[i]/100:
- *     How 'effective' the 'i'th device is, in cooling the given zone.
- * P3. cur_trip_level/max_no_of_trips:
- *     This describes the extent to which the devices should be throttled.
- *     We do not want to throttle too much when we trip a lower temperature,
- *     whereas the throttling is at full swing if we trip critical levels.
- *     (Heavily assumes the trip points are in ascending order)
- * new_state of cooling device = P3 * P2 * P1
- */
-static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
-{
-       struct thermal_instance *instance;
-       int total_weight = 0;
-       int total_instance = 0;
-       int cur_trip_level = get_trip_level(tz);
-
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               if (instance->trip != trip)
-                       continue;
-
-               total_weight += instance->weight;
-               total_instance++;
-       }
-
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               int percentage;
-               struct thermal_cooling_device *cdev = instance->cdev;
-
-               if (instance->trip != trip)
-                       continue;
-
-               if (!total_weight)
-                       percentage = 100 / total_instance;
-               else
-                       percentage = (instance->weight * 100) / total_weight;
-
-               instance->target = get_target_state(tz, cdev, percentage,
-                                                   cur_trip_level);
-
-               mutex_lock(&instance->cdev->lock);
-               instance->cdev->updated = false;
-               mutex_unlock(&instance->cdev->lock);
-               thermal_cdev_update(cdev);
-       }
-       return 0;
-}
-
-static struct thermal_governor thermal_gov_fair_share = {
-       .name           = "fair_share",
-       .throttle       = fair_share_throttle,
-};
-THERMAL_GOVERNOR_DECLARE(thermal_gov_fair_share);
diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c
new file mode 100644 (file)
index 0000000..aaa0718
--- /dev/null
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  fair_share.c - A simple weight based Thermal governor
+ *
+ *  Copyright (C) 2012 Intel Corp
+ *  Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/thermal.h>
+#include <trace/events/thermal.h>
+
+#include "thermal_core.h"
+
+/**
+ * get_trip_level: - obtains the current trip level for a zone
+ * @tz:                thermal zone device
+ */
+static int get_trip_level(struct thermal_zone_device *tz)
+{
+       int count = 0;
+       int trip_temp;
+       enum thermal_trip_type trip_type;
+
+       if (tz->trips == 0 || !tz->ops->get_trip_temp)
+               return 0;
+
+       for (count = 0; count < tz->trips; count++) {
+               tz->ops->get_trip_temp(tz, count, &trip_temp);
+               if (tz->temperature < trip_temp)
+                       break;
+       }
+
+       /*
+        * count > 0 only if temperature is greater than first trip
+        * point, in which case, trip_point = count - 1
+        */
+       if (count > 0) {
+               tz->ops->get_trip_type(tz, count - 1, &trip_type);
+               trace_thermal_zone_trip(tz, count - 1, trip_type);
+       }
+
+       return count;
+}
+
+static long get_target_state(struct thermal_zone_device *tz,
+               struct thermal_cooling_device *cdev, int percentage, int level)
+{
+       unsigned long max_state;
+
+       cdev->ops->get_max_state(cdev, &max_state);
+
+       return (long)(percentage * level * max_state) / (100 * tz->trips);
+}
+
+/**
+ * fair_share_throttle - throttles devices associated with the given zone
+ * @tz: thermal_zone_device
+ * @trip: trip point index
+ *
+ * Throttling Logic: This uses three parameters to calculate the new
+ * throttle state of the cooling devices associated with the given zone.
+ *
+ * Parameters used for Throttling:
+ * P1. max_state: Maximum throttle state exposed by the cooling device.
+ * P2. percentage[i]/100:
+ *     How 'effective' the 'i'th device is, in cooling the given zone.
+ * P3. cur_trip_level/max_no_of_trips:
+ *     This describes the extent to which the devices should be throttled.
+ *     We do not want to throttle too much when we trip a lower temperature,
+ *     whereas the throttling is at full swing if we trip critical levels.
+ *     (Heavily assumes the trip points are in ascending order)
+ * new_state of cooling device = P3 * P2 * P1
+ */
+static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
+{
+       struct thermal_instance *instance;
+       int total_weight = 0;
+       int total_instance = 0;
+       int cur_trip_level = get_trip_level(tz);
+
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               if (instance->trip != trip)
+                       continue;
+
+               total_weight += instance->weight;
+               total_instance++;
+       }
+
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               int percentage;
+               struct thermal_cooling_device *cdev = instance->cdev;
+
+               if (instance->trip != trip)
+                       continue;
+
+               if (!total_weight)
+                       percentage = 100 / total_instance;
+               else
+                       percentage = (instance->weight * 100) / total_weight;
+
+               instance->target = get_target_state(tz, cdev, percentage,
+                                                   cur_trip_level);
+
+               mutex_lock(&instance->cdev->lock);
+               instance->cdev->updated = false;
+               mutex_unlock(&instance->cdev->lock);
+               thermal_cdev_update(cdev);
+       }
+       return 0;
+}
+
+static struct thermal_governor thermal_gov_fair_share = {
+       .name           = "fair_share",
+       .throttle       = fair_share_throttle,
+};
+THERMAL_GOVERNOR_DECLARE(thermal_gov_fair_share);
diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c
new file mode 100644 (file)
index 0000000..4463647
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * A power allocator to manage temperature
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "Power allocator: " fmt
+
+#include <linux/rculist.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/thermal_power_allocator.h>
+
+#include "thermal_core.h"
+
+#define INVALID_TRIP -1
+
+#define FRAC_BITS 10
+#define int_to_frac(x) ((x) << FRAC_BITS)
+#define frac_to_int(x) ((x) >> FRAC_BITS)
+
+/**
+ * mul_frac() - multiply two fixed-point numbers
+ * @x: first multiplicand
+ * @y: second multiplicand
+ *
+ * Return: the result of multiplying two fixed-point numbers.  The
+ * result is also a fixed-point number.
+ */
+static inline s64 mul_frac(s64 x, s64 y)
+{
+       return (x * y) >> FRAC_BITS;
+}
+
+/**
+ * div_frac() - divide two fixed-point numbers
+ * @x: the dividend
+ * @y: the divisor
+ *
+ * Return: the result of dividing two fixed-point numbers.  The
+ * result is also a fixed-point number.
+ */
+static inline s64 div_frac(s64 x, s64 y)
+{
+       return div_s64(x << FRAC_BITS, y);
+}
+
+/**
+ * struct power_allocator_params - parameters for the power allocator governor
+ * @allocated_tzp:     whether we have allocated tzp for this thermal zone and
+ *                     it needs to be freed on unbind
+ * @err_integral:      accumulated error in the PID controller.
+ * @prev_err:  error in the previous iteration of the PID controller.
+ *             Used to calculate the derivative term.
+ * @trip_switch_on:    first passive trip point of the thermal zone.  The
+ *                     governor switches on when this trip point is crossed.
+ *                     If the thermal zone only has one passive trip point,
+ *                     @trip_switch_on should be INVALID_TRIP.
+ * @trip_max_desired_temperature:      last passive trip point of the thermal
+ *                                     zone.  The temperature we are
+ *                                     controlling for.
+ */
+struct power_allocator_params {
+       bool allocated_tzp;
+       s64 err_integral;
+       s32 prev_err;
+       int trip_switch_on;
+       int trip_max_desired_temperature;
+};
+
+/**
+ * estimate_sustainable_power() - Estimate the sustainable power of a thermal zone
+ * @tz: thermal zone we are operating in
+ *
+ * For thermal zones that don't provide a sustainable_power in their
+ * thermal_zone_params, estimate one.  Calculate it using the minimum
+ * power of all the cooling devices as that gives a valid value that
+ * can give some degree of functionality.  For optimal performance of
+ * this governor, provide a sustainable_power in the thermal zone's
+ * thermal_zone_params.
+ */
+static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
+{
+       u32 sustainable_power = 0;
+       struct thermal_instance *instance;
+       struct power_allocator_params *params = tz->governor_data;
+
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               struct thermal_cooling_device *cdev = instance->cdev;
+               u32 min_power;
+
+               if (instance->trip != params->trip_max_desired_temperature)
+                       continue;
+
+               if (power_actor_get_min_power(cdev, tz, &min_power))
+                       continue;
+
+               sustainable_power += min_power;
+       }
+
+       return sustainable_power;
+}
+
+/**
+ * estimate_pid_constants() - Estimate the constants for the PID controller
+ * @tz:                thermal zone for which to estimate the constants
+ * @sustainable_power: sustainable power for the thermal zone
+ * @trip_switch_on:    trip point number for the switch on temperature
+ * @control_temp:      target temperature for the power allocator governor
+ * @force:     whether to force the update of the constants
+ *
+ * This function is used to update the estimation of the PID
+ * controller constants in struct thermal_zone_parameters.
+ * Sustainable power is provided in case it was estimated.  The
+ * estimated sustainable_power should not be stored in the
+ * thermal_zone_parameters so it has to be passed explicitly to this
+ * function.
+ *
+ * If @force is not set, the values in the thermal zone's parameters
+ * are preserved if they are not zero.  If @force is set, the values
+ * in thermal zone's parameters are overwritten.
+ */
+static void estimate_pid_constants(struct thermal_zone_device *tz,
+                                  u32 sustainable_power, int trip_switch_on,
+                                  int control_temp, bool force)
+{
+       int ret;
+       int switch_on_temp;
+       u32 temperature_threshold;
+
+       ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp);
+       if (ret)
+               switch_on_temp = 0;
+
+       temperature_threshold = control_temp - switch_on_temp;
+       /*
+        * estimate_pid_constants() tries to find appropriate default
+        * values for thermal zones that don't provide them. If a
+        * system integrator has configured a thermal zone with two
+        * passive trip points at the same temperature, that person
+        * hasn't put any effort to set up the thermal zone properly
+        * so just give up.
+        */
+       if (!temperature_threshold)
+               return;
+
+       if (!tz->tzp->k_po || force)
+               tz->tzp->k_po = int_to_frac(sustainable_power) /
+                       temperature_threshold;
+
+       if (!tz->tzp->k_pu || force)
+               tz->tzp->k_pu = int_to_frac(2 * sustainable_power) /
+                       temperature_threshold;
+
+       if (!tz->tzp->k_i || force)
+               tz->tzp->k_i = int_to_frac(10) / 1000;
+       /*
+        * The default for k_d and integral_cutoff is 0, so we can
+        * leave them as they are.
+        */
+}
+
+/**
+ * pid_controller() - PID controller
+ * @tz:        thermal zone we are operating in
+ * @control_temp:      the target temperature in millicelsius
+ * @max_allocatable_power:     maximum allocatable power for this thermal zone
+ *
+ * This PID controller increases the available power budget so that the
+ * temperature of the thermal zone gets as close as possible to
+ * @control_temp and limits the power if it exceeds it.  k_po is the
+ * proportional term when we are overshooting, k_pu is the
+ * proportional term when we are undershooting.  integral_cutoff is a
+ * threshold below which we stop accumulating the error.  The
+ * accumulated error is only valid if the requested power will make
+ * the system warmer.  If the system is mostly idle, there's no point
+ * in accumulating positive error.
+ *
+ * Return: The power budget for the next period.
+ */
+static u32 pid_controller(struct thermal_zone_device *tz,
+                         int control_temp,
+                         u32 max_allocatable_power)
+{
+       s64 p, i, d, power_range;
+       s32 err, max_power_frac;
+       u32 sustainable_power;
+       struct power_allocator_params *params = tz->governor_data;
+
+       max_power_frac = int_to_frac(max_allocatable_power);
+
+       if (tz->tzp->sustainable_power) {
+               sustainable_power = tz->tzp->sustainable_power;
+       } else {
+               sustainable_power = estimate_sustainable_power(tz);
+               estimate_pid_constants(tz, sustainable_power,
+                                      params->trip_switch_on, control_temp,
+                                      true);
+       }
+
+       err = control_temp - tz->temperature;
+       err = int_to_frac(err);
+
+       /* Calculate the proportional term */
+       p = mul_frac(err < 0 ? tz->tzp->k_po : tz->tzp->k_pu, err);
+
+       /*
+        * Calculate the integral term
+        *
+        * if the error is less than cut off allow integration (but
+        * the integral is limited to max power)
+        */
+       i = mul_frac(tz->tzp->k_i, params->err_integral);
+
+       if (err < int_to_frac(tz->tzp->integral_cutoff)) {
+               s64 i_next = i + mul_frac(tz->tzp->k_i, err);
+
+               if (abs(i_next) < max_power_frac) {
+                       i = i_next;
+                       params->err_integral += err;
+               }
+       }
+
+       /*
+        * Calculate the derivative term
+        *
+        * We do err - prev_err, so with a positive k_d, a decreasing
+        * error (i.e. driving closer to the line) results in less
+        * power being applied, slowing down the controller)
+        */
+       d = mul_frac(tz->tzp->k_d, err - params->prev_err);
+       d = div_frac(d, tz->passive_delay);
+       params->prev_err = err;
+
+       power_range = p + i + d;
+
+       /* feed-forward the known sustainable dissipatable power */
+       power_range = sustainable_power + frac_to_int(power_range);
+
+       power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power);
+
+       trace_thermal_power_allocator_pid(tz, frac_to_int(err),
+                                         frac_to_int(params->err_integral),
+                                         frac_to_int(p), frac_to_int(i),
+                                         frac_to_int(d), power_range);
+
+       return power_range;
+}
+
+/**
+ * divvy_up_power() - divvy the allocated power between the actors
+ * @req_power: each actor's requested power
+ * @max_power: each actor's maximum available power
+ * @num_actors:        size of the @req_power, @max_power and @granted_power's array
+ * @total_req_power: sum of @req_power
+ * @power_range:       total allocated power
+ * @granted_power:     output array: each actor's granted power
+ * @extra_actor_power: an appropriately sized array to be used in the
+ *                     function as temporary storage of the extra power given
+ *                     to the actors
+ *
+ * This function divides the total allocated power (@power_range)
+ * fairly between the actors.  It first tries to give each actor a
+ * share of the @power_range according to how much power it requested
+ * compared to the rest of the actors.  For example, if only one actor
+ * requests power, then it receives all the @power_range.  If
+ * three actors each requests 1mW, each receives a third of the
+ * @power_range.
+ *
+ * If any actor received more than their maximum power, then that
+ * surplus is re-divvied among the actors based on how far they are
+ * from their respective maximums.
+ *
+ * Granted power for each actor is written to @granted_power, which
+ * should've been allocated by the calling function.
+ */
+static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
+                          u32 total_req_power, u32 power_range,
+                          u32 *granted_power, u32 *extra_actor_power)
+{
+       u32 extra_power, capped_extra_power;
+       int i;
+
+       /*
+        * Prevent division by 0 if none of the actors request power.
+        */
+       if (!total_req_power)
+               total_req_power = 1;
+
+       capped_extra_power = 0;
+       extra_power = 0;
+       for (i = 0; i < num_actors; i++) {
+               u64 req_range = (u64)req_power[i] * power_range;
+
+               granted_power[i] = DIV_ROUND_CLOSEST_ULL(req_range,
+                                                        total_req_power);
+
+               if (granted_power[i] > max_power[i]) {
+                       extra_power += granted_power[i] - max_power[i];
+                       granted_power[i] = max_power[i];
+               }
+
+               extra_actor_power[i] = max_power[i] - granted_power[i];
+               capped_extra_power += extra_actor_power[i];
+       }
+
+       if (!extra_power)
+               return;
+
+       /*
+        * Re-divvy the reclaimed extra among actors based on
+        * how far they are from the max
+        */
+       extra_power = min(extra_power, capped_extra_power);
+       if (capped_extra_power > 0)
+               for (i = 0; i < num_actors; i++)
+                       granted_power[i] += (extra_actor_power[i] *
+                                       extra_power) / capped_extra_power;
+}
+
+static int allocate_power(struct thermal_zone_device *tz,
+                         int control_temp)
+{
+       struct thermal_instance *instance;
+       struct power_allocator_params *params = tz->governor_data;
+       u32 *req_power, *max_power, *granted_power, *extra_actor_power;
+       u32 *weighted_req_power;
+       u32 total_req_power, max_allocatable_power, total_weighted_req_power;
+       u32 total_granted_power, power_range;
+       int i, num_actors, total_weight, ret = 0;
+       int trip_max_desired_temperature = params->trip_max_desired_temperature;
+
+       mutex_lock(&tz->lock);
+
+       num_actors = 0;
+       total_weight = 0;
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               if ((instance->trip == trip_max_desired_temperature) &&
+                   cdev_is_power_actor(instance->cdev)) {
+                       num_actors++;
+                       total_weight += instance->weight;
+               }
+       }
+
+       if (!num_actors) {
+               ret = -ENODEV;
+               goto unlock;
+       }
+
+       /*
+        * We need to allocate five arrays of the same size:
+        * req_power, max_power, granted_power, extra_actor_power and
+        * weighted_req_power.  They are going to be needed until this
+        * function returns.  Allocate them all in one go to simplify
+        * the allocation and deallocation logic.
+        */
+       BUILD_BUG_ON(sizeof(*req_power) != sizeof(*max_power));
+       BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power));
+       BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power));
+       BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power));
+       req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL);
+       if (!req_power) {
+               ret = -ENOMEM;
+               goto unlock;
+       }
+
+       max_power = &req_power[num_actors];
+       granted_power = &req_power[2 * num_actors];
+       extra_actor_power = &req_power[3 * num_actors];
+       weighted_req_power = &req_power[4 * num_actors];
+
+       i = 0;
+       total_weighted_req_power = 0;
+       total_req_power = 0;
+       max_allocatable_power = 0;
+
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               int weight;
+               struct thermal_cooling_device *cdev = instance->cdev;
+
+               if (instance->trip != trip_max_desired_temperature)
+                       continue;
+
+               if (!cdev_is_power_actor(cdev))
+                       continue;
+
+               if (cdev->ops->get_requested_power(cdev, tz, &req_power[i]))
+                       continue;
+
+               if (!total_weight)
+                       weight = 1 << FRAC_BITS;
+               else
+                       weight = instance->weight;
+
+               weighted_req_power[i] = frac_to_int(weight * req_power[i]);
+
+               if (power_actor_get_max_power(cdev, tz, &max_power[i]))
+                       continue;
+
+               total_req_power += req_power[i];
+               max_allocatable_power += max_power[i];
+               total_weighted_req_power += weighted_req_power[i];
+
+               i++;
+       }
+
+       power_range = pid_controller(tz, control_temp, max_allocatable_power);
+
+       divvy_up_power(weighted_req_power, max_power, num_actors,
+                      total_weighted_req_power, power_range, granted_power,
+                      extra_actor_power);
+
+       total_granted_power = 0;
+       i = 0;
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               if (instance->trip != trip_max_desired_temperature)
+                       continue;
+
+               if (!cdev_is_power_actor(instance->cdev))
+                       continue;
+
+               power_actor_set_power(instance->cdev, instance,
+                                     granted_power[i]);
+               total_granted_power += granted_power[i];
+
+               i++;
+       }
+
+       trace_thermal_power_allocator(tz, req_power, total_req_power,
+                                     granted_power, total_granted_power,
+                                     num_actors, power_range,
+                                     max_allocatable_power, tz->temperature,
+                                     control_temp - tz->temperature);
+
+       kfree(req_power);
+unlock:
+       mutex_unlock(&tz->lock);
+
+       return ret;
+}
+
+/**
+ * get_governor_trips() - get the number of the two trip points that are key for this governor
+ * @tz:        thermal zone to operate on
+ * @params:    pointer to private data for this governor
+ *
+ * The power allocator governor works optimally with two trips points:
+ * a "switch on" trip point and a "maximum desired temperature".  These
+ * are defined as the first and last passive trip points.
+ *
+ * If there is only one trip point, then that's considered to be the
+ * "maximum desired temperature" trip point and the governor is always
+ * on.  If there are no passive or active trip points, then the
+ * governor won't do anything.  In fact, its throttle function
+ * won't be called at all.
+ */
+static void get_governor_trips(struct thermal_zone_device *tz,
+                              struct power_allocator_params *params)
+{
+       int i, last_active, last_passive;
+       bool found_first_passive;
+
+       found_first_passive = false;
+       last_active = INVALID_TRIP;
+       last_passive = INVALID_TRIP;
+
+       for (i = 0; i < tz->trips; i++) {
+               enum thermal_trip_type type;
+               int ret;
+
+               ret = tz->ops->get_trip_type(tz, i, &type);
+               if (ret) {
+                       dev_warn(&tz->device,
+                                "Failed to get trip point %d type: %d\n", i,
+                                ret);
+                       continue;
+               }
+
+               if (type == THERMAL_TRIP_PASSIVE) {
+                       if (!found_first_passive) {
+                               params->trip_switch_on = i;
+                               found_first_passive = true;
+                       } else  {
+                               last_passive = i;
+                       }
+               } else if (type == THERMAL_TRIP_ACTIVE) {
+                       last_active = i;
+               } else {
+                       break;
+               }
+       }
+
+       if (last_passive != INVALID_TRIP) {
+               params->trip_max_desired_temperature = last_passive;
+       } else if (found_first_passive) {
+               params->trip_max_desired_temperature = params->trip_switch_on;
+               params->trip_switch_on = INVALID_TRIP;
+       } else {
+               params->trip_switch_on = INVALID_TRIP;
+               params->trip_max_desired_temperature = last_active;
+       }
+}
+
+static void reset_pid_controller(struct power_allocator_params *params)
+{
+       params->err_integral = 0;
+       params->prev_err = 0;
+}
+
+static void allow_maximum_power(struct thermal_zone_device *tz)
+{
+       struct thermal_instance *instance;
+       struct power_allocator_params *params = tz->governor_data;
+
+       mutex_lock(&tz->lock);
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               if ((instance->trip != params->trip_max_desired_temperature) ||
+                   (!cdev_is_power_actor(instance->cdev)))
+                       continue;
+
+               instance->target = 0;
+               mutex_lock(&instance->cdev->lock);
+               instance->cdev->updated = false;
+               mutex_unlock(&instance->cdev->lock);
+               thermal_cdev_update(instance->cdev);
+       }
+       mutex_unlock(&tz->lock);
+}
+
+/**
+ * power_allocator_bind() - bind the power_allocator governor to a thermal zone
+ * @tz:        thermal zone to bind it to
+ *
+ * Initialize the PID controller parameters and bind it to the thermal
+ * zone.
+ *
+ * Return: 0 on success, or -ENOMEM if we ran out of memory.
+ */
+static int power_allocator_bind(struct thermal_zone_device *tz)
+{
+       int ret;
+       struct power_allocator_params *params;
+       int control_temp;
+
+       params = kzalloc(sizeof(*params), GFP_KERNEL);
+       if (!params)
+               return -ENOMEM;
+
+       if (!tz->tzp) {
+               tz->tzp = kzalloc(sizeof(*tz->tzp), GFP_KERNEL);
+               if (!tz->tzp) {
+                       ret = -ENOMEM;
+                       goto free_params;
+               }
+
+               params->allocated_tzp = true;
+       }
+
+       if (!tz->tzp->sustainable_power)
+               dev_warn(&tz->device, "power_allocator: sustainable_power will be estimated\n");
+
+       get_governor_trips(tz, params);
+
+       if (tz->trips > 0) {
+               ret = tz->ops->get_trip_temp(tz,
+                                       params->trip_max_desired_temperature,
+                                       &control_temp);
+               if (!ret)
+                       estimate_pid_constants(tz, tz->tzp->sustainable_power,
+                                              params->trip_switch_on,
+                                              control_temp, false);
+       }
+
+       reset_pid_controller(params);
+
+       tz->governor_data = params;
+
+       return 0;
+
+free_params:
+       kfree(params);
+
+       return ret;
+}
+
+static void power_allocator_unbind(struct thermal_zone_device *tz)
+{
+       struct power_allocator_params *params = tz->governor_data;
+
+       dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id);
+
+       if (params->allocated_tzp) {
+               kfree(tz->tzp);
+               tz->tzp = NULL;
+       }
+
+       kfree(tz->governor_data);
+       tz->governor_data = NULL;
+}
+
+static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
+{
+       int ret;
+       int switch_on_temp, control_temp;
+       struct power_allocator_params *params = tz->governor_data;
+
+       /*
+        * We get called for every trip point but we only need to do
+        * our calculations once
+        */
+       if (trip != params->trip_max_desired_temperature)
+               return 0;
+
+       ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
+                                    &switch_on_temp);
+       if (!ret && (tz->temperature < switch_on_temp)) {
+               tz->passive = 0;
+               reset_pid_controller(params);
+               allow_maximum_power(tz);
+               return 0;
+       }
+
+       tz->passive = 1;
+
+       ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature,
+                               &control_temp);
+       if (ret) {
+               dev_warn(&tz->device,
+                        "Failed to get the maximum desired temperature: %d\n",
+                        ret);
+               return ret;
+       }
+
+       return allocate_power(tz, control_temp);
+}
+
+static struct thermal_governor thermal_gov_power_allocator = {
+       .name           = "power_allocator",
+       .bind_to_tz     = power_allocator_bind,
+       .unbind_from_tz = power_allocator_unbind,
+       .throttle       = power_allocator_throttle,
+};
+THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator);
diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c
new file mode 100644 (file)
index 0000000..2ae7198
--- /dev/null
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  step_wise.c - A step-by-step Thermal throttling governor
+ *
+ *  Copyright (C) 2012 Intel Corp
+ *  Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/thermal.h>
+#include <trace/events/thermal.h>
+
+#include "thermal_core.h"
+
+/*
+ * If the temperature is higher than a trip point,
+ *    a. if the trend is THERMAL_TREND_RAISING, use higher cooling
+ *       state for this trip point
+ *    b. if the trend is THERMAL_TREND_DROPPING, do nothing
+ *    c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit
+ *       for this trip point
+ *    d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit
+ *       for this trip point
+ * If the temperature is lower than a trip point,
+ *    a. if the trend is THERMAL_TREND_RAISING, do nothing
+ *    b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
+ *       state for this trip point, if the cooling state already
+ *       equals lower limit, deactivate the thermal instance
+ *    c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing
+ *    d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit,
+ *       if the cooling state already equals lower limit,
+ *       deactivate the thermal instance
+ */
+static unsigned long get_target_state(struct thermal_instance *instance,
+                               enum thermal_trend trend, bool throttle)
+{
+       struct thermal_cooling_device *cdev = instance->cdev;
+       unsigned long cur_state;
+       unsigned long next_target;
+
+       /*
+        * We keep this instance the way it is by default.
+        * Otherwise, we use the current state of the
+        * cdev in use to determine the next_target.
+        */
+       cdev->ops->get_cur_state(cdev, &cur_state);
+       next_target = instance->target;
+       dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
+
+       if (!instance->initialized) {
+               if (throttle) {
+                       next_target = (cur_state + 1) >= instance->upper ?
+                                       instance->upper :
+                                       ((cur_state + 1) < instance->lower ?
+                                       instance->lower : (cur_state + 1));
+               } else {
+                       next_target = THERMAL_NO_TARGET;
+               }
+
+               return next_target;
+       }
+
+       switch (trend) {
+       case THERMAL_TREND_RAISING:
+               if (throttle) {
+                       next_target = cur_state < instance->upper ?
+                                   (cur_state + 1) : instance->upper;
+                       if (next_target < instance->lower)
+                               next_target = instance->lower;
+               }
+               break;
+       case THERMAL_TREND_RAISE_FULL:
+               if (throttle)
+                       next_target = instance->upper;
+               break;
+       case THERMAL_TREND_DROPPING:
+               if (cur_state <= instance->lower) {
+                       if (!throttle)
+                               next_target = THERMAL_NO_TARGET;
+               } else {
+                       if (!throttle) {
+                               next_target = cur_state - 1;
+                               if (next_target > instance->upper)
+                                       next_target = instance->upper;
+                       }
+               }
+               break;
+       case THERMAL_TREND_DROP_FULL:
+               if (cur_state == instance->lower) {
+                       if (!throttle)
+                               next_target = THERMAL_NO_TARGET;
+               } else
+                       next_target = instance->lower;
+               break;
+       default:
+               break;
+       }
+
+       return next_target;
+}
+
+static void update_passive_instance(struct thermal_zone_device *tz,
+                               enum thermal_trip_type type, int value)
+{
+       /*
+        * If value is +1, activate a passive instance.
+        * If value is -1, deactivate a passive instance.
+        */
+       if (type == THERMAL_TRIP_PASSIVE || type == THERMAL_TRIPS_NONE)
+               tz->passive += value;
+}
+
+static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
+{
+       int trip_temp;
+       enum thermal_trip_type trip_type;
+       enum thermal_trend trend;
+       struct thermal_instance *instance;
+       bool throttle = false;
+       int old_target;
+
+       if (trip == THERMAL_TRIPS_NONE) {
+               trip_temp = tz->forced_passive;
+               trip_type = THERMAL_TRIPS_NONE;
+       } else {
+               tz->ops->get_trip_temp(tz, trip, &trip_temp);
+               tz->ops->get_trip_type(tz, trip, &trip_type);
+       }
+
+       trend = get_tz_trend(tz, trip);
+
+       if (tz->temperature >= trip_temp) {
+               throttle = true;
+               trace_thermal_zone_trip(tz, trip, trip_type);
+       }
+
+       dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
+                               trip, trip_type, trip_temp, trend, throttle);
+
+       mutex_lock(&tz->lock);
+
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+               if (instance->trip != trip)
+                       continue;
+
+               old_target = instance->target;
+               instance->target = get_target_state(instance, trend, throttle);
+               dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
+                                       old_target, (int)instance->target);
+
+               if (instance->initialized && old_target == instance->target)
+                       continue;
+
+               /* Activate a passive thermal instance */
+               if (old_target == THERMAL_NO_TARGET &&
+                       instance->target != THERMAL_NO_TARGET)
+                       update_passive_instance(tz, trip_type, 1);
+               /* Deactivate a passive thermal instance */
+               else if (old_target != THERMAL_NO_TARGET &&
+                       instance->target == THERMAL_NO_TARGET)
+                       update_passive_instance(tz, trip_type, -1);
+
+               instance->initialized = true;
+               mutex_lock(&instance->cdev->lock);
+               instance->cdev->updated = false; /* cdev needs update */
+               mutex_unlock(&instance->cdev->lock);
+       }
+
+       mutex_unlock(&tz->lock);
+}
+
+/**
+ * step_wise_throttle - throttles devices associated with the given zone
+ * @tz: thermal_zone_device
+ * @trip: trip point index
+ *
+ * Throttling Logic: This uses the trend of the thermal zone to throttle.
+ * If the thermal zone is 'heating up' this throttles all the cooling
+ * devices associated with the zone and its particular trip point, by one
+ * step. If the zone is 'cooling down' it brings back the performance of
+ * the devices by one step.
+ */
+static int step_wise_throttle(struct thermal_zone_device *tz, int trip)
+{
+       struct thermal_instance *instance;
+
+       thermal_zone_trip_update(tz, trip);
+
+       if (tz->forced_passive)
+               thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE);
+
+       mutex_lock(&tz->lock);
+
+       list_for_each_entry(instance, &tz->thermal_instances, tz_node)
+               thermal_cdev_update(instance->cdev);
+
+       mutex_unlock(&tz->lock);
+
+       return 0;
+}
+
+static struct thermal_governor thermal_gov_step_wise = {
+       .name           = "step_wise",
+       .throttle       = step_wise_throttle,
+};
+THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise);
diff --git a/drivers/thermal/gov_user_space.c b/drivers/thermal/gov_user_space.c
new file mode 100644 (file)
index 0000000..82a7198
--- /dev/null
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  user_space.c - A simple user space Thermal events notifier
+ *
+ *  Copyright (C) 2012 Intel Corp
+ *  Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+/**
+ * notify_user_space - Notifies user space about thermal events
+ * @tz: thermal_zone_device
+ * @trip: trip point index
+ *
+ * This function notifies the user space through UEvents.
+ */
+static int notify_user_space(struct thermal_zone_device *tz, int trip)
+{
+       char *thermal_prop[5];
+       int i;
+
+       mutex_lock(&tz->lock);
+       thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", tz->type);
+       thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", tz->temperature);
+       thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=%d", trip);
+       thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", tz->notify_event);
+       thermal_prop[4] = NULL;
+       kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop);
+       for (i = 0; i < 4; ++i)
+               kfree(thermal_prop[i]);
+       mutex_unlock(&tz->lock);
+       return 0;
+}
+
+static struct thermal_governor thermal_gov_user_space = {
+       .name           = "user_space",
+       .throttle       = notify_user_space,
+};
+THERMAL_GOVERNOR_DECLARE(thermal_gov_user_space);
index 0d60f8d..e6061e2 100644 (file)
@@ -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)
index 8938ea8..b01d28e 100644 (file)
@@ -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);
index ceef89c..0b3a626 100644 (file)
@@ -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 (file)
index 0000000..35f41e8
--- /dev/null
@@ -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/of-thermal.c b/drivers/thermal/of-thermal.c
deleted file mode 100644 (file)
index 874a47d..0000000
+++ /dev/null
@@ -1,1151 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- *  of-thermal.c - Generic Thermal Management device tree support.
- *
- *  Copyright (C) 2013 Texas Instruments
- *  Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
- */
-
-#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/string.h>
-
-#include "thermal_core.h"
-
-/***   Private data structures to represent thermal device tree data ***/
-
-/**
- * struct __thermal_cooling_bind_param - a cooling device for a trip point
- * @cooling_device: a pointer to identify the referred cooling device
- * @min: minimum cooling state used at this trip point
- * @max: maximum cooling state used at this trip point
- */
-
-struct __thermal_cooling_bind_param {
-       struct device_node *cooling_device;
-       unsigned long min;
-       unsigned long max;
-};
-
-/**
- * struct __thermal_bind_param - a match between trip and cooling device
- * @tcbp: a pointer to an array of cooling devices
- * @count: number of elements in array
- * @trip_id: the trip point index
- * @usage: the percentage (from 0 to 100) of cooling contribution
- */
-
-struct __thermal_bind_params {
-       struct __thermal_cooling_bind_param *tcbp;
-       unsigned int count;
-       unsigned int trip_id;
-       unsigned int usage;
-};
-
-/**
- * struct __thermal_zone - internal representation of a thermal zone
- * @mode: current thermal zone device mode (enabled/disabled)
- * @passive_delay: polling interval while passive cooling is activated
- * @polling_delay: zone polling interval
- * @slope: slope of the temperature adjustment curve
- * @offset: offset of the temperature adjustment curve
- * @ntrips: number of trip points
- * @trips: an array of trip points (0..ntrips - 1)
- * @num_tbps: number of thermal bind params
- * @tbps: an array of thermal bind params (0..num_tbps - 1)
- * @sensor_data: sensor private data used while reading temperature and trend
- * @ops: set of callbacks to handle the thermal zone based on DT
- */
-
-struct __thermal_zone {
-       enum thermal_device_mode mode;
-       int passive_delay;
-       int polling_delay;
-       int slope;
-       int offset;
-
-       /* trip data */
-       int ntrips;
-       struct thermal_trip *trips;
-
-       /* cooling binding data */
-       int num_tbps;
-       struct __thermal_bind_params *tbps;
-
-       /* sensor interface */
-       void *sensor_data;
-       const struct thermal_zone_of_device_ops *ops;
-};
-
-/***   DT thermal zone device callbacks   ***/
-
-static int of_thermal_get_temp(struct thermal_zone_device *tz,
-                              int *temp)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (!data->ops->get_temp)
-               return -EINVAL;
-
-       return data->ops->get_temp(data->sensor_data, temp);
-}
-
-static int of_thermal_set_trips(struct thermal_zone_device *tz,
-                               int low, int high)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (!data->ops || !data->ops->set_trips)
-               return -EINVAL;
-
-       return data->ops->set_trips(data->sensor_data, low, high);
-}
-
-/**
- * of_thermal_get_ntrips - function to export number of available trip
- *                        points.
- * @tz: pointer to a thermal zone
- *
- * This function is a globally visible wrapper to get number of trip points
- * stored in the local struct __thermal_zone
- *
- * Return: number of available trip points, -ENODEV when data not available
- */
-int of_thermal_get_ntrips(struct thermal_zone_device *tz)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (!data || IS_ERR(data))
-               return -ENODEV;
-
-       return data->ntrips;
-}
-EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
-
-/**
- * of_thermal_is_trip_valid - function to check if trip point is valid
- *
- * @tz:        pointer to a thermal zone
- * @trip:      trip point to evaluate
- *
- * This function is responsible for checking if passed trip point is valid
- *
- * Return: true if trip point is valid, false otherwise
- */
-bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (!data || trip >= data->ntrips || trip < 0)
-               return false;
-
-       return true;
-}
-EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
-
-/**
- * of_thermal_get_trip_points - function to get access to a globally exported
- *                             trip points
- *
- * @tz:        pointer to a thermal zone
- *
- * This function provides a pointer to trip points table
- *
- * Return: pointer to trip points table, NULL otherwise
- */
-const struct thermal_trip *
-of_thermal_get_trip_points(struct thermal_zone_device *tz)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (!data)
-               return NULL;
-
-       return data->trips;
-}
-EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
-
-/**
- * of_thermal_set_emul_temp - function to set emulated temperature
- *
- * @tz:        pointer to a thermal zone
- * @temp:      temperature to set
- *
- * This function gives the ability to set emulated value of temperature,
- * which is handy for debugging
- *
- * Return: zero on success, error code otherwise
- */
-static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
-                                   int temp)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       return data->ops->set_emul_temp(data->sensor_data, temp);
-}
-
-static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
-                               enum thermal_trend *trend)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (!data->ops->get_trend)
-               return -EINVAL;
-
-       return data->ops->get_trend(data->sensor_data, trip, trend);
-}
-
-static int of_thermal_bind(struct thermal_zone_device *thermal,
-                          struct thermal_cooling_device *cdev)
-{
-       struct __thermal_zone *data = thermal->devdata;
-       struct __thermal_bind_params *tbp;
-       struct __thermal_cooling_bind_param *tcbp;
-       int i, j;
-
-       if (!data || IS_ERR(data))
-               return -ENODEV;
-
-       /* find where to bind */
-       for (i = 0; i < data->num_tbps; i++) {
-               tbp = data->tbps + i;
-
-               for (j = 0; j < tbp->count; j++) {
-                       tcbp = tbp->tcbp + j;
-
-                       if (tcbp->cooling_device == cdev->np) {
-                               int ret;
-
-                               ret = thermal_zone_bind_cooling_device(thermal,
-                                               tbp->trip_id, cdev,
-                                               tcbp->max,
-                                               tcbp->min,
-                                               tbp->usage);
-                               if (ret)
-                                       return ret;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int of_thermal_unbind(struct thermal_zone_device *thermal,
-                            struct thermal_cooling_device *cdev)
-{
-       struct __thermal_zone *data = thermal->devdata;
-       struct __thermal_bind_params *tbp;
-       struct __thermal_cooling_bind_param *tcbp;
-       int i, j;
-
-       if (!data || IS_ERR(data))
-               return -ENODEV;
-
-       /* find where to unbind */
-       for (i = 0; i < data->num_tbps; i++) {
-               tbp = data->tbps + i;
-
-               for (j = 0; j < tbp->count; j++) {
-                       tcbp = tbp->tcbp + j;
-
-                       if (tcbp->cooling_device == cdev->np) {
-                               int ret;
-
-                               ret = thermal_zone_unbind_cooling_device(thermal,
-                                                       tbp->trip_id, cdev);
-                               if (ret)
-                                       return ret;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int of_thermal_get_mode(struct thermal_zone_device *tz,
-                              enum thermal_device_mode *mode)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       *mode = data->mode;
-
-       return 0;
-}
-
-static int of_thermal_set_mode(struct thermal_zone_device *tz,
-                              enum thermal_device_mode mode)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       mutex_lock(&tz->lock);
-
-       if (mode == THERMAL_DEVICE_ENABLED) {
-               tz->polling_delay = data->polling_delay;
-               tz->passive_delay = data->passive_delay;
-       } else {
-               tz->polling_delay = 0;
-               tz->passive_delay = 0;
-       }
-
-       mutex_unlock(&tz->lock);
-
-       data->mode = mode;
-       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
-
-       return 0;
-}
-
-static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
-                                   enum thermal_trip_type *type)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (trip >= data->ntrips || trip < 0)
-               return -EDOM;
-
-       *type = data->trips[trip].type;
-
-       return 0;
-}
-
-static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
-                                   int *temp)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (trip >= data->ntrips || trip < 0)
-               return -EDOM;
-
-       *temp = data->trips[trip].temperature;
-
-       return 0;
-}
-
-static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
-                                   int temp)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (trip >= data->ntrips || trip < 0)
-               return -EDOM;
-
-       if (data->ops->set_trip_temp) {
-               int ret;
-
-               ret = data->ops->set_trip_temp(data->sensor_data, trip, temp);
-               if (ret)
-                       return ret;
-       }
-
-       /* thermal framework should take care of data->mask & (1 << trip) */
-       data->trips[trip].temperature = temp;
-
-       return 0;
-}
-
-static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
-                                   int *hyst)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (trip >= data->ntrips || trip < 0)
-               return -EDOM;
-
-       *hyst = data->trips[trip].hysteresis;
-
-       return 0;
-}
-
-static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
-                                   int hyst)
-{
-       struct __thermal_zone *data = tz->devdata;
-
-       if (trip >= data->ntrips || trip < 0)
-               return -EDOM;
-
-       /* thermal framework should take care of data->mask & (1 << trip) */
-       data->trips[trip].hysteresis = hyst;
-
-       return 0;
-}
-
-static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
-                                   int *temp)
-{
-       struct __thermal_zone *data = tz->devdata;
-       int i;
-
-       for (i = 0; i < data->ntrips; i++)
-               if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
-                       *temp = data->trips[i].temperature;
-                       return 0;
-               }
-
-       return -EINVAL;
-}
-
-static struct thermal_zone_device_ops of_thermal_ops = {
-       .get_mode = of_thermal_get_mode,
-       .set_mode = of_thermal_set_mode,
-
-       .get_trip_type = of_thermal_get_trip_type,
-       .get_trip_temp = of_thermal_get_trip_temp,
-       .set_trip_temp = of_thermal_set_trip_temp,
-       .get_trip_hyst = of_thermal_get_trip_hyst,
-       .set_trip_hyst = of_thermal_set_trip_hyst,
-       .get_crit_temp = of_thermal_get_crit_temp,
-
-       .bind = of_thermal_bind,
-       .unbind = of_thermal_unbind,
-};
-
-/***   sensor API   ***/
-
-static struct thermal_zone_device *
-thermal_zone_of_add_sensor(struct device_node *zone,
-                          struct device_node *sensor, void *data,
-                          const struct thermal_zone_of_device_ops *ops)
-{
-       struct thermal_zone_device *tzd;
-       struct __thermal_zone *tz;
-
-       tzd = thermal_zone_get_zone_by_name(zone->name);
-       if (IS_ERR(tzd))
-               return ERR_PTR(-EPROBE_DEFER);
-
-       tz = tzd->devdata;
-
-       if (!ops)
-               return ERR_PTR(-EINVAL);
-
-       mutex_lock(&tzd->lock);
-       tz->ops = ops;
-       tz->sensor_data = data;
-
-       tzd->ops->get_temp = of_thermal_get_temp;
-       tzd->ops->get_trend = of_thermal_get_trend;
-
-       /*
-        * The thermal zone core will calculate the window if they have set the
-        * optional set_trips pointer.
-        */
-       if (ops->set_trips)
-               tzd->ops->set_trips = of_thermal_set_trips;
-
-       if (ops->set_emul_temp)
-               tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
-
-       mutex_unlock(&tzd->lock);
-
-       return tzd;
-}
-
-/**
- * thermal_zone_of_get_sensor_id - get sensor ID from a DT thermal zone
- * @tz_np: a valid thermal zone device node.
- * @sensor_np: a sensor node of a valid sensor device.
- * @id: the sensor ID returned if success.
- *
- * This function will get sensor ID from a given thermal zone node and
- * the sensor node must match the temperature provider @sensor_np.
- *
- * Return: 0 on success, proper error code otherwise.
- */
-
-int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
-                                 struct device_node *sensor_np,
-                                 u32 *id)
-{
-       struct of_phandle_args sensor_specs;
-       int ret;
-
-       ret = of_parse_phandle_with_args(tz_np,
-                                        "thermal-sensors",
-                                        "#thermal-sensor-cells",
-                                        0,
-                                        &sensor_specs);
-       if (ret)
-               return ret;
-
-       if (sensor_specs.np != sensor_np) {
-               of_node_put(sensor_specs.np);
-               return -ENODEV;
-       }
-
-       if (sensor_specs.args_count > 1)
-               pr_warn("%pOFn: too many cells in sensor specifier %d\n",
-                    sensor_specs.np, sensor_specs.args_count);
-
-       *id = sensor_specs.args_count ? sensor_specs.args[0] : 0;
-
-       of_node_put(sensor_specs.np);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(thermal_zone_of_get_sensor_id);
-
-/**
- * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
- * @dev: a valid struct device pointer of a sensor device. Must contain
- *       a valid .of_node, for the sensor node.
- * @sensor_id: a sensor identifier, in case the sensor IP has more
- *             than one sensors
- * @data: a private pointer (owned by the caller) that will be passed
- *        back, when a temperature reading is needed.
- * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
- *
- * This function will search the list of thermal zones described in device
- * tree and look for the zone that refer to the sensor device pointed by
- * @dev->of_node as temperature providers. For the zone pointing to the
- * sensor node, the sensor will be added to the DT thermal zone device.
- *
- * The thermal zone temperature is provided by the @get_temp function
- * pointer. When called, it will have the private pointer @data back.
- *
- * The thermal zone temperature trend is provided by the @get_trend function
- * pointer. When called, it will have the private pointer @data back.
- *
- * TODO:
- * 01 - This function must enqueue the new sensor instead of using
- * it as the only source of temperature values.
- *
- * 02 - There must be a way to match the sensor with all thermal zones
- * that refer to it.
- *
- * Return: On success returns a valid struct thermal_zone_device,
- * otherwise, it returns a corresponding ERR_PTR(). Caller must
- * check the return value with help of IS_ERR() helper.
- */
-struct thermal_zone_device *
-thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
-                               const struct thermal_zone_of_device_ops *ops)
-{
-       struct device_node *np, *child, *sensor_np;
-       struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
-
-       np = of_find_node_by_name(NULL, "thermal-zones");
-       if (!np)
-               return ERR_PTR(-ENODEV);
-
-       if (!dev || !dev->of_node) {
-               of_node_put(np);
-               return ERR_PTR(-ENODEV);
-       }
-
-       sensor_np = of_node_get(dev->of_node);
-
-       for_each_available_child_of_node(np, child) {
-               int ret, id;
-
-               /* For now, thermal framework supports only 1 sensor per zone */
-               ret = thermal_zone_of_get_sensor_id(child, sensor_np, &id);
-               if (ret)
-                       continue;
-
-               if (id == sensor_id) {
-                       tzd = thermal_zone_of_add_sensor(child, sensor_np,
-                                                        data, ops);
-                       if (!IS_ERR(tzd))
-                               tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
-
-                       of_node_put(child);
-                       goto exit;
-               }
-       }
-exit:
-       of_node_put(sensor_np);
-       of_node_put(np);
-
-       return tzd;
-}
-EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
-
-/**
- * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
- * @dev: a valid struct device pointer of a sensor device. Must contain
- *       a valid .of_node, for the sensor node.
- * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
- *
- * This function removes the sensor callbacks and private data from the
- * thermal zone device registered with thermal_zone_of_sensor_register()
- * API. It will also silent the zone by remove the .get_temp() and .get_trend()
- * thermal zone device callbacks.
- *
- * TODO: When the support to several sensors per zone is added, this
- * function must search the sensor list based on @dev parameter.
- *
- */
-void thermal_zone_of_sensor_unregister(struct device *dev,
-                                      struct thermal_zone_device *tzd)
-{
-       struct __thermal_zone *tz;
-
-       if (!dev || !tzd || !tzd->devdata)
-               return;
-
-       tz = tzd->devdata;
-
-       /* no __thermal_zone, nothing to be done */
-       if (!tz)
-               return;
-
-       mutex_lock(&tzd->lock);
-       tzd->ops->get_temp = NULL;
-       tzd->ops->get_trend = NULL;
-       tzd->ops->set_emul_temp = NULL;
-
-       tz->ops = NULL;
-       tz->sensor_data = NULL;
-       mutex_unlock(&tzd->lock);
-}
-EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
-
-static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
-{
-       thermal_zone_of_sensor_unregister(dev,
-                                         *(struct thermal_zone_device **)res);
-}
-
-static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
-                                            void *data)
-{
-       struct thermal_zone_device **r = res;
-
-       if (WARN_ON(!r || !*r))
-               return 0;
-
-       return *r == data;
-}
-
-/**
- * devm_thermal_zone_of_sensor_register - Resource managed version of
- *                             thermal_zone_of_sensor_register()
- * @dev: a valid struct device pointer of a sensor device. Must contain
- *       a valid .of_node, for the sensor node.
- * @sensor_id: a sensor identifier, in case the sensor IP has more
- *            than one sensors
- * @data: a private pointer (owned by the caller) that will be passed
- *       back, when a temperature reading is needed.
- * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
- *
- * Refer thermal_zone_of_sensor_register() for more details.
- *
- * Return: On success returns a valid struct thermal_zone_device,
- * otherwise, it returns a corresponding ERR_PTR(). Caller must
- * check the return value with help of IS_ERR() helper.
- * Registered thermal_zone_device device will automatically be
- * released when device is unbounded.
- */
-struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
-       struct device *dev, int sensor_id,
-       void *data, const struct thermal_zone_of_device_ops *ops)
-{
-       struct thermal_zone_device **ptr, *tzd;
-
-       ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
-                          GFP_KERNEL);
-       if (!ptr)
-               return ERR_PTR(-ENOMEM);
-
-       tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
-       if (IS_ERR(tzd)) {
-               devres_free(ptr);
-               return tzd;
-       }
-
-       *ptr = tzd;
-       devres_add(dev, ptr);
-
-       return tzd;
-}
-EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
-
-/**
- * devm_thermal_zone_of_sensor_unregister - Resource managed version of
- *                             thermal_zone_of_sensor_unregister().
- * @dev: Device for which which resource was allocated.
- * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
- *
- * This function removes the sensor callbacks and private data from the
- * thermal zone device registered with devm_thermal_zone_of_sensor_register()
- * API. It will also silent the zone by remove the .get_temp() and .get_trend()
- * thermal zone device callbacks.
- * Normally this function will not need to be called and the resource
- * management code will ensure that the resource is freed.
- */
-void devm_thermal_zone_of_sensor_unregister(struct device *dev,
-                                           struct thermal_zone_device *tzd)
-{
-       WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
-                              devm_thermal_zone_of_sensor_match, tzd));
-}
-EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
-
-/***   functions parsing device tree nodes   ***/
-
-/**
- * thermal_of_populate_bind_params - parse and fill cooling map data
- * @np: DT node containing a cooling-map node
- * @__tbp: data structure to be filled with cooling map info
- * @trips: array of thermal zone trip points
- * @ntrips: number of trip points inside trips.
- *
- * This function parses a cooling-map type of node represented by
- * @np parameter and fills the read data into @__tbp data structure.
- * It needs the already parsed array of trip points of the thermal zone
- * in consideration.
- *
- * Return: 0 on success, proper error code otherwise
- */
-static int thermal_of_populate_bind_params(struct device_node *np,
-                                          struct __thermal_bind_params *__tbp,
-                                          struct thermal_trip *trips,
-                                          int ntrips)
-{
-       struct of_phandle_args cooling_spec;
-       struct __thermal_cooling_bind_param *__tcbp;
-       struct device_node *trip;
-       int ret, i, count;
-       u32 prop;
-
-       /* Default weight. Usage is optional */
-       __tbp->usage = THERMAL_WEIGHT_DEFAULT;
-       ret = of_property_read_u32(np, "contribution", &prop);
-       if (ret == 0)
-               __tbp->usage = prop;
-
-       trip = of_parse_phandle(np, "trip", 0);
-       if (!trip) {
-               pr_err("missing trip property\n");
-               return -ENODEV;
-       }
-
-       /* match using device_node */
-       for (i = 0; i < ntrips; i++)
-               if (trip == trips[i].np) {
-                       __tbp->trip_id = i;
-                       break;
-               }
-
-       if (i == ntrips) {
-               ret = -ENODEV;
-               goto end;
-       }
-
-       count = of_count_phandle_with_args(np, "cooling-device",
-                                          "#cooling-cells");
-       if (!count) {
-               pr_err("Add a cooling_device property with at least one device\n");
-               goto end;
-       }
-
-       __tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL);
-       if (!__tcbp)
-               goto end;
-
-       for (i = 0; i < count; i++) {
-               ret = of_parse_phandle_with_args(np, "cooling-device",
-                               "#cooling-cells", i, &cooling_spec);
-               if (ret < 0) {
-                       pr_err("Invalid cooling-device entry\n");
-                       goto free_tcbp;
-               }
-
-               __tcbp[i].cooling_device = cooling_spec.np;
-
-               if (cooling_spec.args_count >= 2) { /* at least min and max */
-                       __tcbp[i].min = cooling_spec.args[0];
-                       __tcbp[i].max = cooling_spec.args[1];
-               } else {
-                       pr_err("wrong reference to cooling device, missing limits\n");
-               }
-       }
-
-       __tbp->tcbp = __tcbp;
-       __tbp->count = count;
-
-       goto end;
-
-free_tcbp:
-       for (i = i - 1; i >= 0; i--)
-               of_node_put(__tcbp[i].cooling_device);
-       kfree(__tcbp);
-end:
-       of_node_put(trip);
-
-       return ret;
-}
-
-/*
- * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
- * into the device tree binding of 'trip', property type.
- */
-static const char * const trip_types[] = {
-       [THERMAL_TRIP_ACTIVE]   = "active",
-       [THERMAL_TRIP_PASSIVE]  = "passive",
-       [THERMAL_TRIP_HOT]      = "hot",
-       [THERMAL_TRIP_CRITICAL] = "critical",
-};
-
-/**
- * thermal_of_get_trip_type - Get phy mode for given device_node
- * @np:        Pointer to the given device_node
- * @type: Pointer to resulting trip type
- *
- * The function gets trip type string from property 'type',
- * and store its index in trip_types table in @type,
- *
- * Return: 0 on success, or errno in error case.
- */
-static int thermal_of_get_trip_type(struct device_node *np,
-                                   enum thermal_trip_type *type)
-{
-       const char *t;
-       int err, i;
-
-       err = of_property_read_string(np, "type", &t);
-       if (err < 0)
-               return err;
-
-       for (i = 0; i < ARRAY_SIZE(trip_types); i++)
-               if (!strcasecmp(t, trip_types[i])) {
-                       *type = i;
-                       return 0;
-               }
-
-       return -ENODEV;
-}
-
-/**
- * thermal_of_populate_trip - parse and fill one trip point data
- * @np: DT node containing a trip point node
- * @trip: trip point data structure to be filled up
- *
- * This function parses a trip point type of node represented by
- * @np parameter and fills the read data into @trip data structure.
- *
- * Return: 0 on success, proper error code otherwise
- */
-static int thermal_of_populate_trip(struct device_node *np,
-                                   struct thermal_trip *trip)
-{
-       int prop;
-       int ret;
-
-       ret = of_property_read_u32(np, "temperature", &prop);
-       if (ret < 0) {
-               pr_err("missing temperature property\n");
-               return ret;
-       }
-       trip->temperature = prop;
-
-       ret = of_property_read_u32(np, "hysteresis", &prop);
-       if (ret < 0) {
-               pr_err("missing hysteresis property\n");
-               return ret;
-       }
-       trip->hysteresis = prop;
-
-       ret = thermal_of_get_trip_type(np, &trip->type);
-       if (ret < 0) {
-               pr_err("wrong trip type property\n");
-               return ret;
-       }
-
-       /* Required for cooling map matching */
-       trip->np = np;
-       of_node_get(np);
-
-       return 0;
-}
-
-/**
- * thermal_of_build_thermal_zone - parse and fill one thermal zone data
- * @np: DT node containing a thermal zone node
- *
- * This function parses a thermal zone type of node represented by
- * @np parameter and fills the read data into a __thermal_zone data structure
- * and return this pointer.
- *
- * TODO: Missing properties to parse: thermal-sensor-names
- *
- * Return: On success returns a valid struct __thermal_zone,
- * otherwise, it returns a corresponding ERR_PTR(). Caller must
- * check the return value with help of IS_ERR() helper.
- */
-static struct __thermal_zone
-__init *thermal_of_build_thermal_zone(struct device_node *np)
-{
-       struct device_node *child = NULL, *gchild;
-       struct __thermal_zone *tz;
-       int ret, i;
-       u32 prop, coef[2];
-
-       if (!np) {
-               pr_err("no thermal zone np\n");
-               return ERR_PTR(-EINVAL);
-       }
-
-       tz = kzalloc(sizeof(*tz), GFP_KERNEL);
-       if (!tz)
-               return ERR_PTR(-ENOMEM);
-
-       ret = of_property_read_u32(np, "polling-delay-passive", &prop);
-       if (ret < 0) {
-               pr_err("%pOFn: missing polling-delay-passive property\n", np);
-               goto free_tz;
-       }
-       tz->passive_delay = prop;
-
-       ret = of_property_read_u32(np, "polling-delay", &prop);
-       if (ret < 0) {
-               pr_err("%pOFn: missing polling-delay property\n", np);
-               goto free_tz;
-       }
-       tz->polling_delay = prop;
-
-       /*
-        * REVIST: for now, the thermal framework supports only
-        * one sensor per thermal zone. Thus, we are considering
-        * only the first two values as slope and offset.
-        */
-       ret = of_property_read_u32_array(np, "coefficients", coef, 2);
-       if (ret == 0) {
-               tz->slope = coef[0];
-               tz->offset = coef[1];
-       } else {
-               tz->slope = 1;
-               tz->offset = 0;
-       }
-
-       /* trips */
-       child = of_get_child_by_name(np, "trips");
-
-       /* No trips provided */
-       if (!child)
-               goto finish;
-
-       tz->ntrips = of_get_child_count(child);
-       if (tz->ntrips == 0) /* must have at least one child */
-               goto finish;
-
-       tz->trips = kcalloc(tz->ntrips, sizeof(*tz->trips), GFP_KERNEL);
-       if (!tz->trips) {
-               ret = -ENOMEM;
-               goto free_tz;
-       }
-
-       i = 0;
-       for_each_child_of_node(child, gchild) {
-               ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
-               if (ret)
-                       goto free_trips;
-       }
-
-       of_node_put(child);
-
-       /* cooling-maps */
-       child = of_get_child_by_name(np, "cooling-maps");
-
-       /* cooling-maps not provided */
-       if (!child)
-               goto finish;
-
-       tz->num_tbps = of_get_child_count(child);
-       if (tz->num_tbps == 0)
-               goto finish;
-
-       tz->tbps = kcalloc(tz->num_tbps, sizeof(*tz->tbps), GFP_KERNEL);
-       if (!tz->tbps) {
-               ret = -ENOMEM;
-               goto free_trips;
-       }
-
-       i = 0;
-       for_each_child_of_node(child, gchild) {
-               ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
-                                                     tz->trips, tz->ntrips);
-               if (ret)
-                       goto free_tbps;
-       }
-
-finish:
-       of_node_put(child);
-       tz->mode = THERMAL_DEVICE_DISABLED;
-
-       return tz;
-
-free_tbps:
-       for (i = i - 1; i >= 0; i--) {
-               struct __thermal_bind_params *tbp = tz->tbps + i;
-               int j;
-
-               for (j = 0; j < tbp->count; j++)
-                       of_node_put(tbp->tcbp[j].cooling_device);
-
-               kfree(tbp->tcbp);
-       }
-
-       kfree(tz->tbps);
-free_trips:
-       for (i = 0; i < tz->ntrips; i++)
-               of_node_put(tz->trips[i].np);
-       kfree(tz->trips);
-       of_node_put(gchild);
-free_tz:
-       kfree(tz);
-       of_node_put(child);
-
-       return ERR_PTR(ret);
-}
-
-static __init void of_thermal_free_zone(struct __thermal_zone *tz)
-{
-       struct __thermal_bind_params *tbp;
-       int i, j;
-
-       for (i = 0; i < tz->num_tbps; i++) {
-               tbp = tz->tbps + i;
-
-               for (j = 0; j < tbp->count; j++)
-                       of_node_put(tbp->tcbp[j].cooling_device);
-
-               kfree(tbp->tcbp);
-       }
-
-       kfree(tz->tbps);
-       for (i = 0; i < tz->ntrips; i++)
-               of_node_put(tz->trips[i].np);
-       kfree(tz->trips);
-       kfree(tz);
-}
-
-/**
- * of_thermal_destroy_zones - remove all zones parsed and allocated resources
- *
- * Finds all zones parsed and added to the thermal framework and remove them
- * from the system, together with their resources.
- *
- */
-static __init void of_thermal_destroy_zones(void)
-{
-       struct device_node *np, *child;
-
-       np = of_find_node_by_name(NULL, "thermal-zones");
-       if (!np) {
-               pr_debug("unable to find thermal zones\n");
-               return;
-       }
-
-       for_each_available_child_of_node(np, child) {
-               struct thermal_zone_device *zone;
-
-               zone = thermal_zone_get_zone_by_name(child->name);
-               if (IS_ERR(zone))
-                       continue;
-
-               thermal_zone_device_unregister(zone);
-               kfree(zone->tzp);
-               kfree(zone->ops);
-               of_thermal_free_zone(zone->devdata);
-       }
-       of_node_put(np);
-}
-
-/**
- * of_parse_thermal_zones - parse device tree thermal data
- *
- * Initialization function that can be called by machine initialization
- * code to parse thermal data and populate the thermal framework
- * with hardware thermal zones info. This function only parses thermal zones.
- * Cooling devices and sensor devices nodes are supposed to be parsed
- * by their respective drivers.
- *
- * Return: 0 on success, proper error code otherwise
- *
- */
-int __init of_parse_thermal_zones(void)
-{
-       struct device_node *np, *child;
-       struct __thermal_zone *tz;
-       struct thermal_zone_device_ops *ops;
-
-       np = of_find_node_by_name(NULL, "thermal-zones");
-       if (!np) {
-               pr_debug("unable to find thermal zones\n");
-               return 0; /* Run successfully on systems without thermal DT */
-       }
-
-       for_each_available_child_of_node(np, child) {
-               struct thermal_zone_device *zone;
-               struct thermal_zone_params *tzp;
-               int i, mask = 0;
-               u32 prop;
-
-               tz = thermal_of_build_thermal_zone(child);
-               if (IS_ERR(tz)) {
-                       pr_err("failed to build thermal zone %pOFn: %ld\n",
-                              child,
-                              PTR_ERR(tz));
-                       continue;
-               }
-
-               ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
-               if (!ops)
-                       goto exit_free;
-
-               tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
-               if (!tzp) {
-                       kfree(ops);
-                       goto exit_free;
-               }
-
-               /* No hwmon because there might be hwmon drivers registering */
-               tzp->no_hwmon = true;
-
-               if (!of_property_read_u32(child, "sustainable-power", &prop))
-                       tzp->sustainable_power = prop;
-
-               for (i = 0; i < tz->ntrips; i++)
-                       mask |= 1 << i;
-
-               /* these two are left for temperature drivers to use */
-               tzp->slope = tz->slope;
-               tzp->offset = tz->offset;
-
-               zone = thermal_zone_device_register(child->name, tz->ntrips,
-                                                   mask, tz,
-                                                   ops, tzp,
-                                                   tz->passive_delay,
-                                                   tz->polling_delay);
-               if (IS_ERR(zone)) {
-                       pr_err("Failed to build %pOFn zone %ld\n", child,
-                              PTR_ERR(zone));
-                       kfree(tzp);
-                       kfree(ops);
-                       of_thermal_free_zone(tz);
-                       /* attempting to build remaining zones still */
-               }
-       }
-       of_node_put(np);
-
-       return 0;
-
-exit_free:
-       of_node_put(child);
-       of_node_put(np);
-       of_thermal_free_zone(tz);
-
-       /* no memory available, so free what we have built */
-       of_thermal_destroy_zones();
-
-       return -ENOMEM;
-}
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
deleted file mode 100644 (file)
index 4463647..0000000
+++ /dev/null
@@ -1,654 +0,0 @@
-/*
- * A power allocator to manage temperature
- *
- * Copyright (C) 2014 ARM Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#define pr_fmt(fmt) "Power allocator: " fmt
-
-#include <linux/rculist.h>
-#include <linux/slab.h>
-#include <linux/thermal.h>
-
-#define CREATE_TRACE_POINTS
-#include <trace/events/thermal_power_allocator.h>
-
-#include "thermal_core.h"
-
-#define INVALID_TRIP -1
-
-#define FRAC_BITS 10
-#define int_to_frac(x) ((x) << FRAC_BITS)
-#define frac_to_int(x) ((x) >> FRAC_BITS)
-
-/**
- * mul_frac() - multiply two fixed-point numbers
- * @x: first multiplicand
- * @y: second multiplicand
- *
- * Return: the result of multiplying two fixed-point numbers.  The
- * result is also a fixed-point number.
- */
-static inline s64 mul_frac(s64 x, s64 y)
-{
-       return (x * y) >> FRAC_BITS;
-}
-
-/**
- * div_frac() - divide two fixed-point numbers
- * @x: the dividend
- * @y: the divisor
- *
- * Return: the result of dividing two fixed-point numbers.  The
- * result is also a fixed-point number.
- */
-static inline s64 div_frac(s64 x, s64 y)
-{
-       return div_s64(x << FRAC_BITS, y);
-}
-
-/**
- * struct power_allocator_params - parameters for the power allocator governor
- * @allocated_tzp:     whether we have allocated tzp for this thermal zone and
- *                     it needs to be freed on unbind
- * @err_integral:      accumulated error in the PID controller.
- * @prev_err:  error in the previous iteration of the PID controller.
- *             Used to calculate the derivative term.
- * @trip_switch_on:    first passive trip point of the thermal zone.  The
- *                     governor switches on when this trip point is crossed.
- *                     If the thermal zone only has one passive trip point,
- *                     @trip_switch_on should be INVALID_TRIP.
- * @trip_max_desired_temperature:      last passive trip point of the thermal
- *                                     zone.  The temperature we are
- *                                     controlling for.
- */
-struct power_allocator_params {
-       bool allocated_tzp;
-       s64 err_integral;
-       s32 prev_err;
-       int trip_switch_on;
-       int trip_max_desired_temperature;
-};
-
-/**
- * estimate_sustainable_power() - Estimate the sustainable power of a thermal zone
- * @tz: thermal zone we are operating in
- *
- * For thermal zones that don't provide a sustainable_power in their
- * thermal_zone_params, estimate one.  Calculate it using the minimum
- * power of all the cooling devices as that gives a valid value that
- * can give some degree of functionality.  For optimal performance of
- * this governor, provide a sustainable_power in the thermal zone's
- * thermal_zone_params.
- */
-static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
-{
-       u32 sustainable_power = 0;
-       struct thermal_instance *instance;
-       struct power_allocator_params *params = tz->governor_data;
-
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               struct thermal_cooling_device *cdev = instance->cdev;
-               u32 min_power;
-
-               if (instance->trip != params->trip_max_desired_temperature)
-                       continue;
-
-               if (power_actor_get_min_power(cdev, tz, &min_power))
-                       continue;
-
-               sustainable_power += min_power;
-       }
-
-       return sustainable_power;
-}
-
-/**
- * estimate_pid_constants() - Estimate the constants for the PID controller
- * @tz:                thermal zone for which to estimate the constants
- * @sustainable_power: sustainable power for the thermal zone
- * @trip_switch_on:    trip point number for the switch on temperature
- * @control_temp:      target temperature for the power allocator governor
- * @force:     whether to force the update of the constants
- *
- * This function is used to update the estimation of the PID
- * controller constants in struct thermal_zone_parameters.
- * Sustainable power is provided in case it was estimated.  The
- * estimated sustainable_power should not be stored in the
- * thermal_zone_parameters so it has to be passed explicitly to this
- * function.
- *
- * If @force is not set, the values in the thermal zone's parameters
- * are preserved if they are not zero.  If @force is set, the values
- * in thermal zone's parameters are overwritten.
- */
-static void estimate_pid_constants(struct thermal_zone_device *tz,
-                                  u32 sustainable_power, int trip_switch_on,
-                                  int control_temp, bool force)
-{
-       int ret;
-       int switch_on_temp;
-       u32 temperature_threshold;
-
-       ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp);
-       if (ret)
-               switch_on_temp = 0;
-
-       temperature_threshold = control_temp - switch_on_temp;
-       /*
-        * estimate_pid_constants() tries to find appropriate default
-        * values for thermal zones that don't provide them. If a
-        * system integrator has configured a thermal zone with two
-        * passive trip points at the same temperature, that person
-        * hasn't put any effort to set up the thermal zone properly
-        * so just give up.
-        */
-       if (!temperature_threshold)
-               return;
-
-       if (!tz->tzp->k_po || force)
-               tz->tzp->k_po = int_to_frac(sustainable_power) /
-                       temperature_threshold;
-
-       if (!tz->tzp->k_pu || force)
-               tz->tzp->k_pu = int_to_frac(2 * sustainable_power) /
-                       temperature_threshold;
-
-       if (!tz->tzp->k_i || force)
-               tz->tzp->k_i = int_to_frac(10) / 1000;
-       /*
-        * The default for k_d and integral_cutoff is 0, so we can
-        * leave them as they are.
-        */
-}
-
-/**
- * pid_controller() - PID controller
- * @tz:        thermal zone we are operating in
- * @control_temp:      the target temperature in millicelsius
- * @max_allocatable_power:     maximum allocatable power for this thermal zone
- *
- * This PID controller increases the available power budget so that the
- * temperature of the thermal zone gets as close as possible to
- * @control_temp and limits the power if it exceeds it.  k_po is the
- * proportional term when we are overshooting, k_pu is the
- * proportional term when we are undershooting.  integral_cutoff is a
- * threshold below which we stop accumulating the error.  The
- * accumulated error is only valid if the requested power will make
- * the system warmer.  If the system is mostly idle, there's no point
- * in accumulating positive error.
- *
- * Return: The power budget for the next period.
- */
-static u32 pid_controller(struct thermal_zone_device *tz,
-                         int control_temp,
-                         u32 max_allocatable_power)
-{
-       s64 p, i, d, power_range;
-       s32 err, max_power_frac;
-       u32 sustainable_power;
-       struct power_allocator_params *params = tz->governor_data;
-
-       max_power_frac = int_to_frac(max_allocatable_power);
-
-       if (tz->tzp->sustainable_power) {
-               sustainable_power = tz->tzp->sustainable_power;
-       } else {
-               sustainable_power = estimate_sustainable_power(tz);
-               estimate_pid_constants(tz, sustainable_power,
-                                      params->trip_switch_on, control_temp,
-                                      true);
-       }
-
-       err = control_temp - tz->temperature;
-       err = int_to_frac(err);
-
-       /* Calculate the proportional term */
-       p = mul_frac(err < 0 ? tz->tzp->k_po : tz->tzp->k_pu, err);
-
-       /*
-        * Calculate the integral term
-        *
-        * if the error is less than cut off allow integration (but
-        * the integral is limited to max power)
-        */
-       i = mul_frac(tz->tzp->k_i, params->err_integral);
-
-       if (err < int_to_frac(tz->tzp->integral_cutoff)) {
-               s64 i_next = i + mul_frac(tz->tzp->k_i, err);
-
-               if (abs(i_next) < max_power_frac) {
-                       i = i_next;
-                       params->err_integral += err;
-               }
-       }
-
-       /*
-        * Calculate the derivative term
-        *
-        * We do err - prev_err, so with a positive k_d, a decreasing
-        * error (i.e. driving closer to the line) results in less
-        * power being applied, slowing down the controller)
-        */
-       d = mul_frac(tz->tzp->k_d, err - params->prev_err);
-       d = div_frac(d, tz->passive_delay);
-       params->prev_err = err;
-
-       power_range = p + i + d;
-
-       /* feed-forward the known sustainable dissipatable power */
-       power_range = sustainable_power + frac_to_int(power_range);
-
-       power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power);
-
-       trace_thermal_power_allocator_pid(tz, frac_to_int(err),
-                                         frac_to_int(params->err_integral),
-                                         frac_to_int(p), frac_to_int(i),
-                                         frac_to_int(d), power_range);
-
-       return power_range;
-}
-
-/**
- * divvy_up_power() - divvy the allocated power between the actors
- * @req_power: each actor's requested power
- * @max_power: each actor's maximum available power
- * @num_actors:        size of the @req_power, @max_power and @granted_power's array
- * @total_req_power: sum of @req_power
- * @power_range:       total allocated power
- * @granted_power:     output array: each actor's granted power
- * @extra_actor_power: an appropriately sized array to be used in the
- *                     function as temporary storage of the extra power given
- *                     to the actors
- *
- * This function divides the total allocated power (@power_range)
- * fairly between the actors.  It first tries to give each actor a
- * share of the @power_range according to how much power it requested
- * compared to the rest of the actors.  For example, if only one actor
- * requests power, then it receives all the @power_range.  If
- * three actors each requests 1mW, each receives a third of the
- * @power_range.
- *
- * If any actor received more than their maximum power, then that
- * surplus is re-divvied among the actors based on how far they are
- * from their respective maximums.
- *
- * Granted power for each actor is written to @granted_power, which
- * should've been allocated by the calling function.
- */
-static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
-                          u32 total_req_power, u32 power_range,
-                          u32 *granted_power, u32 *extra_actor_power)
-{
-       u32 extra_power, capped_extra_power;
-       int i;
-
-       /*
-        * Prevent division by 0 if none of the actors request power.
-        */
-       if (!total_req_power)
-               total_req_power = 1;
-
-       capped_extra_power = 0;
-       extra_power = 0;
-       for (i = 0; i < num_actors; i++) {
-               u64 req_range = (u64)req_power[i] * power_range;
-
-               granted_power[i] = DIV_ROUND_CLOSEST_ULL(req_range,
-                                                        total_req_power);
-
-               if (granted_power[i] > max_power[i]) {
-                       extra_power += granted_power[i] - max_power[i];
-                       granted_power[i] = max_power[i];
-               }
-
-               extra_actor_power[i] = max_power[i] - granted_power[i];
-               capped_extra_power += extra_actor_power[i];
-       }
-
-       if (!extra_power)
-               return;
-
-       /*
-        * Re-divvy the reclaimed extra among actors based on
-        * how far they are from the max
-        */
-       extra_power = min(extra_power, capped_extra_power);
-       if (capped_extra_power > 0)
-               for (i = 0; i < num_actors; i++)
-                       granted_power[i] += (extra_actor_power[i] *
-                                       extra_power) / capped_extra_power;
-}
-
-static int allocate_power(struct thermal_zone_device *tz,
-                         int control_temp)
-{
-       struct thermal_instance *instance;
-       struct power_allocator_params *params = tz->governor_data;
-       u32 *req_power, *max_power, *granted_power, *extra_actor_power;
-       u32 *weighted_req_power;
-       u32 total_req_power, max_allocatable_power, total_weighted_req_power;
-       u32 total_granted_power, power_range;
-       int i, num_actors, total_weight, ret = 0;
-       int trip_max_desired_temperature = params->trip_max_desired_temperature;
-
-       mutex_lock(&tz->lock);
-
-       num_actors = 0;
-       total_weight = 0;
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               if ((instance->trip == trip_max_desired_temperature) &&
-                   cdev_is_power_actor(instance->cdev)) {
-                       num_actors++;
-                       total_weight += instance->weight;
-               }
-       }
-
-       if (!num_actors) {
-               ret = -ENODEV;
-               goto unlock;
-       }
-
-       /*
-        * We need to allocate five arrays of the same size:
-        * req_power, max_power, granted_power, extra_actor_power and
-        * weighted_req_power.  They are going to be needed until this
-        * function returns.  Allocate them all in one go to simplify
-        * the allocation and deallocation logic.
-        */
-       BUILD_BUG_ON(sizeof(*req_power) != sizeof(*max_power));
-       BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power));
-       BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power));
-       BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power));
-       req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL);
-       if (!req_power) {
-               ret = -ENOMEM;
-               goto unlock;
-       }
-
-       max_power = &req_power[num_actors];
-       granted_power = &req_power[2 * num_actors];
-       extra_actor_power = &req_power[3 * num_actors];
-       weighted_req_power = &req_power[4 * num_actors];
-
-       i = 0;
-       total_weighted_req_power = 0;
-       total_req_power = 0;
-       max_allocatable_power = 0;
-
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               int weight;
-               struct thermal_cooling_device *cdev = instance->cdev;
-
-               if (instance->trip != trip_max_desired_temperature)
-                       continue;
-
-               if (!cdev_is_power_actor(cdev))
-                       continue;
-
-               if (cdev->ops->get_requested_power(cdev, tz, &req_power[i]))
-                       continue;
-
-               if (!total_weight)
-                       weight = 1 << FRAC_BITS;
-               else
-                       weight = instance->weight;
-
-               weighted_req_power[i] = frac_to_int(weight * req_power[i]);
-
-               if (power_actor_get_max_power(cdev, tz, &max_power[i]))
-                       continue;
-
-               total_req_power += req_power[i];
-               max_allocatable_power += max_power[i];
-               total_weighted_req_power += weighted_req_power[i];
-
-               i++;
-       }
-
-       power_range = pid_controller(tz, control_temp, max_allocatable_power);
-
-       divvy_up_power(weighted_req_power, max_power, num_actors,
-                      total_weighted_req_power, power_range, granted_power,
-                      extra_actor_power);
-
-       total_granted_power = 0;
-       i = 0;
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               if (instance->trip != trip_max_desired_temperature)
-                       continue;
-
-               if (!cdev_is_power_actor(instance->cdev))
-                       continue;
-
-               power_actor_set_power(instance->cdev, instance,
-                                     granted_power[i]);
-               total_granted_power += granted_power[i];
-
-               i++;
-       }
-
-       trace_thermal_power_allocator(tz, req_power, total_req_power,
-                                     granted_power, total_granted_power,
-                                     num_actors, power_range,
-                                     max_allocatable_power, tz->temperature,
-                                     control_temp - tz->temperature);
-
-       kfree(req_power);
-unlock:
-       mutex_unlock(&tz->lock);
-
-       return ret;
-}
-
-/**
- * get_governor_trips() - get the number of the two trip points that are key for this governor
- * @tz:        thermal zone to operate on
- * @params:    pointer to private data for this governor
- *
- * The power allocator governor works optimally with two trips points:
- * a "switch on" trip point and a "maximum desired temperature".  These
- * are defined as the first and last passive trip points.
- *
- * If there is only one trip point, then that's considered to be the
- * "maximum desired temperature" trip point and the governor is always
- * on.  If there are no passive or active trip points, then the
- * governor won't do anything.  In fact, its throttle function
- * won't be called at all.
- */
-static void get_governor_trips(struct thermal_zone_device *tz,
-                              struct power_allocator_params *params)
-{
-       int i, last_active, last_passive;
-       bool found_first_passive;
-
-       found_first_passive = false;
-       last_active = INVALID_TRIP;
-       last_passive = INVALID_TRIP;
-
-       for (i = 0; i < tz->trips; i++) {
-               enum thermal_trip_type type;
-               int ret;
-
-               ret = tz->ops->get_trip_type(tz, i, &type);
-               if (ret) {
-                       dev_warn(&tz->device,
-                                "Failed to get trip point %d type: %d\n", i,
-                                ret);
-                       continue;
-               }
-
-               if (type == THERMAL_TRIP_PASSIVE) {
-                       if (!found_first_passive) {
-                               params->trip_switch_on = i;
-                               found_first_passive = true;
-                       } else  {
-                               last_passive = i;
-                       }
-               } else if (type == THERMAL_TRIP_ACTIVE) {
-                       last_active = i;
-               } else {
-                       break;
-               }
-       }
-
-       if (last_passive != INVALID_TRIP) {
-               params->trip_max_desired_temperature = last_passive;
-       } else if (found_first_passive) {
-               params->trip_max_desired_temperature = params->trip_switch_on;
-               params->trip_switch_on = INVALID_TRIP;
-       } else {
-               params->trip_switch_on = INVALID_TRIP;
-               params->trip_max_desired_temperature = last_active;
-       }
-}
-
-static void reset_pid_controller(struct power_allocator_params *params)
-{
-       params->err_integral = 0;
-       params->prev_err = 0;
-}
-
-static void allow_maximum_power(struct thermal_zone_device *tz)
-{
-       struct thermal_instance *instance;
-       struct power_allocator_params *params = tz->governor_data;
-
-       mutex_lock(&tz->lock);
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               if ((instance->trip != params->trip_max_desired_temperature) ||
-                   (!cdev_is_power_actor(instance->cdev)))
-                       continue;
-
-               instance->target = 0;
-               mutex_lock(&instance->cdev->lock);
-               instance->cdev->updated = false;
-               mutex_unlock(&instance->cdev->lock);
-               thermal_cdev_update(instance->cdev);
-       }
-       mutex_unlock(&tz->lock);
-}
-
-/**
- * power_allocator_bind() - bind the power_allocator governor to a thermal zone
- * @tz:        thermal zone to bind it to
- *
- * Initialize the PID controller parameters and bind it to the thermal
- * zone.
- *
- * Return: 0 on success, or -ENOMEM if we ran out of memory.
- */
-static int power_allocator_bind(struct thermal_zone_device *tz)
-{
-       int ret;
-       struct power_allocator_params *params;
-       int control_temp;
-
-       params = kzalloc(sizeof(*params), GFP_KERNEL);
-       if (!params)
-               return -ENOMEM;
-
-       if (!tz->tzp) {
-               tz->tzp = kzalloc(sizeof(*tz->tzp), GFP_KERNEL);
-               if (!tz->tzp) {
-                       ret = -ENOMEM;
-                       goto free_params;
-               }
-
-               params->allocated_tzp = true;
-       }
-
-       if (!tz->tzp->sustainable_power)
-               dev_warn(&tz->device, "power_allocator: sustainable_power will be estimated\n");
-
-       get_governor_trips(tz, params);
-
-       if (tz->trips > 0) {
-               ret = tz->ops->get_trip_temp(tz,
-                                       params->trip_max_desired_temperature,
-                                       &control_temp);
-               if (!ret)
-                       estimate_pid_constants(tz, tz->tzp->sustainable_power,
-                                              params->trip_switch_on,
-                                              control_temp, false);
-       }
-
-       reset_pid_controller(params);
-
-       tz->governor_data = params;
-
-       return 0;
-
-free_params:
-       kfree(params);
-
-       return ret;
-}
-
-static void power_allocator_unbind(struct thermal_zone_device *tz)
-{
-       struct power_allocator_params *params = tz->governor_data;
-
-       dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id);
-
-       if (params->allocated_tzp) {
-               kfree(tz->tzp);
-               tz->tzp = NULL;
-       }
-
-       kfree(tz->governor_data);
-       tz->governor_data = NULL;
-}
-
-static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
-{
-       int ret;
-       int switch_on_temp, control_temp;
-       struct power_allocator_params *params = tz->governor_data;
-
-       /*
-        * We get called for every trip point but we only need to do
-        * our calculations once
-        */
-       if (trip != params->trip_max_desired_temperature)
-               return 0;
-
-       ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
-                                    &switch_on_temp);
-       if (!ret && (tz->temperature < switch_on_temp)) {
-               tz->passive = 0;
-               reset_pid_controller(params);
-               allow_maximum_power(tz);
-               return 0;
-       }
-
-       tz->passive = 1;
-
-       ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature,
-                               &control_temp);
-       if (ret) {
-               dev_warn(&tz->device,
-                        "Failed to get the maximum desired temperature: %d\n",
-                        ret);
-               return ret;
-       }
-
-       return allocate_power(tz, control_temp);
-}
-
-static struct thermal_governor thermal_gov_power_allocator = {
-       .name           = "power_allocator",
-       .bind_to_tz     = power_allocator_bind,
-       .unbind_from_tz = power_allocator_unbind,
-       .throttle       = power_allocator_throttle,
-};
-THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator);
index 7c8dc6e..ec86eef 100644 (file)
@@ -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 (file)
index 1725453..0000000
+++ /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;
-}
index 2f77d23..8d3e94d 100644 (file)
 // 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;
index 502acf0..59d0116 100644 (file)
@@ -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;
index 028a6bb..73049f9 100644 (file)
@@ -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)),
index e0c1f24..46aeb28 100644 (file)
@@ -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,
index 7c1a8bc..15a71ec 100644 (file)
@@ -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);
index a824b78..a011445 100644 (file)
@@ -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,
index 9314e3d..331e2b7 100644 (file)
@@ -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/step_wise.c b/drivers/thermal/step_wise.c
deleted file mode 100644 (file)
index 2ae7198..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *  step_wise.c - A step-by-step Thermal throttling governor
- *
- *  Copyright (C) 2012 Intel Corp
- *  Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
- *
- *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/thermal.h>
-#include <trace/events/thermal.h>
-
-#include "thermal_core.h"
-
-/*
- * If the temperature is higher than a trip point,
- *    a. if the trend is THERMAL_TREND_RAISING, use higher cooling
- *       state for this trip point
- *    b. if the trend is THERMAL_TREND_DROPPING, do nothing
- *    c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit
- *       for this trip point
- *    d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit
- *       for this trip point
- * If the temperature is lower than a trip point,
- *    a. if the trend is THERMAL_TREND_RAISING, do nothing
- *    b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
- *       state for this trip point, if the cooling state already
- *       equals lower limit, deactivate the thermal instance
- *    c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing
- *    d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit,
- *       if the cooling state already equals lower limit,
- *       deactivate the thermal instance
- */
-static unsigned long get_target_state(struct thermal_instance *instance,
-                               enum thermal_trend trend, bool throttle)
-{
-       struct thermal_cooling_device *cdev = instance->cdev;
-       unsigned long cur_state;
-       unsigned long next_target;
-
-       /*
-        * We keep this instance the way it is by default.
-        * Otherwise, we use the current state of the
-        * cdev in use to determine the next_target.
-        */
-       cdev->ops->get_cur_state(cdev, &cur_state);
-       next_target = instance->target;
-       dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state);
-
-       if (!instance->initialized) {
-               if (throttle) {
-                       next_target = (cur_state + 1) >= instance->upper ?
-                                       instance->upper :
-                                       ((cur_state + 1) < instance->lower ?
-                                       instance->lower : (cur_state + 1));
-               } else {
-                       next_target = THERMAL_NO_TARGET;
-               }
-
-               return next_target;
-       }
-
-       switch (trend) {
-       case THERMAL_TREND_RAISING:
-               if (throttle) {
-                       next_target = cur_state < instance->upper ?
-                                   (cur_state + 1) : instance->upper;
-                       if (next_target < instance->lower)
-                               next_target = instance->lower;
-               }
-               break;
-       case THERMAL_TREND_RAISE_FULL:
-               if (throttle)
-                       next_target = instance->upper;
-               break;
-       case THERMAL_TREND_DROPPING:
-               if (cur_state <= instance->lower) {
-                       if (!throttle)
-                               next_target = THERMAL_NO_TARGET;
-               } else {
-                       if (!throttle) {
-                               next_target = cur_state - 1;
-                               if (next_target > instance->upper)
-                                       next_target = instance->upper;
-                       }
-               }
-               break;
-       case THERMAL_TREND_DROP_FULL:
-               if (cur_state == instance->lower) {
-                       if (!throttle)
-                               next_target = THERMAL_NO_TARGET;
-               } else
-                       next_target = instance->lower;
-               break;
-       default:
-               break;
-       }
-
-       return next_target;
-}
-
-static void update_passive_instance(struct thermal_zone_device *tz,
-                               enum thermal_trip_type type, int value)
-{
-       /*
-        * If value is +1, activate a passive instance.
-        * If value is -1, deactivate a passive instance.
-        */
-       if (type == THERMAL_TRIP_PASSIVE || type == THERMAL_TRIPS_NONE)
-               tz->passive += value;
-}
-
-static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
-{
-       int trip_temp;
-       enum thermal_trip_type trip_type;
-       enum thermal_trend trend;
-       struct thermal_instance *instance;
-       bool throttle = false;
-       int old_target;
-
-       if (trip == THERMAL_TRIPS_NONE) {
-               trip_temp = tz->forced_passive;
-               trip_type = THERMAL_TRIPS_NONE;
-       } else {
-               tz->ops->get_trip_temp(tz, trip, &trip_temp);
-               tz->ops->get_trip_type(tz, trip, &trip_type);
-       }
-
-       trend = get_tz_trend(tz, trip);
-
-       if (tz->temperature >= trip_temp) {
-               throttle = true;
-               trace_thermal_zone_trip(tz, trip, trip_type);
-       }
-
-       dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
-                               trip, trip_type, trip_temp, trend, throttle);
-
-       mutex_lock(&tz->lock);
-
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
-               if (instance->trip != trip)
-                       continue;
-
-               old_target = instance->target;
-               instance->target = get_target_state(instance, trend, throttle);
-               dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
-                                       old_target, (int)instance->target);
-
-               if (instance->initialized && old_target == instance->target)
-                       continue;
-
-               /* Activate a passive thermal instance */
-               if (old_target == THERMAL_NO_TARGET &&
-                       instance->target != THERMAL_NO_TARGET)
-                       update_passive_instance(tz, trip_type, 1);
-               /* Deactivate a passive thermal instance */
-               else if (old_target != THERMAL_NO_TARGET &&
-                       instance->target == THERMAL_NO_TARGET)
-                       update_passive_instance(tz, trip_type, -1);
-
-               instance->initialized = true;
-               mutex_lock(&instance->cdev->lock);
-               instance->cdev->updated = false; /* cdev needs update */
-               mutex_unlock(&instance->cdev->lock);
-       }
-
-       mutex_unlock(&tz->lock);
-}
-
-/**
- * step_wise_throttle - throttles devices associated with the given zone
- * @tz: thermal_zone_device
- * @trip: trip point index
- *
- * Throttling Logic: This uses the trend of the thermal zone to throttle.
- * If the thermal zone is 'heating up' this throttles all the cooling
- * devices associated with the zone and its particular trip point, by one
- * step. If the zone is 'cooling down' it brings back the performance of
- * the devices by one step.
- */
-static int step_wise_throttle(struct thermal_zone_device *tz, int trip)
-{
-       struct thermal_instance *instance;
-
-       thermal_zone_trip_update(tz, trip);
-
-       if (tz->forced_passive)
-               thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE);
-
-       mutex_lock(&tz->lock);
-
-       list_for_each_entry(instance, &tz->thermal_instances, tz_node)
-               thermal_cdev_update(instance->cdev);
-
-       mutex_unlock(&tz->lock);
-
-       return 0;
-}
-
-static struct thermal_governor thermal_gov_step_wise = {
-       .name           = "step_wise",
-       .throttle       = step_wise_throttle,
-};
-THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise);
index 9a321dc..b71196e 100644 (file)
@@ -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>
 #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)
index a9bf00e..c956895 100644 (file)
 #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 *);
index 2ba756a..87b1256 100644 (file)
 
 #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)
 {
index c8d2620..8b92e00 100644 (file)
  *  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/thermal_of.c b/drivers/thermal/thermal_of.c
new file mode 100644 (file)
index 0000000..ddf88db
--- /dev/null
@@ -0,0 +1,1151 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  of-thermal.c - Generic Thermal Management device tree support.
+ *
+ *  Copyright (C) 2013 Texas Instruments
+ *  Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#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"
+
+/***   Private data structures to represent thermal device tree data ***/
+
+/**
+ * struct __thermal_cooling_bind_param - a cooling device for a trip point
+ * @cooling_device: a pointer to identify the referred cooling device
+ * @min: minimum cooling state used at this trip point
+ * @max: maximum cooling state used at this trip point
+ */
+
+struct __thermal_cooling_bind_param {
+       struct device_node *cooling_device;
+       unsigned long min;
+       unsigned long max;
+};
+
+/**
+ * struct __thermal_bind_param - a match between trip and cooling device
+ * @tcbp: a pointer to an array of cooling devices
+ * @count: number of elements in array
+ * @trip_id: the trip point index
+ * @usage: the percentage (from 0 to 100) of cooling contribution
+ */
+
+struct __thermal_bind_params {
+       struct __thermal_cooling_bind_param *tcbp;
+       unsigned int count;
+       unsigned int trip_id;
+       unsigned int usage;
+};
+
+/**
+ * struct __thermal_zone - internal representation of a thermal zone
+ * @mode: current thermal zone device mode (enabled/disabled)
+ * @passive_delay: polling interval while passive cooling is activated
+ * @polling_delay: zone polling interval
+ * @slope: slope of the temperature adjustment curve
+ * @offset: offset of the temperature adjustment curve
+ * @ntrips: number of trip points
+ * @trips: an array of trip points (0..ntrips - 1)
+ * @num_tbps: number of thermal bind params
+ * @tbps: an array of thermal bind params (0..num_tbps - 1)
+ * @sensor_data: sensor private data used while reading temperature and trend
+ * @ops: set of callbacks to handle the thermal zone based on DT
+ */
+
+struct __thermal_zone {
+       enum thermal_device_mode mode;
+       int passive_delay;
+       int polling_delay;
+       int slope;
+       int offset;
+
+       /* trip data */
+       int ntrips;
+       struct thermal_trip *trips;
+
+       /* cooling binding data */
+       int num_tbps;
+       struct __thermal_bind_params *tbps;
+
+       /* sensor interface */
+       void *sensor_data;
+       const struct thermal_zone_of_device_ops *ops;
+};
+
+/***   DT thermal zone device callbacks   ***/
+
+static int of_thermal_get_temp(struct thermal_zone_device *tz,
+                              int *temp)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (!data->ops->get_temp)
+               return -EINVAL;
+
+       return data->ops->get_temp(data->sensor_data, temp);
+}
+
+static int of_thermal_set_trips(struct thermal_zone_device *tz,
+                               int low, int high)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (!data->ops || !data->ops->set_trips)
+               return -EINVAL;
+
+       return data->ops->set_trips(data->sensor_data, low, high);
+}
+
+/**
+ * of_thermal_get_ntrips - function to export number of available trip
+ *                        points.
+ * @tz: pointer to a thermal zone
+ *
+ * This function is a globally visible wrapper to get number of trip points
+ * stored in the local struct __thermal_zone
+ *
+ * Return: number of available trip points, -ENODEV when data not available
+ */
+int of_thermal_get_ntrips(struct thermal_zone_device *tz)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (!data || IS_ERR(data))
+               return -ENODEV;
+
+       return data->ntrips;
+}
+EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
+
+/**
+ * of_thermal_is_trip_valid - function to check if trip point is valid
+ *
+ * @tz:        pointer to a thermal zone
+ * @trip:      trip point to evaluate
+ *
+ * This function is responsible for checking if passed trip point is valid
+ *
+ * Return: true if trip point is valid, false otherwise
+ */
+bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (!data || trip >= data->ntrips || trip < 0)
+               return false;
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
+
+/**
+ * of_thermal_get_trip_points - function to get access to a globally exported
+ *                             trip points
+ *
+ * @tz:        pointer to a thermal zone
+ *
+ * This function provides a pointer to trip points table
+ *
+ * Return: pointer to trip points table, NULL otherwise
+ */
+const struct thermal_trip *
+of_thermal_get_trip_points(struct thermal_zone_device *tz)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (!data)
+               return NULL;
+
+       return data->trips;
+}
+EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
+
+/**
+ * of_thermal_set_emul_temp - function to set emulated temperature
+ *
+ * @tz:        pointer to a thermal zone
+ * @temp:      temperature to set
+ *
+ * This function gives the ability to set emulated value of temperature,
+ * which is handy for debugging
+ *
+ * Return: zero on success, error code otherwise
+ */
+static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
+                                   int temp)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       return data->ops->set_emul_temp(data->sensor_data, temp);
+}
+
+static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
+                               enum thermal_trend *trend)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (!data->ops->get_trend)
+               return -EINVAL;
+
+       return data->ops->get_trend(data->sensor_data, trip, trend);
+}
+
+static int of_thermal_bind(struct thermal_zone_device *thermal,
+                          struct thermal_cooling_device *cdev)
+{
+       struct __thermal_zone *data = thermal->devdata;
+       struct __thermal_bind_params *tbp;
+       struct __thermal_cooling_bind_param *tcbp;
+       int i, j;
+
+       if (!data || IS_ERR(data))
+               return -ENODEV;
+
+       /* find where to bind */
+       for (i = 0; i < data->num_tbps; i++) {
+               tbp = data->tbps + i;
+
+               for (j = 0; j < tbp->count; j++) {
+                       tcbp = tbp->tcbp + j;
+
+                       if (tcbp->cooling_device == cdev->np) {
+                               int ret;
+
+                               ret = thermal_zone_bind_cooling_device(thermal,
+                                               tbp->trip_id, cdev,
+                                               tcbp->max,
+                                               tcbp->min,
+                                               tbp->usage);
+                               if (ret)
+                                       return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int of_thermal_unbind(struct thermal_zone_device *thermal,
+                            struct thermal_cooling_device *cdev)
+{
+       struct __thermal_zone *data = thermal->devdata;
+       struct __thermal_bind_params *tbp;
+       struct __thermal_cooling_bind_param *tcbp;
+       int i, j;
+
+       if (!data || IS_ERR(data))
+               return -ENODEV;
+
+       /* find where to unbind */
+       for (i = 0; i < data->num_tbps; i++) {
+               tbp = data->tbps + i;
+
+               for (j = 0; j < tbp->count; j++) {
+                       tcbp = tbp->tcbp + j;
+
+                       if (tcbp->cooling_device == cdev->np) {
+                               int ret;
+
+                               ret = thermal_zone_unbind_cooling_device(thermal,
+                                                       tbp->trip_id, cdev);
+                               if (ret)
+                                       return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int of_thermal_get_mode(struct thermal_zone_device *tz,
+                              enum thermal_device_mode *mode)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       *mode = data->mode;
+
+       return 0;
+}
+
+static int of_thermal_set_mode(struct thermal_zone_device *tz,
+                              enum thermal_device_mode mode)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       mutex_lock(&tz->lock);
+
+       if (mode == THERMAL_DEVICE_ENABLED) {
+               tz->polling_delay = data->polling_delay;
+               tz->passive_delay = data->passive_delay;
+       } else {
+               tz->polling_delay = 0;
+               tz->passive_delay = 0;
+       }
+
+       mutex_unlock(&tz->lock);
+
+       data->mode = mode;
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
+
+       return 0;
+}
+
+static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
+                                   enum thermal_trip_type *type)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (trip >= data->ntrips || trip < 0)
+               return -EDOM;
+
+       *type = data->trips[trip].type;
+
+       return 0;
+}
+
+static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
+                                   int *temp)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (trip >= data->ntrips || trip < 0)
+               return -EDOM;
+
+       *temp = data->trips[trip].temperature;
+
+       return 0;
+}
+
+static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
+                                   int temp)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (trip >= data->ntrips || trip < 0)
+               return -EDOM;
+
+       if (data->ops->set_trip_temp) {
+               int ret;
+
+               ret = data->ops->set_trip_temp(data->sensor_data, trip, temp);
+               if (ret)
+                       return ret;
+       }
+
+       /* thermal framework should take care of data->mask & (1 << trip) */
+       data->trips[trip].temperature = temp;
+
+       return 0;
+}
+
+static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
+                                   int *hyst)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (trip >= data->ntrips || trip < 0)
+               return -EDOM;
+
+       *hyst = data->trips[trip].hysteresis;
+
+       return 0;
+}
+
+static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
+                                   int hyst)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (trip >= data->ntrips || trip < 0)
+               return -EDOM;
+
+       /* thermal framework should take care of data->mask & (1 << trip) */
+       data->trips[trip].hysteresis = hyst;
+
+       return 0;
+}
+
+static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
+                                   int *temp)
+{
+       struct __thermal_zone *data = tz->devdata;
+       int i;
+
+       for (i = 0; i < data->ntrips; i++)
+               if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
+                       *temp = data->trips[i].temperature;
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+
+static struct thermal_zone_device_ops of_thermal_ops = {
+       .get_mode = of_thermal_get_mode,
+       .set_mode = of_thermal_set_mode,
+
+       .get_trip_type = of_thermal_get_trip_type,
+       .get_trip_temp = of_thermal_get_trip_temp,
+       .set_trip_temp = of_thermal_set_trip_temp,
+       .get_trip_hyst = of_thermal_get_trip_hyst,
+       .set_trip_hyst = of_thermal_set_trip_hyst,
+       .get_crit_temp = of_thermal_get_crit_temp,
+
+       .bind = of_thermal_bind,
+       .unbind = of_thermal_unbind,
+};
+
+/***   sensor API   ***/
+
+static struct thermal_zone_device *
+thermal_zone_of_add_sensor(struct device_node *zone,
+                          struct device_node *sensor, void *data,
+                          const struct thermal_zone_of_device_ops *ops)
+{
+       struct thermal_zone_device *tzd;
+       struct __thermal_zone *tz;
+
+       tzd = thermal_zone_get_zone_by_name(zone->name);
+       if (IS_ERR(tzd))
+               return ERR_PTR(-EPROBE_DEFER);
+
+       tz = tzd->devdata;
+
+       if (!ops)
+               return ERR_PTR(-EINVAL);
+
+       mutex_lock(&tzd->lock);
+       tz->ops = ops;
+       tz->sensor_data = data;
+
+       tzd->ops->get_temp = of_thermal_get_temp;
+       tzd->ops->get_trend = of_thermal_get_trend;
+
+       /*
+        * The thermal zone core will calculate the window if they have set the
+        * optional set_trips pointer.
+        */
+       if (ops->set_trips)
+               tzd->ops->set_trips = of_thermal_set_trips;
+
+       if (ops->set_emul_temp)
+               tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
+
+       mutex_unlock(&tzd->lock);
+
+       return tzd;
+}
+
+/**
+ * thermal_zone_of_get_sensor_id - get sensor ID from a DT thermal zone
+ * @tz_np: a valid thermal zone device node.
+ * @sensor_np: a sensor node of a valid sensor device.
+ * @id: the sensor ID returned if success.
+ *
+ * This function will get sensor ID from a given thermal zone node and
+ * the sensor node must match the temperature provider @sensor_np.
+ *
+ * Return: 0 on success, proper error code otherwise.
+ */
+
+int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
+                                 struct device_node *sensor_np,
+                                 u32 *id)
+{
+       struct of_phandle_args sensor_specs;
+       int ret;
+
+       ret = of_parse_phandle_with_args(tz_np,
+                                        "thermal-sensors",
+                                        "#thermal-sensor-cells",
+                                        0,
+                                        &sensor_specs);
+       if (ret)
+               return ret;
+
+       if (sensor_specs.np != sensor_np) {
+               of_node_put(sensor_specs.np);
+               return -ENODEV;
+       }
+
+       if (sensor_specs.args_count > 1)
+               pr_warn("%pOFn: too many cells in sensor specifier %d\n",
+                    sensor_specs.np, sensor_specs.args_count);
+
+       *id = sensor_specs.args_count ? sensor_specs.args[0] : 0;
+
+       of_node_put(sensor_specs.np);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_zone_of_get_sensor_id);
+
+/**
+ * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
+ * @dev: a valid struct device pointer of a sensor device. Must contain
+ *       a valid .of_node, for the sensor node.
+ * @sensor_id: a sensor identifier, in case the sensor IP has more
+ *             than one sensors
+ * @data: a private pointer (owned by the caller) that will be passed
+ *        back, when a temperature reading is needed.
+ * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
+ *
+ * This function will search the list of thermal zones described in device
+ * tree and look for the zone that refer to the sensor device pointed by
+ * @dev->of_node as temperature providers. For the zone pointing to the
+ * sensor node, the sensor will be added to the DT thermal zone device.
+ *
+ * The thermal zone temperature is provided by the @get_temp function
+ * pointer. When called, it will have the private pointer @data back.
+ *
+ * The thermal zone temperature trend is provided by the @get_trend function
+ * pointer. When called, it will have the private pointer @data back.
+ *
+ * TODO:
+ * 01 - This function must enqueue the new sensor instead of using
+ * it as the only source of temperature values.
+ *
+ * 02 - There must be a way to match the sensor with all thermal zones
+ * that refer to it.
+ *
+ * Return: On success returns a valid struct thermal_zone_device,
+ * otherwise, it returns a corresponding ERR_PTR(). Caller must
+ * check the return value with help of IS_ERR() helper.
+ */
+struct thermal_zone_device *
+thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
+                               const struct thermal_zone_of_device_ops *ops)
+{
+       struct device_node *np, *child, *sensor_np;
+       struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
+
+       np = of_find_node_by_name(NULL, "thermal-zones");
+       if (!np)
+               return ERR_PTR(-ENODEV);
+
+       if (!dev || !dev->of_node) {
+               of_node_put(np);
+               return ERR_PTR(-ENODEV);
+       }
+
+       sensor_np = of_node_get(dev->of_node);
+
+       for_each_available_child_of_node(np, child) {
+               int ret, id;
+
+               /* For now, thermal framework supports only 1 sensor per zone */
+               ret = thermal_zone_of_get_sensor_id(child, sensor_np, &id);
+               if (ret)
+                       continue;
+
+               if (id == sensor_id) {
+                       tzd = thermal_zone_of_add_sensor(child, sensor_np,
+                                                        data, ops);
+                       if (!IS_ERR(tzd))
+                               tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
+
+                       of_node_put(child);
+                       goto exit;
+               }
+       }
+exit:
+       of_node_put(sensor_np);
+       of_node_put(np);
+
+       return tzd;
+}
+EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
+
+/**
+ * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
+ * @dev: a valid struct device pointer of a sensor device. Must contain
+ *       a valid .of_node, for the sensor node.
+ * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
+ *
+ * This function removes the sensor callbacks and private data from the
+ * thermal zone device registered with thermal_zone_of_sensor_register()
+ * API. It will also silent the zone by remove the .get_temp() and .get_trend()
+ * thermal zone device callbacks.
+ *
+ * TODO: When the support to several sensors per zone is added, this
+ * function must search the sensor list based on @dev parameter.
+ *
+ */
+void thermal_zone_of_sensor_unregister(struct device *dev,
+                                      struct thermal_zone_device *tzd)
+{
+       struct __thermal_zone *tz;
+
+       if (!dev || !tzd || !tzd->devdata)
+               return;
+
+       tz = tzd->devdata;
+
+       /* no __thermal_zone, nothing to be done */
+       if (!tz)
+               return;
+
+       mutex_lock(&tzd->lock);
+       tzd->ops->get_temp = NULL;
+       tzd->ops->get_trend = NULL;
+       tzd->ops->set_emul_temp = NULL;
+
+       tz->ops = NULL;
+       tz->sensor_data = NULL;
+       mutex_unlock(&tzd->lock);
+}
+EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
+
+static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
+{
+       thermal_zone_of_sensor_unregister(dev,
+                                         *(struct thermal_zone_device **)res);
+}
+
+static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
+                                            void *data)
+{
+       struct thermal_zone_device **r = res;
+
+       if (WARN_ON(!r || !*r))
+               return 0;
+
+       return *r == data;
+}
+
+/**
+ * devm_thermal_zone_of_sensor_register - Resource managed version of
+ *                             thermal_zone_of_sensor_register()
+ * @dev: a valid struct device pointer of a sensor device. Must contain
+ *       a valid .of_node, for the sensor node.
+ * @sensor_id: a sensor identifier, in case the sensor IP has more
+ *            than one sensors
+ * @data: a private pointer (owned by the caller) that will be passed
+ *       back, when a temperature reading is needed.
+ * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
+ *
+ * Refer thermal_zone_of_sensor_register() for more details.
+ *
+ * Return: On success returns a valid struct thermal_zone_device,
+ * otherwise, it returns a corresponding ERR_PTR(). Caller must
+ * check the return value with help of IS_ERR() helper.
+ * Registered thermal_zone_device device will automatically be
+ * released when device is unbounded.
+ */
+struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
+       struct device *dev, int sensor_id,
+       void *data, const struct thermal_zone_of_device_ops *ops)
+{
+       struct thermal_zone_device **ptr, *tzd;
+
+       ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
+                          GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
+       if (IS_ERR(tzd)) {
+               devres_free(ptr);
+               return tzd;
+       }
+
+       *ptr = tzd;
+       devres_add(dev, ptr);
+
+       return tzd;
+}
+EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
+
+/**
+ * devm_thermal_zone_of_sensor_unregister - Resource managed version of
+ *                             thermal_zone_of_sensor_unregister().
+ * @dev: Device for which which resource was allocated.
+ * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
+ *
+ * This function removes the sensor callbacks and private data from the
+ * thermal zone device registered with devm_thermal_zone_of_sensor_register()
+ * API. It will also silent the zone by remove the .get_temp() and .get_trend()
+ * thermal zone device callbacks.
+ * Normally this function will not need to be called and the resource
+ * management code will ensure that the resource is freed.
+ */
+void devm_thermal_zone_of_sensor_unregister(struct device *dev,
+                                           struct thermal_zone_device *tzd)
+{
+       WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
+                              devm_thermal_zone_of_sensor_match, tzd));
+}
+EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
+
+/***   functions parsing device tree nodes   ***/
+
+/**
+ * thermal_of_populate_bind_params - parse and fill cooling map data
+ * @np: DT node containing a cooling-map node
+ * @__tbp: data structure to be filled with cooling map info
+ * @trips: array of thermal zone trip points
+ * @ntrips: number of trip points inside trips.
+ *
+ * This function parses a cooling-map type of node represented by
+ * @np parameter and fills the read data into @__tbp data structure.
+ * It needs the already parsed array of trip points of the thermal zone
+ * in consideration.
+ *
+ * Return: 0 on success, proper error code otherwise
+ */
+static int thermal_of_populate_bind_params(struct device_node *np,
+                                          struct __thermal_bind_params *__tbp,
+                                          struct thermal_trip *trips,
+                                          int ntrips)
+{
+       struct of_phandle_args cooling_spec;
+       struct __thermal_cooling_bind_param *__tcbp;
+       struct device_node *trip;
+       int ret, i, count;
+       u32 prop;
+
+       /* Default weight. Usage is optional */
+       __tbp->usage = THERMAL_WEIGHT_DEFAULT;
+       ret = of_property_read_u32(np, "contribution", &prop);
+       if (ret == 0)
+               __tbp->usage = prop;
+
+       trip = of_parse_phandle(np, "trip", 0);
+       if (!trip) {
+               pr_err("missing trip property\n");
+               return -ENODEV;
+       }
+
+       /* match using device_node */
+       for (i = 0; i < ntrips; i++)
+               if (trip == trips[i].np) {
+                       __tbp->trip_id = i;
+                       break;
+               }
+
+       if (i == ntrips) {
+               ret = -ENODEV;
+               goto end;
+       }
+
+       count = of_count_phandle_with_args(np, "cooling-device",
+                                          "#cooling-cells");
+       if (!count) {
+               pr_err("Add a cooling_device property with at least one device\n");
+               goto end;
+       }
+
+       __tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL);
+       if (!__tcbp)
+               goto end;
+
+       for (i = 0; i < count; i++) {
+               ret = of_parse_phandle_with_args(np, "cooling-device",
+                               "#cooling-cells", i, &cooling_spec);
+               if (ret < 0) {
+                       pr_err("Invalid cooling-device entry\n");
+                       goto free_tcbp;
+               }
+
+               __tcbp[i].cooling_device = cooling_spec.np;
+
+               if (cooling_spec.args_count >= 2) { /* at least min and max */
+                       __tcbp[i].min = cooling_spec.args[0];
+                       __tcbp[i].max = cooling_spec.args[1];
+               } else {
+                       pr_err("wrong reference to cooling device, missing limits\n");
+               }
+       }
+
+       __tbp->tcbp = __tcbp;
+       __tbp->count = count;
+
+       goto end;
+
+free_tcbp:
+       for (i = i - 1; i >= 0; i--)
+               of_node_put(__tcbp[i].cooling_device);
+       kfree(__tcbp);
+end:
+       of_node_put(trip);
+
+       return ret;
+}
+
+/*
+ * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
+ * into the device tree binding of 'trip', property type.
+ */
+static const char * const trip_types[] = {
+       [THERMAL_TRIP_ACTIVE]   = "active",
+       [THERMAL_TRIP_PASSIVE]  = "passive",
+       [THERMAL_TRIP_HOT]      = "hot",
+       [THERMAL_TRIP_CRITICAL] = "critical",
+};
+
+/**
+ * thermal_of_get_trip_type - Get phy mode for given device_node
+ * @np:        Pointer to the given device_node
+ * @type: Pointer to resulting trip type
+ *
+ * The function gets trip type string from property 'type',
+ * and store its index in trip_types table in @type,
+ *
+ * Return: 0 on success, or errno in error case.
+ */
+static int thermal_of_get_trip_type(struct device_node *np,
+                                   enum thermal_trip_type *type)
+{
+       const char *t;
+       int err, i;
+
+       err = of_property_read_string(np, "type", &t);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < ARRAY_SIZE(trip_types); i++)
+               if (!strcasecmp(t, trip_types[i])) {
+                       *type = i;
+                       return 0;
+               }
+
+       return -ENODEV;
+}
+
+/**
+ * thermal_of_populate_trip - parse and fill one trip point data
+ * @np: DT node containing a trip point node
+ * @trip: trip point data structure to be filled up
+ *
+ * This function parses a trip point type of node represented by
+ * @np parameter and fills the read data into @trip data structure.
+ *
+ * Return: 0 on success, proper error code otherwise
+ */
+static int thermal_of_populate_trip(struct device_node *np,
+                                   struct thermal_trip *trip)
+{
+       int prop;
+       int ret;
+
+       ret = of_property_read_u32(np, "temperature", &prop);
+       if (ret < 0) {
+               pr_err("missing temperature property\n");
+               return ret;
+       }
+       trip->temperature = prop;
+
+       ret = of_property_read_u32(np, "hysteresis", &prop);
+       if (ret < 0) {
+               pr_err("missing hysteresis property\n");
+               return ret;
+       }
+       trip->hysteresis = prop;
+
+       ret = thermal_of_get_trip_type(np, &trip->type);
+       if (ret < 0) {
+               pr_err("wrong trip type property\n");
+               return ret;
+       }
+
+       /* Required for cooling map matching */
+       trip->np = np;
+       of_node_get(np);
+
+       return 0;
+}
+
+/**
+ * thermal_of_build_thermal_zone - parse and fill one thermal zone data
+ * @np: DT node containing a thermal zone node
+ *
+ * This function parses a thermal zone type of node represented by
+ * @np parameter and fills the read data into a __thermal_zone data structure
+ * and return this pointer.
+ *
+ * TODO: Missing properties to parse: thermal-sensor-names
+ *
+ * Return: On success returns a valid struct __thermal_zone,
+ * otherwise, it returns a corresponding ERR_PTR(). Caller must
+ * check the return value with help of IS_ERR() helper.
+ */
+static struct __thermal_zone
+__init *thermal_of_build_thermal_zone(struct device_node *np)
+{
+       struct device_node *child = NULL, *gchild;
+       struct __thermal_zone *tz;
+       int ret, i;
+       u32 prop, coef[2];
+
+       if (!np) {
+               pr_err("no thermal zone np\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       tz = kzalloc(sizeof(*tz), GFP_KERNEL);
+       if (!tz)
+               return ERR_PTR(-ENOMEM);
+
+       ret = of_property_read_u32(np, "polling-delay-passive", &prop);
+       if (ret < 0) {
+               pr_err("%pOFn: missing polling-delay-passive property\n", np);
+               goto free_tz;
+       }
+       tz->passive_delay = prop;
+
+       ret = of_property_read_u32(np, "polling-delay", &prop);
+       if (ret < 0) {
+               pr_err("%pOFn: missing polling-delay property\n", np);
+               goto free_tz;
+       }
+       tz->polling_delay = prop;
+
+       /*
+        * REVIST: for now, the thermal framework supports only
+        * one sensor per thermal zone. Thus, we are considering
+        * only the first two values as slope and offset.
+        */
+       ret = of_property_read_u32_array(np, "coefficients", coef, 2);
+       if (ret == 0) {
+               tz->slope = coef[0];
+               tz->offset = coef[1];
+       } else {
+               tz->slope = 1;
+               tz->offset = 0;
+       }
+
+       /* trips */
+       child = of_get_child_by_name(np, "trips");
+
+       /* No trips provided */
+       if (!child)
+               goto finish;
+
+       tz->ntrips = of_get_child_count(child);
+       if (tz->ntrips == 0) /* must have at least one child */
+               goto finish;
+
+       tz->trips = kcalloc(tz->ntrips, sizeof(*tz->trips), GFP_KERNEL);
+       if (!tz->trips) {
+               ret = -ENOMEM;
+               goto free_tz;
+       }
+
+       i = 0;
+       for_each_child_of_node(child, gchild) {
+               ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
+               if (ret)
+                       goto free_trips;
+       }
+
+       of_node_put(child);
+
+       /* cooling-maps */
+       child = of_get_child_by_name(np, "cooling-maps");
+
+       /* cooling-maps not provided */
+       if (!child)
+               goto finish;
+
+       tz->num_tbps = of_get_child_count(child);
+       if (tz->num_tbps == 0)
+               goto finish;
+
+       tz->tbps = kcalloc(tz->num_tbps, sizeof(*tz->tbps), GFP_KERNEL);
+       if (!tz->tbps) {
+               ret = -ENOMEM;
+               goto free_trips;
+       }
+
+       i = 0;
+       for_each_child_of_node(child, gchild) {
+               ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
+                                                     tz->trips, tz->ntrips);
+               if (ret)
+                       goto free_tbps;
+       }
+
+finish:
+       of_node_put(child);
+       tz->mode = THERMAL_DEVICE_DISABLED;
+
+       return tz;
+
+free_tbps:
+       for (i = i - 1; i >= 0; i--) {
+               struct __thermal_bind_params *tbp = tz->tbps + i;
+               int j;
+
+               for (j = 0; j < tbp->count; j++)
+                       of_node_put(tbp->tcbp[j].cooling_device);
+
+               kfree(tbp->tcbp);
+       }
+
+       kfree(tz->tbps);
+free_trips:
+       for (i = 0; i < tz->ntrips; i++)
+               of_node_put(tz->trips[i].np);
+       kfree(tz->trips);
+       of_node_put(gchild);
+free_tz:
+       kfree(tz);
+       of_node_put(child);
+
+       return ERR_PTR(ret);
+}
+
+static __init void of_thermal_free_zone(struct __thermal_zone *tz)
+{
+       struct __thermal_bind_params *tbp;
+       int i, j;
+
+       for (i = 0; i < tz->num_tbps; i++) {
+               tbp = tz->tbps + i;
+
+               for (j = 0; j < tbp->count; j++)
+                       of_node_put(tbp->tcbp[j].cooling_device);
+
+               kfree(tbp->tcbp);
+       }
+
+       kfree(tz->tbps);
+       for (i = 0; i < tz->ntrips; i++)
+               of_node_put(tz->trips[i].np);
+       kfree(tz->trips);
+       kfree(tz);
+}
+
+/**
+ * of_thermal_destroy_zones - remove all zones parsed and allocated resources
+ *
+ * Finds all zones parsed and added to the thermal framework and remove them
+ * from the system, together with their resources.
+ *
+ */
+static __init void of_thermal_destroy_zones(void)
+{
+       struct device_node *np, *child;
+
+       np = of_find_node_by_name(NULL, "thermal-zones");
+       if (!np) {
+               pr_debug("unable to find thermal zones\n");
+               return;
+       }
+
+       for_each_available_child_of_node(np, child) {
+               struct thermal_zone_device *zone;
+
+               zone = thermal_zone_get_zone_by_name(child->name);
+               if (IS_ERR(zone))
+                       continue;
+
+               thermal_zone_device_unregister(zone);
+               kfree(zone->tzp);
+               kfree(zone->ops);
+               of_thermal_free_zone(zone->devdata);
+       }
+       of_node_put(np);
+}
+
+/**
+ * of_parse_thermal_zones - parse device tree thermal data
+ *
+ * Initialization function that can be called by machine initialization
+ * code to parse thermal data and populate the thermal framework
+ * with hardware thermal zones info. This function only parses thermal zones.
+ * Cooling devices and sensor devices nodes are supposed to be parsed
+ * by their respective drivers.
+ *
+ * Return: 0 on success, proper error code otherwise
+ *
+ */
+int __init of_parse_thermal_zones(void)
+{
+       struct device_node *np, *child;
+       struct __thermal_zone *tz;
+       struct thermal_zone_device_ops *ops;
+
+       np = of_find_node_by_name(NULL, "thermal-zones");
+       if (!np) {
+               pr_debug("unable to find thermal zones\n");
+               return 0; /* Run successfully on systems without thermal DT */
+       }
+
+       for_each_available_child_of_node(np, child) {
+               struct thermal_zone_device *zone;
+               struct thermal_zone_params *tzp;
+               int i, mask = 0;
+               u32 prop;
+
+               tz = thermal_of_build_thermal_zone(child);
+               if (IS_ERR(tz)) {
+                       pr_err("failed to build thermal zone %pOFn: %ld\n",
+                              child,
+                              PTR_ERR(tz));
+                       continue;
+               }
+
+               ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
+               if (!ops)
+                       goto exit_free;
+
+               tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
+               if (!tzp) {
+                       kfree(ops);
+                       goto exit_free;
+               }
+
+               /* No hwmon because there might be hwmon drivers registering */
+               tzp->no_hwmon = true;
+
+               if (!of_property_read_u32(child, "sustainable-power", &prop))
+                       tzp->sustainable_power = prop;
+
+               for (i = 0; i < tz->ntrips; i++)
+                       mask |= 1 << i;
+
+               /* these two are left for temperature drivers to use */
+               tzp->slope = tz->slope;
+               tzp->offset = tz->offset;
+
+               zone = thermal_zone_device_register(child->name, tz->ntrips,
+                                                   mask, tz,
+                                                   ops, tzp,
+                                                   tz->passive_delay,
+                                                   tz->polling_delay);
+               if (IS_ERR(zone)) {
+                       pr_err("Failed to build %pOFn zone %ld\n", child,
+                              PTR_ERR(zone));
+                       kfree(tzp);
+                       kfree(ops);
+                       of_thermal_free_zone(tz);
+                       /* attempting to build remaining zones still */
+               }
+       }
+       of_node_put(np);
+
+       return 0;
+
+exit_free:
+       of_node_put(child);
+       of_node_put(np);
+       of_thermal_free_zone(tz);
+
+       /* no memory available, so free what we have built */
+       of_thermal_destroy_zones();
+
+       return -ENOMEM;
+}
index 263b042..ab19cef 100644 (file)
@@ -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,
index d3e959d..85776db 100644 (file)
@@ -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/drivers/thermal/user_space.c b/drivers/thermal/user_space.c
deleted file mode 100644 (file)
index 293cffd..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *  user_space.c - A simple user space Thermal events notifier
- *
- *  Copyright (C) 2012 Intel Corp
- *  Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
- *
- *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/thermal.h>
-#include <linux/slab.h>
-
-#include "thermal_core.h"
-
-/**
- * notify_user_space - Notifies user space about thermal events
- * @tz: thermal_zone_device
- * @trip: trip point index
- *
- * This function notifies the user space through UEvents.
- */
-static int notify_user_space(struct thermal_zone_device *tz, int trip)
-{
-       char *thermal_prop[5];
-       int i;
-
-       mutex_lock(&tz->lock);
-       thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", tz->type);
-       thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", tz->temperature);
-       thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=%d", trip);
-       thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", tz->notify_event);
-       thermal_prop[4] = NULL;
-       kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop);
-       for (i = 0; i < 4; ++i)
-               kfree(thermal_prop[i]);
-       mutex_unlock(&tz->lock);
-       return 0;
-}
-
-static struct thermal_governor thermal_gov_user_space = {
-       .name           = "user_space",
-       .throttle       = notify_user_space,
-};
-THERMAL_GOVERNOR_DECLARE(thermal_gov_user_space);
index 65501d8..a3bdc8a 100644 (file)
@@ -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 */
 
index a445cd1..91a8612 100644 (file)
@@ -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__ */
index c91b1e3..216185b 100644 (file)
 /* 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,