summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml3
-rw-r--r--Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml29
-rw-r--r--Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml69
-rw-r--r--Documentation/devicetree/bindings/thermal/qcom,pm8775-mbg-tm.yaml7
-rw-r--r--Documentation/devicetree/bindings/thermal/qcom-tsens.yaml1
-rw-r--r--Documentation/devicetree/bindings/thermal/renesas,r9a08g045-tsu.yaml4
-rw-r--r--Documentation/devicetree/bindings/thermal/ti,omap-bandgap.yaml177
-rw-r--r--Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt88
-rw-r--r--Documentation/driver-api/thermal/cpu-idle-cooling.rst2
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/thermal/airoha_thermal.c179
-rw-r--r--drivers/thermal/amlogic_thermal.c14
-rw-r--r--drivers/thermal/cpufreq_cooling.c14
-rw-r--r--drivers/thermal/devfreq_cooling.c4
-rw-r--r--drivers/thermal/gov_power_allocator.c18
-rw-r--r--drivers/thermal/imx91_thermal.c2
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.c14
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c28
-rw-r--r--drivers/thermal/intel/intel_powerclamp.c64
-rw-r--r--drivers/thermal/intel/intel_tcc_cooling.c1
-rw-r--r--drivers/thermal/qcom/qcom-spmi-adc-tm5.c4
-rw-r--r--drivers/thermal/qcom/qcom-spmi-temp-alarm.c8
-rw-r--r--drivers/thermal/qcom/tsens-v1.c41
-rw-r--r--drivers/thermal/qcom/tsens-v2.c4
-rw-r--r--drivers/thermal/qcom/tsens.c3
-rw-r--r--drivers/thermal/qcom/tsens.h2
-rw-r--r--drivers/thermal/renesas/rzg3e_thermal.c45
-rw-r--r--drivers/thermal/renesas/rzg3s_thermal.c38
-rw-r--r--drivers/thermal/samsung/Kconfig19
-rw-r--r--drivers/thermal/samsung/Makefile2
-rw-r--r--drivers/thermal/samsung/acpm-tmu.c651
-rw-r--r--drivers/thermal/sprd_thermal.c6
-rw-r--r--drivers/thermal/thermal_core.c2
-rw-r--r--drivers/thermal/thermal_fw.c8
-rw-r--r--drivers/thermal/thermal_of.c15
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.c22
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-thermal-common.c4
-rw-r--r--include/uapi/linux/thermal.h8
-rw-r--r--tools/thermal/tmon/sysfs.c2
-rw-r--r--tools/thermal/tmon/tmon.c4
-rw-r--r--tools/thermal/tmon/tmon.h2
-rw-r--r--tools/thermal/tmon/tui.c2
42 files changed, 1331 insertions, 287 deletions
diff --git a/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml b/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml
index cc564dc7b414..4a10d024f5fa 100644
--- a/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml
+++ b/Documentation/devicetree/bindings/arm/airoha,en7581-chip-scu.yaml
@@ -26,7 +26,8 @@ properties:
maxItems: 1
'#thermal-sensor-cells':
- const: 0
+ description: Sensor index, 0 bandgap, 1 GbE, 2 CPU.
+ const: 1
required:
- compatible
diff --git a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
index 8cfa44dcda58..e72cb39ac635 100644
--- a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
+++ b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
@@ -32,7 +32,15 @@ properties:
maxItems: 1
clocks:
- maxItems: 1
+ minItems: 1
+ items:
+ - description: Bus clock, gates register access to the sensor.
+ - description: Functional clock of the temperature sensor, shared between sensors.
+
+ clock-names:
+ items:
+ - const: bus
+ - const: core
power-domains:
maxItems: 1
@@ -72,6 +80,22 @@ allOf:
else:
required:
- amlogic,secure-monitor
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: amlogic,t7-thermal
+ then:
+ properties:
+ clocks:
+ minItems: 2
+ required:
+ - clock-names
+ else:
+ properties:
+ clocks:
+ maxItems: 1
+ clock-names: false
unevaluatedProperties: false
@@ -94,7 +118,8 @@ examples:
compatible = "amlogic,t7-thermal";
reg = <0x20000 0x50>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clkc_periphs CLKID_TS>;
+ clocks = <&clkc_periphs CLKID_SYS_TS_A73>, <&clkc_periphs CLKID_TS>;
+ clock-names = "bus", "core";
#thermal-sensor-cells = <0>;
amlogic,secure-monitor = <&sm 1>;
};
diff --git a/Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml b/Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml
new file mode 100644
index 000000000000..75560ebca48d
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/google,gs101-tmu-top.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung Exynos ACPM Thermal Management Unit (TMU)
+
+maintainers:
+ - Tudor Ambarus <tudor.ambarus@linaro.org>
+
+description:
+ The Samsung Exynos ACPM TMU is a thermal sensor block found on Exynos
+ based platforms (such as Google GS101 and Exynos850). It supports
+ both direct register-level access and firmware-mediated management
+ via the ACPM (Alive Clock and Power Manager) firmware.
+
+ On these platforms, the hardware is managed in a hybrid fashion. The
+ Application Processor (AP) maintains direct memory-mapped access
+ exclusively to the interrupt pending registers to identify thermal
+ events. All other functional aspects - including sensor
+ initialization, threshold configuration, and temperature acquisition
+ - are handled by the ACPM firmware. The AP coordinates these
+ operations through the ACPM IPC protocol.
+
+properties:
+ compatible:
+ const: google,gs101-tmu-top
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: APB peripheral clock (PCLK) for TMU register access.
+
+ interrupts:
+ maxItems: 1
+
+ "#thermal-sensor-cells":
+ const: 1
+
+ samsung,acpm-ipc:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: Phandle to the ACPM IPC node.
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - interrupts
+ - "#thermal-sensor-cells"
+ - samsung,acpm-ipc
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/google,gs101.h>
+
+ thermal-sensor@100a0000 {
+ compatible = "google,gs101-tmu-top";
+ reg = <0x100a0000 0x800>;
+ clocks = <&cmu_misc CLK_GOUT_MISC_TMU_TOP_PCLK>;
+ interrupts = <GIC_SPI 769 IRQ_TYPE_LEVEL_HIGH 0>;
+ #thermal-sensor-cells = <1>;
+ samsung,acpm-ipc = <&acpm_ipc>;
+ };
diff --git a/Documentation/devicetree/bindings/thermal/qcom,pm8775-mbg-tm.yaml b/Documentation/devicetree/bindings/thermal/qcom,pm8775-mbg-tm.yaml
index 2e084d040625..ef8361a55389 100644
--- a/Documentation/devicetree/bindings/thermal/qcom,pm8775-mbg-tm.yaml
+++ b/Documentation/devicetree/bindings/thermal/qcom,pm8775-mbg-tm.yaml
@@ -21,7 +21,12 @@ description:
properties:
compatible:
- const: qcom,pm8775-mbg-tm
+ oneOf:
+ - const: qcom,pm8775-mbg-tm
+ - items:
+ - enum:
+ - qcom,pmau0102-mbg-tm
+ - const: qcom,pm8775-mbg-tm
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
index 5a8f7673e730..c78074fbcecf 100644
--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
@@ -46,6 +46,7 @@ properties:
items:
- enum:
- qcom,msm8937-tsens
+ - qcom,msm8952-tsens
- qcom,msm8956-tsens
- qcom,msm8976-tsens
- qcom,qcs404-tsens
diff --git a/Documentation/devicetree/bindings/thermal/renesas,r9a08g045-tsu.yaml b/Documentation/devicetree/bindings/thermal/renesas,r9a08g045-tsu.yaml
index 573e2b9d3752..20fc1a8000e3 100644
--- a/Documentation/devicetree/bindings/thermal/renesas,r9a08g045-tsu.yaml
+++ b/Documentation/devicetree/bindings/thermal/renesas,r9a08g045-tsu.yaml
@@ -17,7 +17,9 @@ $ref: thermal-sensor.yaml#
properties:
compatible:
- const: renesas,r9a08g045-tsu
+ enum:
+ - renesas,r9a08g045-tsu
+ - renesas,r9a08g046-tsu
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/thermal/ti,omap-bandgap.yaml b/Documentation/devicetree/bindings/thermal/ti,omap-bandgap.yaml
new file mode 100644
index 000000000000..091b7be28461
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/ti,omap-bandgap.yaml
@@ -0,0 +1,177 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/ti,omap-bandgap.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments OMAP SCM bandgap
+
+maintainers:
+ - Eduardo Valentin <edubezval@gmail.com>
+
+description:
+ In the System Control Module, OMAP supplies a voltage reference and a
+ temperature sensor feature that are gathered in the band gap voltage and
+ temperature sensor (VBGAPTS) module. The band gap provides current and
+ voltage reference for its internal circuits and other analog IP blocks. The
+ analog-to-digital converter (ADC) produces an output value that is
+ proportional to the silicon temperature.
+
+properties:
+ compatible:
+ enum:
+ - ti,omap34xx-bandgap
+ - ti,omap36xx-bandgap
+ - ti,omap4430-bandgap
+ - ti,omap4460-bandgap
+ - ti,omap4470-bandgap
+ - ti,omap5430-bandgap
+ - ti,dra752-bandgap
+
+ reg:
+ minItems: 1
+ maxItems: 6
+
+ interrupts:
+ maxItems: 1
+
+ gpios:
+ maxItems: 1
+ description:
+ used to inform which GPIO line the tshut signal is routed to. The
+ informed GPIO will be treated as an IRQ;
+
+ "#thermal-sensor-cells":
+ enum: [0, 1]
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: thermal-sensor.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - ti,omap34xx-bandgap
+ - ti,omap36xx-bandgap
+
+ then:
+ properties:
+ reg:
+ minItems: 1
+ maxItems: 1
+
+ interrupts: false
+
+ gpios: false
+
+ "#thermal-sensor-cells":
+ const: 0
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,omap4430-bandgap
+
+ then:
+ properties:
+ reg:
+ minItems: 2
+ maxItems: 2
+
+ interrupts: false
+
+ "#thermal-sensor-cells":
+ const: 0
+
+ required:
+ - gpios
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - ti,omap4460-bandgap
+ - ti,omap4470-bandgap
+
+ then:
+ properties:
+ reg:
+ minItems: 3
+ maxItems: 3
+
+ "#thermal-sensor-cells":
+ const: 0
+
+ required:
+ - interrupts
+ - gpios
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,omap5430-bandgap
+
+ then:
+ properties:
+ reg:
+ minItems: 4
+ maxItems: 4
+
+ gpios: false
+
+ "#thermal-sensor-cells":
+ const: 1
+
+ required:
+ - interrupts
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,dra752-bandgap
+
+ then:
+ properties:
+ reg:
+ minItems: 6
+ maxItems: 6
+
+ gpios: false
+
+ "#thermal-sensor-cells":
+ const: 1
+
+ required:
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ bandgap@48002524 {
+ compatible = "ti,omap34xx-bandgap";
+ reg = <0x48002524 0x4>;
+ #thermal-sensor-cells = <0>;
+ };
+
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ bandgap@4a0021e0 {
+ compatible = "ti,dra752-bandgap";
+ reg = <0x4a0021e0 0xc>, <0x4a00232c 0xc>,
+ <0x4a002380 0x2c>, <0x4a0023c0 0x3c>,
+ <0x4a002564 0x8>, <0x4a002574 0x50>;
+ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>; /* talert */
+ #thermal-sensor-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt b/Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt
deleted file mode 100644
index 6299dd8de339..000000000000
--- a/Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-* Texas Instrument OMAP SCM bandgap bindings
-
-In the System Control Module, OMAP supplies a voltage reference
-and a temperature sensor feature that are gathered in the band
-gap voltage and temperature sensor (VBGAPTS) module. The band
-gap provides current and voltage reference for its internal
-circuits and other analog IP blocks. The analog-to-digital
-converter (ADC) produces an output value that is proportional
-to the silicon temperature.
-
-Required properties:
-- compatible : Should be:
- - "ti,omap34xx-bandgap" : for OMAP34xx bandgap
- - "ti,omap36xx-bandgap" : for OMAP36xx bandgap
- - "ti,omap4430-bandgap" : for OMAP4430 bandgap
- - "ti,omap4460-bandgap" : for OMAP4460 bandgap
- - "ti,omap4470-bandgap" : for OMAP4470 bandgap
- - "ti,omap5430-bandgap" : for OMAP5430 bandgap
-- interrupts : this entry should indicate which interrupt line
-the talert signal is routed to;
-Specific:
-- gpios : this entry should be used to inform which GPIO
-line the tshut signal is routed to. The informed GPIO will
-be treated as an IRQ;
-- regs : this entry must also be specified and it is specific
-to each bandgap version, because the mapping may change from
-soc to soc, apart of depending on available features.
-
-Example:
-OMAP34xx:
-bandgap {
- reg = <0x48002524 0x4>;
- compatible = "ti,omap34xx-bandgap";
-};
-
-OMAP36xx:
-bandgap {
- reg = <0x48002524 0x4>;
- compatible = "ti,omap36xx-bandgap";
-};
-
-OMAP4430:
-bandgap {
- reg = <0x4a002260 0x4 0x4a00232C 0x4>;
- compatible = "ti,omap4430-bandgap";
-};
-
-OMAP4460:
-bandgap {
- reg = <0x4a002260 0x4
- 0x4a00232C 0x4
- 0x4a002378 0x18>;
- compatible = "ti,omap4460-bandgap";
- interrupts = <0 126 4>; /* talert */
- gpios = <&gpio3 22 0>; /* tshut */
-};
-
-OMAP4470:
-bandgap {
- reg = <0x4a002260 0x4
- 0x4a00232C 0x4
- 0x4a002378 0x18>;
- compatible = "ti,omap4470-bandgap";
- interrupts = <0 126 4>; /* talert */
- gpios = <&gpio3 22 0>; /* tshut */
-};
-
-OMAP5430:
-bandgap {
- reg = <0x4a0021e0 0xc
- 0x4a00232c 0xc
- 0x4a002380 0x2c
- 0x4a0023C0 0x3c>;
- compatible = "ti,omap5430-bandgap";
- interrupts = <0 126 4>; /* talert */
-};
-
-DRA752:
-bandgap {
- reg = <0x4a0021e0 0xc
- 0x4a00232c 0xc
- 0x4a002380 0x2c
- 0x4a0023C0 0x3c
- 0x4a002564 0x8
- 0x4a002574 0x50>;
- compatible = "ti,dra752-bandgap";
- interrupts = <0 126 4>; /* talert */
-};
diff --git a/Documentation/driver-api/thermal/cpu-idle-cooling.rst b/Documentation/driver-api/thermal/cpu-idle-cooling.rst
index c2a7ca676853..b9d4c91a7f15 100644
--- a/Documentation/driver-api/thermal/cpu-idle-cooling.rst
+++ b/Documentation/driver-api/thermal/cpu-idle-cooling.rst
@@ -161,7 +161,7 @@ tree. So with the idle injection mechanism, we want an average power
specific OPP and idle another amount of time. That could be put in a
equation::
- P(opp)target = ((Trunning x (P(opp)running) + (Tidle x P(opp)idle)) /
+ P(opp)target = (Trunning x (P(opp)running) + (Tidle x P(opp)idle)) /
(Trunning + Tidle)
...
diff --git a/MAINTAINERS b/MAINTAINERS
index b3e79e617b9c..8d2fe0f815b9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24378,6 +24378,14 @@ F: drivers/clk/samsung/clk-acpm.c
F: drivers/firmware/samsung/exynos-acpm*
F: include/linux/firmware/samsung/exynos-acpm-protocol.h
+SAMSUNG EXYNOS ACPM THERMAL DRIVER
+M: Tudor Ambarus <tudor.ambarus@linaro.org>
+L: linux-kernel@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/thermal/google,gs101-tmu-top.yaml
+F: drivers/thermal/samsung/acpm-tmu.c
+
SAMSUNG EXYNOS MAILBOX DRIVER
M: Tudor Ambarus <tudor.ambarus@linaro.org>
L: linux-samsung-soc@vger.kernel.org
diff --git a/drivers/thermal/airoha_thermal.c b/drivers/thermal/airoha_thermal.c
index 77b3a1af271f..7c63a1b44a6b 100644
--- a/drivers/thermal/airoha_thermal.c
+++ b/drivers/thermal/airoha_thermal.c
@@ -5,6 +5,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
@@ -18,10 +19,10 @@
#define EN7581_DOUT_TADC 0x2f8
#define EN7581_DOUT_TADC_MASK GENMASK(15, 0)
-#define AN7583_MUX_SENSOR 0x2a0
+#define AN7583_MUX_TADC 0x2a0
#define AN7583_LOAD_ADJ GENMASK(3, 2)
-#define AN7583_MUX_TADC 0x2e4
-#define AN7583_MUX_TADC_MASK GENMASK(3, 1)
+#define AN7583_MUX_SENSOR 0x2e4
+#define AN7583_MUX_SENSOR_MASK GENMASK(3, 1)
#define AN7583_DOUT_TADC 0x2f0
/* PTP_THERMAL regs */
@@ -191,6 +192,7 @@
#define AN7583_NUM_SENSOR 3
#define AIROHA_THERMAL_NO_MUX_SENSOR -1
+#define AIROHA_THERMAL_MUX_TRIES 3
/* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */
#define TEMP_TO_RAW(priv, temp) ((((((temp) / 100) - (priv)->init_temp) * \
@@ -243,6 +245,11 @@ enum airoha_thermal_chip_scu_field {
AIROHA_THERMAL_FIELD_MAX,
};
+struct airoha_thermal_zone {
+ struct airoha_thermal_priv *priv;
+ int sensor;
+};
+
struct airoha_thermal_priv {
struct regmap *map;
struct regmap *chip_scu;
@@ -250,7 +257,8 @@ struct airoha_thermal_priv {
struct resource scu_adc_res;
u32 pllrg_protect;
- int current_adc;
+ /* Serialises mux selection and ADC sampling */
+ struct mutex lock;
struct thermal_zone_device *tz;
int init_temp;
@@ -260,6 +268,9 @@ struct airoha_thermal_priv {
struct airoha_thermal_soc_data {
u32 pllrg_protect;
+ /* Sensor mux value per zone, NULL without a sensor mux */
+ const int *sensors;
+ int num_sensors;
const struct thermal_zone_device_ops *thdev_ops;
int (*probe)(struct platform_device *pdev,
@@ -294,9 +305,26 @@ static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv)
return val;
}
+static bool airoha_thermal_mux_holds(struct airoha_thermal_priv *priv,
+ int tdac_idx, int sensor_idx)
+{
+ unsigned int val;
+
+ if (regmap_field_read(priv->chip_scu_fields[AIROHA_THERMAL_MUX_TADC], &val) ||
+ val != tdac_idx)
+ return false;
+
+ if (sensor_idx == AIROHA_THERMAL_NO_MUX_SENSOR)
+ return true;
+
+ return !regmap_field_read(priv->chip_scu_fields[AIROHA_THERMAL_MUX_SENSOR], &val) &&
+ val == sensor_idx;
+}
+
static void airoha_set_thermal_mux(struct airoha_thermal_priv *priv,
int tdac_idx, int sensor_idx)
{
+ int tries = AIROHA_THERMAL_MUX_TRIES;
u32 pllrg;
/* Save PLLRG current value */
@@ -307,19 +335,17 @@ static void airoha_set_thermal_mux(struct airoha_thermal_priv *priv,
priv->pllrg_protect);
/*
- * Configure Thermal Sensor mux to sensor_idx.
+ * Configure Thermal Sensor mux to sensor_idx and Thermal ADC mux to
+ * tdac_idx. Writes can be dropped silently, so read them back.
* (if not supported, sensor_idx is AIROHA_THERMAL_NO_MUX_SENSOR)
*/
- if (sensor_idx != AIROHA_THERMAL_NO_MUX_SENSOR)
- regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_SENSOR],
- sensor_idx);
-
- /* Configure Thermal ADC mux to tdac_idx */
- if (priv->current_adc != tdac_idx) {
+ do {
+ if (sensor_idx != AIROHA_THERMAL_NO_MUX_SENSOR)
+ regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_SENSOR],
+ sensor_idx);
regmap_field_write(priv->chip_scu_fields[AIROHA_THERMAL_MUX_TADC],
tdac_idx);
- priv->current_adc = tdac_idx;
- }
+ } while (!airoha_thermal_mux_holds(priv, tdac_idx, sensor_idx) && --tries);
/* Restore PLLRG value on exit */
regmap_write(priv->chip_scu, EN7581_PLLRG_PROTECT, pllrg);
@@ -330,7 +356,8 @@ static void airoha_set_thermal_mux(struct airoha_thermal_priv *priv,
static int en7581_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
- struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
+ struct airoha_thermal_zone *zone = thermal_zone_device_priv(tz);
+ struct airoha_thermal_priv *priv = zone->priv;
int min_value, max_value, avg_value, value;
int i;
@@ -356,7 +383,8 @@ static int en7581_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
static int en7581_thermal_set_trips(struct thermal_zone_device *tz, int low,
int high)
{
- struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
+ struct airoha_thermal_zone *zone = thermal_zone_device_priv(tz);
+ struct airoha_thermal_priv *priv = zone->priv;
bool enable_monitor = false;
if (high != INT_MAX) {
@@ -528,7 +556,7 @@ static int en7581_thermal_probe(struct platform_device *pdev,
struct device_node *chip_scu_np;
struct device *dev = &pdev->dev;
void __iomem *base;
- int i, irq, ret;
+ int i;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -567,6 +595,18 @@ static int en7581_thermal_probe(struct platform_device *pdev,
of_address_to_resource(chip_scu_np, 0, &priv->scu_adc_res);
of_node_put(chip_scu_np);
+ en7581_thermal_setup_monitor(priv);
+ en7581_thermal_setup_adc_val(dev, priv);
+
+ return 0;
+}
+
+static int en7581_thermal_post_probe(struct platform_device *pdev)
+{
+ struct airoha_thermal_priv *priv = platform_get_drvdata(pdev);
+ int irq, ret;
+
+ /* After zone registration: the handler uses priv->tz */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
@@ -577,45 +617,63 @@ static int en7581_thermal_probe(struct platform_device *pdev,
if (ret)
return ret;
- en7581_thermal_setup_monitor(priv);
- en7581_thermal_setup_adc_val(dev, priv);
+ /* Enable LOW and HIGH interrupt (if supported) */
+ regmap_write(priv->map, EN7581_TEMPMONINT,
+ EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0);
return 0;
}
-static int en7581_thermal_post_probe(struct platform_device *pdev)
+static int an7583_thermal_read_diode(struct airoha_thermal_priv *priv,
+ int diode, int sensor_idx, int *val)
{
- struct airoha_thermal_priv *priv = platform_get_drvdata(pdev);
+ airoha_set_thermal_mux(priv, diode, sensor_idx);
+ *val = airoha_get_thermal_ADC(priv);
- /* Enable LOW and HIGH interrupt (if supported) */
- regmap_write(priv->map, EN7581_TEMPMONINT,
- EN7581_HOFSINTEN0 | EN7581_LOFSINTEN0);
+ return airoha_thermal_mux_holds(priv, diode, sensor_idx) ? 0 : -EAGAIN;
+}
- return 0;
+static int an7583_thermal_read_diodes(struct airoha_thermal_priv *priv,
+ int sensor_idx, int *zero, int *d0,
+ int *d1)
+{
+ int ret;
+
+ ret = an7583_thermal_read_diode(priv, AN7583_ZERO_TADC, sensor_idx, zero);
+ if (ret)
+ return ret;
+
+ ret = an7583_thermal_read_diode(priv, AN7583_D0_TADC, sensor_idx, d0);
+ if (ret)
+ return ret;
+
+ return an7583_thermal_read_diode(priv, AN7583_D1_TADC, sensor_idx, d1);
}
static int an7583_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
- struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz);
- int sensor_idx;
+ struct airoha_thermal_zone *zone = thermal_zone_device_priv(tz);
+ struct airoha_thermal_priv *priv = zone->priv;
+ int sensor_idx = zone->sensor;
int delta_diode, delta_gain;
int coeff, slope, offset;
+ int tries = AIROHA_THERMAL_MUX_TRIES;
+ int ret;
int diode_zero, diode_d0, diode_d1;
- /* Always read sensor AN7583_BGP_TEMP_SENSOR */
- sensor_idx = AN7583_BGP_TEMP_SENSOR;
-
coeff = an7583_thermal_coeff[sensor_idx];
slope = an7583_thermal_slope[sensor_idx];
offset = an7583_thermal_offset[sensor_idx];
- airoha_set_thermal_mux(priv, AN7583_ZERO_TADC, sensor_idx);
- diode_zero = airoha_get_thermal_ADC(priv);
- airoha_set_thermal_mux(priv, AN7583_D0_TADC, sensor_idx);
- diode_d0 = airoha_get_thermal_ADC(priv);
- airoha_set_thermal_mux(priv, AN7583_D1_TADC, sensor_idx);
- diode_d1 = airoha_get_thermal_ADC(priv);
+ mutex_lock(&priv->lock);
+ do {
+ ret = an7583_thermal_read_diodes(priv, sensor_idx, &diode_zero,
+ &diode_d0, &diode_d1);
+ } while (ret == -EAGAIN && --tries);
+ mutex_unlock(&priv->lock);
+ if (ret)
+ return ret;
delta_diode = diode_d1 - diode_d0;
delta_gain = (delta_diode * coeff) / 100 + (diode_zero - diode_d1);
@@ -634,8 +692,8 @@ static const struct thermal_zone_device_ops an7583_tz_ops = {
static const struct reg_field an7583_chip_scu_fields[AIROHA_THERMAL_FIELD_MAX] = {
[AIROHA_THERMAL_DOUT_TADC] = REG_FIELD(AN7583_DOUT_TADC, 0, 31),
- [AIROHA_THERMAL_MUX_TADC] = REG_FIELD(AN7583_MUX_TADC, 1, 3),
- [AIROHA_THERMAL_MUX_SENSOR] = REG_FIELD(AN7583_MUX_SENSOR, 2, 3),
+ [AIROHA_THERMAL_MUX_TADC] = REG_FIELD(AN7583_MUX_TADC, 2, 3),
+ [AIROHA_THERMAL_MUX_SENSOR] = REG_FIELD(AN7583_MUX_SENSOR, 1, 3),
};
static int an7583_thermal_probe(struct platform_device *pdev,
@@ -667,7 +725,7 @@ static int airoha_thermal_probe(struct platform_device *pdev)
const struct airoha_thermal_soc_data *soc_data;
struct airoha_thermal_priv *priv;
struct device *dev = &pdev->dev;
- int ret;
+ int ret, i;
soc_data = device_get_match_data(dev);
@@ -676,7 +734,10 @@ static int airoha_thermal_probe(struct platform_device *pdev)
return -ENOMEM;
priv->pllrg_protect = soc_data->pllrg_protect;
- priv->current_adc = -1;
+
+ ret = devm_mutex_init(dev, &priv->lock);
+ if (ret)
+ return ret;
if (!soc_data->probe)
return -EINVAL;
@@ -686,11 +747,31 @@ static int airoha_thermal_probe(struct platform_device *pdev)
return ret;
/* register of thermal sensor and get info from DT */
- priv->tz = devm_thermal_of_zone_register(dev, 0, priv,
- soc_data->thdev_ops);
- if (IS_ERR(priv->tz)) {
- dev_err(dev, "register thermal zone sensor failed\n");
- return PTR_ERR(priv->tz);
+ for (i = 0; i < soc_data->num_sensors; i++) {
+ struct airoha_thermal_zone *zone;
+ struct thermal_zone_device *tz;
+
+ zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
+ if (!zone)
+ return -ENOMEM;
+
+ zone->priv = priv;
+ zone->sensor = soc_data->sensors ? soc_data->sensors[i] :
+ AIROHA_THERMAL_NO_MUX_SENSOR;
+
+ tz = devm_thermal_of_zone_register(dev, i, zone,
+ soc_data->thdev_ops);
+ if (IS_ERR(tz)) {
+ /* A DT may describe fewer zones than sensors */
+ if (i && PTR_ERR(tz) == -ENODEV)
+ continue;
+
+ dev_err(dev, "register thermal zone %d failed\n", i);
+ return PTR_ERR(tz);
+ }
+
+ if (!i)
+ priv->tz = tz;
}
platform_set_drvdata(pdev, priv);
@@ -698,7 +779,15 @@ static int airoha_thermal_probe(struct platform_device *pdev)
return soc_data->post_probe ? soc_data->post_probe(pdev) : 0;
}
+/* The bandgap sensor stays zone 0 */
+static const int an7583_zone_sensors[AN7583_NUM_SENSOR] = {
+ AN7583_BGP_TEMP_SENSOR,
+ AN7583_GBE_TEMP_SENSOR,
+ AN7583_CPU_TEMP_SENSOR,
+};
+
static const struct airoha_thermal_soc_data en7581_data = {
+ .num_sensors = 1,
.pllrg_protect = EN7581_SCU_THERMAL_PROTECT_KEY,
.thdev_ops = &en7581_thdev_ops,
.probe = &en7581_thermal_probe,
@@ -706,6 +795,8 @@ static const struct airoha_thermal_soc_data en7581_data = {
};
static const struct airoha_thermal_soc_data an7583_data = {
+ .sensors = an7583_zone_sensors,
+ .num_sensors = AN7583_NUM_SENSOR,
.pllrg_protect = AN7583_SCU_THERMAL_PROTECT_KEY,
.thdev_ops = &an7583_tz_ops,
.probe = &an7583_thermal_probe,
diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c
index a0b530624b60..60728de6968a 100644
--- a/drivers/thermal/amlogic_thermal.c
+++ b/drivers/thermal/amlogic_thermal.c
@@ -100,7 +100,8 @@ struct amlogic_thermal {
const struct amlogic_thermal_data *data;
struct regmap *regmap;
struct regmap *sec_ao_map;
- struct clk *clk;
+ struct clk_bulk_data *clks;
+ int num_clks;
struct thermal_zone_device *tzd;
u32 trim_info;
struct meson_sm_firmware *sm_fw;
@@ -142,7 +143,7 @@ static int amlogic_thermal_enable(struct amlogic_thermal *data)
{
int ret;
- ret = clk_prepare_enable(data->clk);
+ ret = clk_bulk_prepare_enable(data->num_clks, data->clks);
if (ret)
return ret;
@@ -156,7 +157,7 @@ static void amlogic_thermal_disable(struct amlogic_thermal *data)
{
regmap_update_bits(data->regmap, TSENSOR_CFG_REG1,
TSENSOR_CFG_REG1_ENABLE, 0);
- clk_disable_unprepare(data->clk);
+ clk_bulk_disable_unprepare(data->num_clks, data->clks);
}
static int amlogic_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
@@ -323,9 +324,10 @@ static int amlogic_thermal_probe(struct platform_device *pdev)
if (IS_ERR(pdata->regmap))
return PTR_ERR(pdata->regmap);
- pdata->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(pdata->clk))
- return dev_err_probe(dev, PTR_ERR(pdata->clk), "failed to get clock\n");
+ ret = devm_clk_bulk_get_all(dev, &pdata->clks);
+ if (ret <= 0)
+ return dev_err_probe(dev, ret ?: -ENOENT, "failed to get clocks\n");
+ pdata->num_clks = ret;
if (pdata->data->use_sm)
ret = amlogic_thermal_probe_sm(pdev, pdata);
diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c
index 768859a7aed0..d75b42fb955e 100644
--- a/drivers/thermal/cpufreq_cooling.c
+++ b/drivers/thermal/cpufreq_cooling.c
@@ -488,12 +488,12 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
frequency = get_state_freq(cpufreq_cdev, state);
ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency);
- if (ret >= 0) {
- cpufreq_cdev->cpufreq_state = state;
- ret = 0;
- }
+ if (ret < 0)
+ return ret;
+
+ cpufreq_cdev->cpufreq_state = state;
- return ret;
+ return 0;
}
/**
@@ -661,8 +661,8 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
cdev = __cpufreq_cooling_register(np, policy, em);
if (IS_ERR(cdev)) {
- pr_err("cpufreq_cooling: cpu%d failed to register as cooling device: %ld\n",
- policy->cpu, PTR_ERR(cdev));
+ pr_err("cpufreq_cooling: cpu%d failed to register as cooling device: %pe\n",
+ policy->cpu, cdev);
cdev = NULL;
}
}
diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c
index 0330a8112832..9ada52cbf7dd 100644
--- a/drivers/thermal/devfreq_cooling.c
+++ b/drivers/thermal/devfreq_cooling.c
@@ -156,8 +156,8 @@ static unsigned long get_voltage(struct devfreq *df, unsigned long freq)
opp = dev_pm_opp_find_freq_exact(dev, freq, false);
if (IS_ERR(opp)) {
- dev_err_ratelimited(dev, "Failed to find OPP for frequency %lu: %ld\n",
- freq, PTR_ERR(opp));
+ dev_err_ratelimited(dev, "Failed to find OPP for frequency %lu: %pe\n",
+ freq, opp);
return 0;
}
diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c
index 37f2e22a999e..1cc9605a738f 100644
--- a/drivers/thermal/gov_power_allocator.c
+++ b/drivers/thermal/gov_power_allocator.c
@@ -660,10 +660,15 @@ static void power_allocator_update_tz(struct thermal_zone_device *tz,
enum thermal_notify_event reason)
{
struct power_allocator_params *params = tz->governor_data;
- const struct thermal_trip_desc *td = trip_to_trip_desc(params->trip_max);
+ const struct thermal_trip_desc *td;
struct thermal_instance *instance;
int num_actors = 0;
+ if (!params->trip_max)
+ return;
+
+ td = trip_to_trip_desc(params->trip_max);
+
switch (reason) {
case THERMAL_TZ_BIND_CDEV:
case THERMAL_TZ_UNBIND_CDEV:
@@ -707,22 +712,20 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
ret = check_power_actors(tz, params);
if (ret < 0) {
dev_warn(&tz->device, "power_allocator: binding failed\n");
- kfree(params);
- return ret;
+ goto free_params;
}
ret = allocate_actors_buffer(params, ret);
if (ret) {
dev_warn(&tz->device, "power_allocator: allocation failed\n");
- kfree(params);
- return ret;
+ goto free_params;
}
if (!tz->tzp) {
tz->tzp = kzalloc_obj(*tz->tzp);
if (!tz->tzp) {
ret = -ENOMEM;
- goto free_params;
+ goto free_power;
}
params->allocated_tzp = true;
@@ -746,8 +749,9 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
return 0;
-free_params:
+free_power:
kfree(params->power);
+free_params:
kfree(params);
return ret;
diff --git a/drivers/thermal/imx91_thermal.c b/drivers/thermal/imx91_thermal.c
index 274eee303142..196ee20fce1e 100644
--- a/drivers/thermal/imx91_thermal.c
+++ b/drivers/thermal/imx91_thermal.c
@@ -225,7 +225,7 @@ static int imx91_tmu_change_mode(struct thermal_zone_device *tz, enum thermal_de
return 0;
}
-static struct thermal_zone_device_ops tmu_tz_ops = {
+static const struct thermal_zone_device_ops tmu_tz_ops = {
.get_temp = imx91_tmu_get_temp,
.change_mode = imx91_tmu_change_mode,
.set_trips = imx91_tmu_set_trips,
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
index f80dbe2ca7e4..850bfe7c1ede 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
@@ -179,17 +179,21 @@ static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
{
int cpu;
int curr_temp, ret;
-
- *temp = 0;
+ bool temp_valid = false;
for_each_online_cpu(cpu) {
ret = intel_tcc_get_temp(cpu, &curr_temp, false);
if (ret < 0)
return ret;
- if (!*temp || curr_temp > *temp)
+ if (!temp_valid || curr_temp > *temp) {
*temp = curr_temp;
+ temp_valid = true;
+ }
}
+ if (!temp_valid)
+ return -ENODATA;
+
*temp *= 1000;
return 0;
@@ -291,10 +295,8 @@ int proc_thermal_add(struct device *dev, struct proc_thermal_device *proc_priv)
}
proc_priv->int340x_zone = int340x_thermal_zone_add(adev, get_temp);
- if (IS_ERR(proc_priv->int340x_zone)) {
+ if (IS_ERR(proc_priv->int340x_zone))
return PTR_ERR(proc_priv->int340x_zone);
- } else
- ret = 0;
ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
proc_thermal_notify,
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c
index 91f291627132..e161b95828e8 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c
@@ -66,15 +66,16 @@ static int slider_def_balance_set(const char *arg, const struct kernel_param *kp
guard(mutex)(&slider_param_lock);
ret = kstrtou8(arg, 16, &slider_val);
- if (!ret) {
- if (slider_val <= slider_values[SOC_POWER_SLIDER_PERFORMANCE] ||
- slider_val >= slider_values[SOC_POWER_SLIDER_POWERSAVE])
- return -EINVAL;
+ if (ret)
+ return ret;
- slider_balanced_param = slider_val;
- }
+ if (slider_val <= slider_values[SOC_POWER_SLIDER_PERFORMANCE] ||
+ slider_val >= slider_values[SOC_POWER_SLIDER_POWERSAVE])
+ return -EINVAL;
- return ret;
+ slider_balanced_param = slider_val;
+
+ return 0;
}
static int slider_def_balance_get(char *buf, const struct kernel_param *kp)
@@ -101,14 +102,15 @@ static int slider_def_offset_set(const char *arg, const struct kernel_param *kp)
guard(mutex)(&slider_param_lock);
ret = kstrtou8(arg, 16, &offset);
- if (!ret) {
- if (offset > SOC_SLIDER_VALUE_MAXIMUM)
- return -EINVAL;
+ if (ret)
+ return ret;
- slider_offset = offset;
- }
+ if (offset > SOC_SLIDER_VALUE_MAXIMUM)
+ return -EINVAL;
- return ret;
+ slider_offset = offset;
+
+ return 0;
}
static int slider_def_offset_get(char *buf, const struct kernel_param *kp)
diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c
index bd7fd98dc310..1ffcb0a699e5 100644
--- a/drivers/thermal/intel/intel_powerclamp.c
+++ b/drivers/thermal/intel/intel_powerclamp.c
@@ -94,7 +94,7 @@ static int duration_set(const char *arg, const struct kernel_param *kp)
}
mutex_lock(&powerclamp_lock);
- duration = clamp(new_duration, 6ul, 25ul) * 1000;
+ duration = new_duration * 1000;
mutex_unlock(&powerclamp_lock);
exit:
@@ -103,13 +103,9 @@ exit:
static int duration_get(char *buf, const struct kernel_param *kp)
{
- int ret;
-
- mutex_lock(&powerclamp_lock);
- ret = sysfs_emit(buf, "%d\n", duration / 1000);
- mutex_unlock(&powerclamp_lock);
+ guard(mutex)(&powerclamp_lock);
- return ret;
+ return sysfs_emit(buf, "%u\n", duration / 1000);
}
static const struct kernel_param_ops duration_ops = {
@@ -143,12 +139,9 @@ copy_mask:
}
/* Return true if the cpumask and idle percent combination is invalid */
-static bool check_invalid(cpumask_var_t mask, u8 idle)
+static bool check_invalid(const struct cpumask *mask, u8 idle)
{
- if (cpumask_equal(cpu_present_mask, mask) && idle > MAX_ALL_CPU_IDLE)
- return true;
-
- return false;
+ return cpumask_equal(cpu_present_mask, mask) && idle > MAX_ALL_CPU_IDLE;
}
static int cpumask_set(const char *arg, const struct kernel_param *kp)
@@ -214,41 +207,32 @@ MODULE_PARM_DESC(cpumask, "Mask of CPUs to use for idle injection.");
static int max_idle_set(const char *arg, const struct kernel_param *kp)
{
u8 new_max_idle;
- int ret = 0;
+ int ret;
- mutex_lock(&powerclamp_lock);
+ guard(mutex)(&powerclamp_lock);
/* Can't set mask when cooling device is in use */
- if (powerclamp_data.clamping) {
- ret = -EAGAIN;
- goto skip_limit_set;
- }
+ if (powerclamp_data.clamping)
+ return -EAGAIN;
ret = kstrtou8(arg, 10, &new_max_idle);
if (ret)
- goto skip_limit_set;
+ return ret;
- if (new_max_idle > MAX_TARGET_RATIO) {
- ret = -EINVAL;
- goto skip_limit_set;
- }
+ if (new_max_idle > MAX_TARGET_RATIO)
+ return -EINVAL;
if (!cpumask_available(idle_injection_cpu_mask)) {
ret = allocate_copy_idle_injection_mask(cpu_present_mask);
if (ret)
- goto skip_limit_set;
+ return ret;
}
- if (check_invalid(idle_injection_cpu_mask, new_max_idle)) {
- ret = -EINVAL;
- goto skip_limit_set;
- }
+ if (check_invalid(idle_injection_cpu_mask, new_max_idle))
+ return -EINVAL;
max_idle = new_max_idle;
-skip_limit_set:
- mutex_unlock(&powerclamp_lock);
-
return ret;
}
@@ -289,9 +273,10 @@ static int window_size_set(const char *arg, const struct kernel_param *kp)
pr_err("Out of recommended window size %lu, between 2-10\n",
new_window_size);
ret = -EINVAL;
+ goto exit_win;
}
- window_size = clamp(new_window_size, 2ul, 10ul);
+ window_size = new_window_size;
smp_mb();
exit_win:
@@ -536,23 +521,17 @@ static struct idle_inject_device *ii_dev;
*/
static bool idle_inject_update(void)
{
- bool update = false;
-
/* We can't sleep in this callback */
if (!mutex_trylock(&powerclamp_lock))
return true;
if (!(powerclamp_data.count % powerclamp_data.window_size_now)) {
+ unsigned int runtime;
should_skip = powerclamp_adjust_controls(powerclamp_data.target_ratio,
powerclamp_data.guard,
powerclamp_data.window_size_now);
- update = true;
- }
-
- if (update) {
- unsigned int runtime = get_run_time();
-
+ runtime = get_run_time();
idle_inject_set_duration(ii_dev, runtime, duration);
}
@@ -560,10 +539,7 @@ static bool idle_inject_update(void)
mutex_unlock(&powerclamp_lock);
- if (should_skip)
- return false;
-
- return true;
+ return !should_skip;
}
/* This function starts idle injection by calling idle_inject_start() */
diff --git a/drivers/thermal/intel/intel_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c
index 52ea4f7fc9f9..75f6fbec2530 100644
--- a/drivers/thermal/intel/intel_tcc_cooling.c
+++ b/drivers/thermal/intel/intel_tcc_cooling.c
@@ -69,6 +69,7 @@ static const struct x86_cpu_id tcc_ids[] __initconst = {
X86_MATCH_VFM(INTEL_ARROWLAKE_U, NULL),
X86_MATCH_VFM(INTEL_ARROWLAKE_H, NULL),
X86_MATCH_VFM(INTEL_PANTHERLAKE_L, NULL),
+ X86_MATCH_VFM(INTEL_PANTHERLAKE_R, NULL),
X86_MATCH_VFM(INTEL_WILDCATLAKE_L, NULL),
X86_MATCH_VFM(INTEL_NOVALAKE, NULL),
X86_MATCH_VFM(INTEL_NOVALAKE_L, NULL),
diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c
index af72db6299cd..4554a6fa55d1 100644
--- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c
+++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c
@@ -680,8 +680,8 @@ static int adc_tm5_register_tzd(struct adc_tm5_chip *adc_tm)
continue;
}
- dev_err(adc_tm->dev, "Error registering TZ zone for channel %d: %ld\n",
- adc_tm->channels[i].channel, PTR_ERR(tzd));
+ dev_err(adc_tm->dev, "Error registering TZ zone for channel %d: %pe\n",
+ adc_tm->channels[i].channel, tzd);
return PTR_ERR(tzd);
}
adc_tm->channels[i].tzd = tzd;
diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
index fb003ca96454..911ebfd0cdeb 100644
--- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
@@ -345,8 +345,8 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
}
skip:
- memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold],
- sizeof(chip->temp_thresh_map));
+ memcpy(chip->temp_thresh_map, (*chip->data->temp_map)[threshold],
+ sizeof(chip->temp_thresh_map));
reg |= threshold;
if (disable_stage2_shutdown && !chip->require_stage2_shutdown)
reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2;
@@ -535,8 +535,8 @@ static int qpnp_tm_sync_thresholds(struct qpnp_tm_chip *chip)
return ret;
threshold = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
- memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold],
- sizeof(chip->temp_thresh_map));
+ memcpy(chip->temp_thresh_map, (*chip->data->temp_map)[threshold],
+ sizeof(chip->temp_thresh_map));
return ret;
}
diff --git a/drivers/thermal/qcom/tsens-v1.c b/drivers/thermal/qcom/tsens-v1.c
index faa5d00788ca..7bda8e6087c4 100644
--- a/drivers/thermal/qcom/tsens-v1.c
+++ b/drivers/thermal/qcom/tsens-v1.c
@@ -145,6 +145,34 @@ static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
};
+static int __init init_8952(struct tsens_priv *priv)
+{
+ priv->sensor[0].p1_calib_offset = 2;
+ priv->sensor[0].p2_calib_offset = -1;
+ priv->sensor[1].p1_calib_offset = -4;
+ priv->sensor[1].p2_calib_offset = -4;
+ priv->sensor[2].p1_calib_offset = 4;
+ priv->sensor[2].p2_calib_offset = 5;
+ priv->sensor[3].p1_calib_offset = 1;
+ priv->sensor[3].p2_calib_offset = 1;
+ priv->sensor[4].p1_calib_offset = 2;
+ priv->sensor[4].p2_calib_offset = 3;
+ priv->sensor[5].p1_calib_offset = -1;
+ priv->sensor[5].p2_calib_offset = -1;
+ priv->sensor[6].p1_calib_offset = 0;
+ priv->sensor[6].p2_calib_offset = -1;
+ priv->sensor[7].p1_calib_offset = 3;
+ priv->sensor[7].p2_calib_offset = 4;
+ priv->sensor[8].p1_calib_offset = 2;
+ priv->sensor[8].p2_calib_offset = 4;
+ priv->sensor[9].p1_calib_offset = 0;
+ priv->sensor[9].p2_calib_offset = 0;
+ priv->sensor[10].p1_calib_offset = -3;
+ priv->sensor[10].p2_calib_offset = -2;
+
+ return init_common(priv);
+}
+
static int __init init_8956(struct tsens_priv *priv) {
priv->sensor[0].slope = 3313;
priv->sensor[1].slope = 3275;
@@ -223,6 +251,19 @@ struct tsens_plat_data data_8937 = {
.fields = tsens_v1_regfields,
};
+static const struct tsens_ops ops_8952 = {
+ .init = init_8952,
+ .calibrate = tsens_calibrate_common,
+ .get_temp = get_temp_tsens_valid,
+};
+
+struct tsens_plat_data data_8952 = {
+ .num_sensors = 11,
+ .ops = &ops_8952,
+ .feat = &tsens_v1_feat,
+ .fields = tsens_v1_regfields,
+};
+
static const struct tsens_ops ops_8956 = {
.init = init_8956,
.calibrate = tsens_calibrate_common,
diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
index 2ee117aa91ba..843dd29fad2e 100644
--- a/drivers/thermal/qcom/tsens-v2.c
+++ b/drivers/thermal/qcom/tsens-v2.c
@@ -54,8 +54,8 @@ static struct tsens_features tsens_v2_feat = {
.adc = 0,
.srot_split = 1,
.max_sensors = 16,
- .trip_min_temp = -40000,
- .trip_max_temp = 120000,
+ .trip_min_temp = -204000,
+ .trip_max_temp = 204000,
};
static struct tsens_features ipq8074_feat = {
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index b5ec70201e2f..4d7013fc79d6 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -1192,6 +1192,9 @@ static const struct of_device_id tsens_table[] = {
.compatible = "qcom,msm8939-tsens",
.data = &data_8939,
}, {
+ .compatible = "qcom,msm8952-tsens",
+ .data = &data_8952,
+ }, {
.compatible = "qcom,msm8956-tsens",
.data = &data_8956,
}, {
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index e8376accdff3..a35b15312961 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -669,7 +669,7 @@ extern struct tsens_plat_data data_8960;
extern struct tsens_plat_data data_8226, data_8909, data_8916, data_8939, data_8974, data_9607;
/* TSENS v1 targets */
-extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956;
+extern struct tsens_plat_data data_tsens_v1, data_8937, data_8952, data_8976, data_8956;
/* TSENS v1 with no RPM targets */
extern const struct tsens_plat_data data_ipq5018;
diff --git a/drivers/thermal/renesas/rzg3e_thermal.c b/drivers/thermal/renesas/rzg3e_thermal.c
index c44f5b8858d0..e806d130dc53 100644
--- a/drivers/thermal/renesas/rzg3e_thermal.c
+++ b/drivers/thermal/renesas/rzg3e_thermal.c
@@ -93,6 +93,7 @@ struct rzg3e_thermal_info {
* @info: chip type specific information
* @trmval0: calibration value 0 (b)
* @trmval1: calibration value 1 (c)
+ * @sier: cached interrupt enable register
* @lock: protects hardware access during conversions
*/
struct rzg3e_thermal_priv {
@@ -103,6 +104,7 @@ struct rzg3e_thermal_priv {
const struct rzg3e_thermal_info *info;
u16 trmval0;
u16 trmval1;
+ u32 sier;
struct mutex lock;
};
@@ -148,12 +150,16 @@ static int rzg3e_thermal_power_on(struct rzg3e_thermal_priv *priv)
return ret;
}
+ /* Restore interrupt enable state */
+ writel(priv->sier, priv->base + TSU_SIER);
+
return 0;
}
static void rzg3e_thermal_power_off(struct rzg3e_thermal_priv *priv)
{
- /* Disable all interrupts */
+ /* Save and disable all interrupts */
+ priv->sier = readl(priv->base + TSU_SIER);
writel(0, priv->base + TSU_SIER);
/* Clear pending interrupts */
@@ -194,7 +200,12 @@ static u16 rzg3e_thermal_temp_to_code(struct rzg3e_thermal_priv *priv, int temp_
s64 numerator, denominator;
s64 code;
- numerator = (temp_mc - info->temp_d_mc) * (priv->trmval1 - priv->trmval0);
+ /*
+ * Perform the arithmetic in 64 bits so that it cannot overflow for
+ * -INT_MAX/INT_MAX values passed from the thermal core or when
+ * userspace writes arbitrary trip point temperatures.
+ */
+ numerator = ((s64)temp_mc - info->temp_d_mc) * (priv->trmval1 - priv->trmval0);
denominator = info->temp_e_mc - info->temp_d_mc;
code = div64_s64(numerator, denominator) + priv->trmval0;
@@ -214,6 +225,16 @@ static int rzg3e_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
guard(mutex)(&priv->lock);
+ /* Make sure a previous conversion is not in progress */
+ ret = readl_poll_timeout(priv->base + TSU_SSR, status,
+ !(status & TSU_SSR_CONV),
+ TSU_POLL_DELAY_US,
+ USEC_PER_MSEC);
+ if (ret) {
+ dev_err(priv->dev, "Timeout waiting for conversion\n");
+ goto out;
+ }
+
/* Clear any previous conversion status */
writel(TSU_SICR_ADCLR, priv->base + TSU_SICR);
@@ -241,7 +262,6 @@ static int rzg3e_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
*temp, *temp / 1000, abs(*temp) % 1000, code);
out:
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
return ret;
}
@@ -254,10 +274,6 @@ static int rzg3e_thermal_set_trips(struct thermal_zone_device *tz,
u32 val;
int ret;
- /* Hardware requires low < high */
- if (low >= high)
- return -EINVAL;
-
ret = pm_runtime_resume_and_get(priv->dev);
if (ret < 0)
return ret;
@@ -293,11 +309,9 @@ static int rzg3e_thermal_set_trips(struct thermal_zone_device *tz,
/* Enable comparison with "out of range" mode (CMPCOND=0) */
writel(TSU_CMSR_CMPEN, priv->base + TSU_CMSR);
- /* Unmask compare IRQ and start a conversion to evaluate window */
+ /* Unmask compare IRQ */
writel(TSU_SIER_CMPIE, priv->base + TSU_SIER);
- writel(TSU_STRGR_ADST, priv->base + TSU_STRGR);
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
return 0;
@@ -469,7 +483,6 @@ static int rzg3e_thermal_probe(struct platform_device *pdev)
if (ret)
dev_warn(dev, "Failed to add hwmon sysfs attributes\n");
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
dev_info(dev, "RZ/G3E thermal sensor registered\n");
@@ -499,15 +512,19 @@ static int rzg3e_thermal_runtime_resume(struct device *dev)
static int rzg3e_thermal_suspend(struct device *dev)
{
struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev);
+ bool active = pm_runtime_active(dev);
+ int ret;
/* If device is active, power it off */
- if (pm_runtime_active(dev))
+ if (active)
rzg3e_thermal_power_off(priv);
/* Assert reset to ensure clean state after resume */
- reset_control_assert(priv->rstc);
+ ret = reset_control_assert(priv->rstc);
+ if (ret && active)
+ rzg3e_thermal_power_on(priv);
- return 0;
+ return ret;
}
static int rzg3e_thermal_resume(struct device *dev)
diff --git a/drivers/thermal/renesas/rzg3s_thermal.c b/drivers/thermal/renesas/rzg3s_thermal.c
index 7ced8f76a0ec..a8716da7fcd2 100644
--- a/drivers/thermal/renesas/rzg3s_thermal.c
+++ b/drivers/thermal/renesas/rzg3s_thermal.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/reset.h>
#include <linux/thermal.h>
#include <linux/units.h>
@@ -27,10 +28,6 @@
#define TSU_READ_STEPS 8
-/* Default calibration values, if FUSE values are missing. */
-#define SW_CALIB0_VAL 1297
-#define SW_CALIB1_VAL 751
-
#define MCELSIUS(temp) ((temp) * MILLIDEGREE_PER_DEGREE)
/**
@@ -55,6 +52,16 @@ struct rzg3s_thermal_priv {
u16 calib1;
};
+/**
+ * struct rzg3s_thermal_info - RZ/G3S thermal information
+ * @calib0: calibration value if FUSE value is missing
+ * @calib1: calibration value if FUSE value is missing
+ */
+struct rzg3s_thermal_info {
+ u16 calib0;
+ u16 calib1;
+};
+
static int rzg3s_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct rzg3s_thermal_priv *priv = thermal_zone_device_priv(tz);
@@ -149,7 +156,8 @@ static const struct thermal_zone_device_ops rzg3s_tz_of_ops = {
.change_mode = rzg3s_thermal_change_mode,
};
-static int rzg3s_thermal_read_calib(struct rzg3s_thermal_priv *priv)
+static int rzg3s_thermal_read_calib(struct rzg3s_thermal_priv *priv,
+ const struct rzg3s_thermal_info *info)
{
struct device *dev = priv->dev;
u32 val;
@@ -163,13 +171,13 @@ static int rzg3s_thermal_read_calib(struct rzg3s_thermal_priv *priv)
if (val & OTPTSUTRIM_EN_MASK)
priv->calib0 = FIELD_GET(OTPTSUTRIM_MASK, val);
else
- priv->calib0 = SW_CALIB0_VAL;
+ priv->calib0 = info->calib0;
val = readl(priv->base + OTPTSUTRIM_REG(1));
if (val & OTPTSUTRIM_EN_MASK)
priv->calib1 = FIELD_GET(OTPTSUTRIM_MASK, val);
else
- priv->calib1 = SW_CALIB1_VAL;
+ priv->calib1 = info->calib1;
pm_runtime_put_autosuspend(dev);
@@ -178,6 +186,7 @@ static int rzg3s_thermal_read_calib(struct rzg3s_thermal_priv *priv)
static int rzg3s_thermal_probe(struct platform_device *pdev)
{
+ const struct rzg3s_thermal_info *info = device_get_match_data(&pdev->dev);
struct rzg3s_thermal_priv *priv;
struct device *dev = &pdev->dev;
int ret;
@@ -208,7 +217,7 @@ static int rzg3s_thermal_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "Failed to enable runtime PM!\n");
- ret = rzg3s_thermal_read_calib(priv);
+ ret = rzg3s_thermal_read_calib(priv, info);
if (ret)
return dev_err_probe(dev, ret, "Failed to read calibration data!\n");
@@ -251,8 +260,19 @@ static const struct dev_pm_ops rzg3s_thermal_pm_ops = {
SYSTEM_SLEEP_PM_OPS(rzg3s_thermal_suspend, rzg3s_thermal_resume)
};
+static const struct rzg3s_thermal_info rzg3s_info = {
+ .calib0 = 1297,
+ .calib1 = 751,
+};
+
+static const struct rzg3s_thermal_info rzg3l_info = {
+ .calib0 = 1322,
+ .calib1 = 772,
+};
+
static const struct of_device_id rzg3s_thermal_dt_ids[] = {
- { .compatible = "renesas,r9a08g045-tsu" },
+ { .compatible = "renesas,r9a08g045-tsu", .data = &rzg3s_info },
+ { .compatible = "renesas,r9a08g046-tsu", .data = &rzg3l_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg3s_thermal_dt_ids);
diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
index e1e8035d24fb..f63eeae304b7 100644
--- a/drivers/thermal/samsung/Kconfig
+++ b/drivers/thermal/samsung/Kconfig
@@ -10,3 +10,22 @@ config EXYNOS_THERMAL
the TMU, reports temperature and handles cooling action if defined.
This driver uses the Exynos core thermal APIs and TMU configuration
data from the supported SoCs.
+
+config EXYNOS_ACPM_THERMAL
+ tristate "Exynos ACPM thermal management unit driver"
+ depends on THERMAL_OF
+ depends on HAS_IOMEM
+ depends on EXYNOS_ACPM_PROTOCOL
+ default ARCH_EXYNOS
+ help
+ Support for the Thermal Management Unit (TMU) on Samsung Exynos SoCs
+ utilizing the ACPM IPC protocol, such as the Google GS101.
+
+ The TMU on these platforms is managed through a hybrid architecture.
+ This driver handles direct register access for thermal interrupt status
+ monitoring and communicates with the Alive Clock and Power Manager
+ (ACPM) firmware via the ACPM IPC protocol for functional sensor control
+ and configuration.
+
+ Select this if you want to monitor device temperature and enable
+ thermal mitigation on Samsung Exynos ACPM based devices.
diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
index f139407150d2..daed80647c34 100644
--- a/drivers/thermal/samsung/Makefile
+++ b/drivers/thermal/samsung/Makefile
@@ -4,3 +4,5 @@
#
obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
exynos_thermal-y := exynos_tmu.o
+obj-$(CONFIG_EXYNOS_ACPM_THERMAL) += exynos_acpm_thermal.o
+exynos_acpm_thermal-y := acpm-tmu.o
diff --git a/drivers/thermal/samsung/acpm-tmu.c b/drivers/thermal/samsung/acpm-tmu.c
new file mode 100644
index 000000000000..f9802080acd7
--- /dev/null
+++ b/drivers/thermal/samsung/acpm-tmu.c
@@ -0,0 +1,651 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2019 Samsung Electronics Co., Ltd.
+ * Copyright 2025 Google LLC.
+ * Copyright 2026 Linaro Ltd.
+ */
+
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/firmware/samsung/exynos-acpm-protocol.h>
+#include <linux/interrupt.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+#include <linux/units.h>
+
+#include "../thermal_hwmon.h"
+
+#define EXYNOS_TMU_SENSOR(i) BIT(i)
+#define EXYNOS_TMU_SENSORS_MAX_COUNT 16
+
+#define GS101_CPUCL2_SENSOR_MASK (EXYNOS_TMU_SENSOR(0) | \
+ EXYNOS_TMU_SENSOR(6) | \
+ EXYNOS_TMU_SENSOR(7) | \
+ EXYNOS_TMU_SENSOR(8) | \
+ EXYNOS_TMU_SENSOR(9))
+#define GS101_CPUCL1_SENSOR_MASK (EXYNOS_TMU_SENSOR(4) | \
+ EXYNOS_TMU_SENSOR(5))
+#define GS101_CPUCL0_SENSOR_MASK (EXYNOS_TMU_SENSOR(1) | \
+ EXYNOS_TMU_SENSOR(2))
+
+#define GS101_REG_INTPEND(i) ((i) * 0x50 + 0xf8)
+
+enum {
+ P0_INTPEND,
+ P1_INTPEND,
+ P2_INTPEND,
+ P3_INTPEND,
+ P4_INTPEND,
+ P5_INTPEND,
+ P6_INTPEND,
+ P7_INTPEND,
+ P8_INTPEND,
+ P9_INTPEND,
+ P10_INTPEND,
+ P11_INTPEND,
+ P12_INTPEND,
+ P13_INTPEND,
+ P14_INTPEND,
+ P15_INTPEND,
+ REG_INTPEND_COUNT,
+};
+
+struct acpm_tmu_sensor_group {
+ u16 mask;
+ u8 id;
+};
+
+struct acpm_tmu_sensor {
+ const struct acpm_tmu_sensor_group *group;
+ struct thermal_zone_device *tzd;
+ struct acpm_tmu_priv *priv;
+ struct mutex lock; /* protects sensor state */
+ bool enabled;
+};
+
+struct acpm_tmu_priv {
+ struct regmap_field *regmap_fields[REG_INTPEND_COUNT];
+ struct acpm_handle *handle;
+ struct device *dev;
+ struct clk *clk;
+ unsigned int mbox_chan_id;
+ unsigned int num_sensors;
+ int irq;
+ struct acpm_tmu_sensor sensors[] __counted_by(num_sensors);
+};
+
+struct acpm_tmu_driver_data {
+ const struct reg_field *reg_fields;
+ const struct acpm_tmu_sensor_group *sensor_groups;
+ unsigned int num_sensor_groups;
+ unsigned int mbox_chan_id;
+};
+
+#define ACPM_TMU_SENSOR_GROUP(_mask, _id) \
+ { \
+ .mask = _mask, \
+ .id = _id, \
+ }
+
+static const struct acpm_tmu_sensor_group gs101_sensor_groups[] = {
+ ACPM_TMU_SENSOR_GROUP(GS101_CPUCL2_SENSOR_MASK, 0),
+ ACPM_TMU_SENSOR_GROUP(GS101_CPUCL1_SENSOR_MASK, 1),
+ ACPM_TMU_SENSOR_GROUP(GS101_CPUCL0_SENSOR_MASK, 2),
+};
+
+static const struct reg_field gs101_reg_fields[REG_INTPEND_COUNT] = {
+ [P0_INTPEND] = REG_FIELD(GS101_REG_INTPEND(0), 0, 31),
+ [P1_INTPEND] = REG_FIELD(GS101_REG_INTPEND(1), 0, 31),
+ [P2_INTPEND] = REG_FIELD(GS101_REG_INTPEND(2), 0, 31),
+ [P3_INTPEND] = REG_FIELD(GS101_REG_INTPEND(3), 0, 31),
+ [P4_INTPEND] = REG_FIELD(GS101_REG_INTPEND(4), 0, 31),
+ [P5_INTPEND] = REG_FIELD(GS101_REG_INTPEND(5), 0, 31),
+ [P6_INTPEND] = REG_FIELD(GS101_REG_INTPEND(6), 0, 31),
+ [P7_INTPEND] = REG_FIELD(GS101_REG_INTPEND(7), 0, 31),
+ [P8_INTPEND] = REG_FIELD(GS101_REG_INTPEND(8), 0, 31),
+ [P9_INTPEND] = REG_FIELD(GS101_REG_INTPEND(9), 0, 31),
+ [P10_INTPEND] = REG_FIELD(GS101_REG_INTPEND(10), 0, 31),
+ [P11_INTPEND] = REG_FIELD(GS101_REG_INTPEND(11), 0, 31),
+ [P12_INTPEND] = REG_FIELD(GS101_REG_INTPEND(12), 0, 31),
+ [P13_INTPEND] = REG_FIELD(GS101_REG_INTPEND(13), 0, 31),
+ [P14_INTPEND] = REG_FIELD(GS101_REG_INTPEND(14), 0, 31),
+ [P15_INTPEND] = REG_FIELD(GS101_REG_INTPEND(15), 0, 31),
+};
+
+static const struct regmap_config gs101_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .use_relaxed_mmio = true,
+ .max_register = GS101_REG_INTPEND(15),
+};
+
+static const struct acpm_tmu_driver_data acpm_tmu_gs101 = {
+ .reg_fields = gs101_reg_fields,
+ .sensor_groups = gs101_sensor_groups,
+ .num_sensor_groups = ARRAY_SIZE(gs101_sensor_groups),
+ .mbox_chan_id = 9,
+};
+
+static int acpm_tmu_op_tz_control(struct acpm_tmu_sensor *sensor, bool on)
+{
+ struct acpm_tmu_priv *priv = sensor->priv;
+ struct acpm_handle *handle = priv->handle;
+ const struct acpm_tmu_ops *ops = &handle->ops->tmu;
+ int ret;
+
+ ret = ops->tz_control(handle, priv->mbox_chan_id, sensor->group->id,
+ on);
+ if (ret)
+ return ret;
+
+ sensor->enabled = on;
+
+ return 0;
+}
+
+static void acpm_tmu_control_rollback(struct acpm_tmu_priv *priv, int start_idx)
+{
+ int i;
+
+ for (i = start_idx; i >= 0; i--) {
+ struct acpm_tmu_sensor *sensor = &priv->sensors[i];
+ int ret;
+
+ if (!sensor->tzd)
+ continue;
+
+ mutex_lock(&sensor->lock);
+ ret = acpm_tmu_op_tz_control(sensor, false);
+ mutex_unlock(&sensor->lock);
+ if (ret)
+ dev_err(priv->dev, "Rollback: Failed to disable sensor %d: %d\n",
+ i, ret);
+ }
+}
+
+static int acpm_tmu_control(struct acpm_tmu_priv *priv, bool on, bool rollback)
+{
+ struct device *dev = priv->dev;
+ int i, ret, err = 0;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < priv->num_sensors; i++) {
+ struct acpm_tmu_sensor *sensor = &priv->sensors[i];
+
+ /* Skip sensors that weren't found in DT */
+ if (!sensor->tzd)
+ continue;
+
+ mutex_lock(&sensor->lock);
+ ret = acpm_tmu_op_tz_control(sensor, on);
+ mutex_unlock(&sensor->lock);
+ if (ret) {
+ if (!err)
+ err = ret;
+
+ /* On enable, stop on first error if rollback is requested */
+ if (on && rollback) {
+ acpm_tmu_control_rollback(priv, i - 1);
+ break;
+ }
+ }
+ }
+
+ pm_runtime_put_sync(dev);
+
+ return err;
+}
+
+static int acpm_tmu_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct acpm_tmu_sensor *sensor = thermal_zone_device_priv(tz);
+ struct acpm_tmu_priv *priv = sensor->priv;
+ struct acpm_handle *handle = priv->handle;
+ const struct acpm_tmu_ops *ops = &handle->ops->tmu;
+ struct device *dev = priv->dev;
+ int acpm_temp = 0, ret;
+
+ /*
+ * Fast path: check locklessly if the sensor is enabled to avoid
+ * expensive runtime PM operations when it is disabled. Any race with
+ * concurrent disabling is caught by the second check under the lock
+ * after PM resume.
+ */
+ if (!sensor->enabled)
+ return -EAGAIN;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ scoped_guard(mutex, &sensor->lock) {
+ if (!sensor->enabled) {
+ ret = -EAGAIN;
+ } else {
+ ret = ops->read_temp(handle, priv->mbox_chan_id,
+ sensor->group->id, &acpm_temp);
+ }
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ if (ret)
+ return ret;
+
+ *temp = acpm_temp * MILLIDEGREE_PER_DEGREE;
+
+ return 0;
+}
+
+static int acpm_tmu_update_thresholds(struct acpm_tmu_sensor *sensor,
+ u8 thresholds[2], u8 inten)
+{
+ struct acpm_tmu_priv *priv = sensor->priv;
+ struct acpm_handle *handle = priv->handle;
+ const struct acpm_tmu_ops *ops = &handle->ops->tmu;
+ unsigned int mbox_chan_id = priv->mbox_chan_id;
+ u8 acpm_sensor_id = sensor->group->id;
+ bool was_enabled;
+ int ret, restore_ret;
+
+ guard(mutex)(&sensor->lock);
+
+ was_enabled = sensor->enabled;
+
+ if (was_enabled) {
+ ret = acpm_tmu_op_tz_control(sensor, false);
+ if (ret)
+ return ret;
+ }
+
+ ret = ops->set_threshold(handle, mbox_chan_id, acpm_sensor_id,
+ thresholds, 2);
+ if (!ret) {
+ ret = ops->set_interrupt_enable(handle, mbox_chan_id,
+ acpm_sensor_id, inten);
+ }
+
+ if (was_enabled) {
+ restore_ret = acpm_tmu_op_tz_control(sensor, true);
+ if (restore_ret)
+ dev_err(priv->dev, "Failed to restore sensor state: %d\n",
+ restore_ret);
+ if (!ret)
+ ret = restore_ret;
+ }
+
+ return ret;
+}
+
+static int acpm_tmu_set_trips(struct thermal_zone_device *tz, int low, int high)
+{
+ struct acpm_tmu_sensor *sensor = thermal_zone_device_priv(tz);
+ struct acpm_tmu_priv *priv = sensor->priv;
+ struct device *dev = priv->dev;
+ u8 thresholds[2] = {};
+ u8 inten = 0;
+ int ret;
+
+ /* If a valid lower bound exists, set the threshold and enable its interrupt */
+ if (low > -INT_MAX) {
+ thresholds[0] = clamp_val(low / MILLIDEGREE_PER_DEGREE, 0, 255);
+ inten |= BIT(0);
+ }
+
+ /* If a valid upper bound exists, set the threshold and enable its interrupt */
+ if (high < INT_MAX) {
+ thresholds[1] = clamp_val(high / MILLIDEGREE_PER_DEGREE, 0, 255);
+ inten |= BIT(1);
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ ret = acpm_tmu_update_thresholds(sensor, thresholds, inten);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static const struct thermal_zone_device_ops acpm_tmu_sensor_ops = {
+ .get_temp = acpm_tmu_get_temp,
+ .set_trips = acpm_tmu_set_trips,
+};
+
+static int acpm_tmu_has_pending_irq(struct acpm_tmu_sensor *sensor,
+ bool *pending_irq)
+{
+ struct acpm_tmu_priv *priv = sensor->priv;
+ unsigned long mask = sensor->group->mask;
+ int i, ret;
+ u32 val;
+
+ guard(mutex)(&sensor->lock);
+
+ for_each_set_bit(i, &mask, EXYNOS_TMU_SENSORS_MAX_COUNT) {
+ ret = regmap_field_read(priv->regmap_fields[i], &val);
+ if (ret)
+ return ret;
+
+ if (val) {
+ *pending_irq = true;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static irqreturn_t acpm_tmu_thread_fn(int irq, void *id)
+{
+ struct acpm_tmu_priv *priv = id;
+ struct acpm_handle *handle = priv->handle;
+ const struct acpm_tmu_ops *ops = &handle->ops->tmu;
+ struct device *dev = priv->dev;
+ bool handled = false;
+ int i, ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(dev, "Failed to resume: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < priv->num_sensors; i++) {
+ struct acpm_tmu_sensor *sensor = &priv->sensors[i];
+ bool pending_irq = false;
+
+ if (!sensor->tzd)
+ continue;
+
+ ret = acpm_tmu_has_pending_irq(sensor, &pending_irq);
+ if (ret || !pending_irq)
+ continue;
+
+ handled = true;
+
+ scoped_guard(mutex, &sensor->lock) {
+ ret = ops->clear_tz_irq(handle, priv->mbox_chan_id,
+ sensor->group->id);
+ if (ret)
+ dev_err(priv->dev, "Sensor %d: failed to clear IRQ (%d)\n",
+ i, ret);
+ }
+
+ thermal_zone_device_update(sensor->tzd,
+ THERMAL_EVENT_UNSPECIFIED);
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static const struct of_device_id acpm_tmu_match[] = {
+ { .compatible = "google,gs101-tmu-top" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, acpm_tmu_match);
+
+static int acpm_tmu_probe(struct platform_device *pdev)
+{
+ const struct acpm_tmu_driver_data *data = &acpm_tmu_gs101;
+ struct acpm_handle *acpm_handle;
+ struct device *dev = &pdev->dev;
+ struct acpm_tmu_priv *priv;
+ struct regmap *regmap;
+ void __iomem *base;
+ int i, ret;
+
+ acpm_handle = devm_acpm_get_by_phandle(dev);
+ if (IS_ERR(acpm_handle))
+ return dev_err_probe(dev, PTR_ERR(acpm_handle),
+ "Failed to get ACPM handle\n");
+
+ priv = devm_kzalloc(dev,
+ struct_size(priv, sensors, data->num_sensor_groups),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->handle = acpm_handle;
+ priv->mbox_chan_id = data->mbox_chan_id;
+ priv->num_sensors = data->num_sensor_groups;
+
+ platform_set_drvdata(pdev, priv);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base), "Failed to ioremap resource\n");
+
+ regmap = devm_regmap_init_mmio(dev, base, &gs101_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ ret = devm_regmap_field_bulk_alloc(dev, regmap, priv->regmap_fields,
+ data->reg_fields, REG_INTPEND_COUNT);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Unable to map syscon registers\n");
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk),
+ "Failed to get the clock\n");
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (priv->irq < 0)
+ return dev_err_probe(dev, priv->irq, "Failed to get irq\n");
+
+ pm_runtime_set_autosuspend_delay(dev, 100);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to resume device\n");
+
+ ret = acpm_handle->ops->tmu.init(acpm_handle, priv->mbox_chan_id);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "Failed to init TMU\n");
+ goto err_pm_put;
+ }
+
+ for (i = 0; i < priv->num_sensors; i++) {
+ struct acpm_tmu_sensor *sensor = &priv->sensors[i];
+
+ mutex_init(&sensor->lock);
+ sensor->group = &data->sensor_groups[i];
+ sensor->priv = priv;
+
+ sensor->tzd = devm_thermal_of_zone_register(dev, i, sensor,
+ &acpm_tmu_sensor_ops);
+ if (IS_ERR(sensor->tzd)) {
+ ret = PTR_ERR(sensor->tzd);
+ if (ret == -ENODEV) {
+ sensor->tzd = NULL;
+ dev_dbg(dev, "Sensor %d not used in DT, skipping\n", i);
+ continue;
+ }
+
+ ret = dev_err_probe(dev, ret, "Failed to register sensor %d\n", i);
+ goto err_rollback;
+ }
+
+ mutex_lock(&sensor->lock);
+ ret = acpm_tmu_op_tz_control(sensor, true);
+ mutex_unlock(&sensor->lock);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "Failed to enable sensor %d\n", i);
+ goto err_rollback;
+ }
+
+ thermal_zone_device_update(sensor->tzd,
+ THERMAL_EVENT_UNSPECIFIED);
+
+ ret = devm_thermal_add_hwmon_sysfs(dev, sensor->tzd);
+ if (ret)
+ dev_warn(dev, "Failed to add hwmon sysfs!\n");
+ }
+
+ ret = devm_request_threaded_irq(dev, priv->irq, NULL,
+ acpm_tmu_thread_fn, IRQF_ONESHOT,
+ dev_name(dev), priv);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "Failed to request irq\n");
+ goto err_rollback;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_rollback:
+ acpm_tmu_control_rollback(priv, i - 1);
+err_pm_put:
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_put_sync(dev);
+ return ret;
+}
+
+static void acpm_tmu_remove(struct platform_device *pdev)
+{
+ struct acpm_tmu_priv *priv = platform_get_drvdata(pdev);
+
+ /* Stop IRQ first to prevent race with thread_fn */
+ disable_irq(priv->irq);
+
+ /*
+ * Disable autosuspend to force the subsequent pm_runtime_put_sync()
+ * inside acpm_tmu_control() to synchronously suspend the device
+ * immediately, preventing clock leaks when the driver is removed.
+ */
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ acpm_tmu_control(priv, false, false);
+}
+
+static int acpm_tmu_suspend(struct device *dev)
+{
+ struct acpm_tmu_priv *priv = dev_get_drvdata(dev);
+ struct acpm_handle *handle = priv->handle;
+ const struct acpm_tmu_ops *ops = &handle->ops->tmu;
+ int ret, restore_ret;
+
+ ret = acpm_tmu_control(priv, false, false);
+ if (ret)
+ goto err_restore_sensors;
+
+ /* APB clock not required for this specific msg */
+ ret = ops->suspend(handle, priv->mbox_chan_id);
+ if (ret)
+ goto err_restore_sensors;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ goto err_resume_acpm;
+
+ return 0;
+
+err_resume_acpm:
+ restore_ret = ops->resume(handle, priv->mbox_chan_id);
+ if (restore_ret)
+ dev_err(dev, "Failed to resume ACPM after force suspend failure: %d\n",
+ restore_ret);
+
+err_restore_sensors:
+ restore_ret = acpm_tmu_control(priv, true, false);
+ if (restore_ret)
+ dev_err(dev, "Failed to re-enable TMU after suspend failure: %d\n",
+ restore_ret);
+
+ return ret;
+}
+
+static int acpm_tmu_resume(struct device *dev)
+{
+ struct acpm_tmu_priv *priv = dev_get_drvdata(dev);
+ struct acpm_handle *handle = priv->handle;
+ const struct acpm_tmu_ops *ops = &handle->ops->tmu;
+ int ret, restore_ret;
+
+ /* APB clock not required for this specific msg */
+ ret = ops->resume(handle, priv->mbox_chan_id);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ goto err_suspend_acpm;
+
+ ret = acpm_tmu_control(priv, true, true);
+ if (ret)
+ goto err_suspend_pm;
+
+ return 0;
+
+err_suspend_pm:
+ restore_ret = pm_runtime_force_suspend(dev);
+ if (restore_ret)
+ dev_err(dev, "Failed to force suspend during resume rollback: %d\n",
+ restore_ret);
+err_suspend_acpm:
+ restore_ret = ops->suspend(handle, priv->mbox_chan_id);
+ if (restore_ret)
+ dev_err(dev, "Failed to suspend ACPM during resume rollback: %d\n",
+ restore_ret);
+ return ret;
+}
+
+static int acpm_tmu_runtime_suspend(struct device *dev)
+{
+ struct acpm_tmu_priv *priv = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static int acpm_tmu_runtime_resume(struct device *dev)
+{
+ struct acpm_tmu_priv *priv = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(priv->clk);
+}
+
+static const struct dev_pm_ops acpm_tmu_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(acpm_tmu_suspend, acpm_tmu_resume)
+ RUNTIME_PM_OPS(acpm_tmu_runtime_suspend, acpm_tmu_runtime_resume, NULL)
+};
+
+static struct platform_driver acpm_tmu_driver = {
+ .driver = {
+ .name = "gs-tmu",
+ .pm = pm_ptr(&acpm_tmu_pm_ops),
+ .of_match_table = acpm_tmu_match,
+ },
+ .probe = acpm_tmu_probe,
+ .remove = acpm_tmu_remove,
+};
+module_platform_driver(acpm_tmu_driver);
+
+MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>");
+MODULE_DESCRIPTION("Samsung Exynos ACPM TMU Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/sprd_thermal.c b/drivers/thermal/sprd_thermal.c
index d683fcb0f8ab..b57277e2eac0 100644
--- a/drivers/thermal/sprd_thermal.c
+++ b/drivers/thermal/sprd_thermal.c
@@ -396,6 +396,12 @@ static int sprd_thm_probe(struct platform_device *pdev)
goto of_put;
}
+ if (sen->id < 0 || sen->id >= SPRD_THM_MAX_SENSOR) {
+ dev_err(&pdev->dev, "invalid sensor id %d\n", sen->id);
+ ret = -EINVAL;
+ goto of_put;
+ }
+
ret = sprd_thm_sensor_calibration(sen_child, thm, sen);
if (ret) {
dev_err(&pdev->dev, "efuse cal analysis failed");
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index ac928c199829..e8d4da0aeac3 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -1666,7 +1666,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
*
* Return: On success returns a reference to an unique thermal zone with
* matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid
- * paramenters, -ENODEV for not found and -EEXIST for multiple matches).
+ * parameters, -ENODEV for not found and -EEXIST for multiple matches).
*/
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name)
{
diff --git a/drivers/thermal/thermal_fw.c b/drivers/thermal/thermal_fw.c
index e8bd2049cf74..7c28bfada386 100644
--- a/drivers/thermal/thermal_fw.c
+++ b/drivers/thermal/thermal_fw.c
@@ -11,13 +11,13 @@
/**
* thermal_fwnode_cooling_device_register() - register an thermal cooling device
- * @np: a pointer to a device tree node.
- * @of_index: a cooling device index in the cooling controller
+ * @fwnode: a pointer to a firmware node.
+ * @fwn_index: a cooling device index in the cooling controller
* @type: the thermal cooling device type.
* @devdata: device private data.
* @ops: standard thermal cooling devices callbacks.
*
- * This function will register a cooling device with device tree node reference.
+ * This function will register a cooling device with a firmware node reference.
* This interface function adds a new thermal cooling device (fan/processor/...)
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
* to all the thermal zone devices registered at the same time.
@@ -71,7 +71,7 @@ __devm_thermal_fwnode_cooling_device_register(struct device *dev, struct fwnode_
/**
* devm_thermal_fwnode_cooling_device_register() - register a thermal cooling device
* @dev: a valid struct device pointer of a sensor device.
- * @fw_index: a cooling device index in the cooling controller
+ * @fwn_index: a cooling device index in the cooling controller
* @type: the thermal cooling device type.
* @devdata: device private data.
* @ops: standard thermal cooling devices callbacks.
diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c
index 14dfac9359b8..9e96fb1b033e 100644
--- a/drivers/thermal/thermal_of.c
+++ b/drivers/thermal/thermal_of.c
@@ -63,22 +63,23 @@ static int thermal_of_get_trip_type(struct device_node *np,
static int thermal_of_populate_trip(struct device_node *np,
struct thermal_trip *trip)
{
- int prop;
+ u32 hysteresis;
+ s32 temperature;
int ret;
- ret = of_property_read_u32(np, "temperature", &prop);
+ ret = of_property_read_s32(np, "temperature", &temperature);
if (ret < 0) {
pr_err("missing temperature property\n");
return ret;
}
- trip->temperature = prop;
+ trip->temperature = temperature;
- ret = of_property_read_u32(np, "hysteresis", &prop);
+ ret = of_property_read_u32(np, "hysteresis", &hysteresis);
if (ret < 0) {
pr_err("missing hysteresis property\n");
return ret;
}
- trip->hysteresis = prop;
+ trip->hysteresis = hysteresis;
ret = thermal_of_get_trip_type(np, &trip->type);
if (ret < 0) {
@@ -349,10 +350,10 @@ out:
}
/**
- * thermal_of_zone_unregister - Cleanup the specific allocated ressources
+ * thermal_of_zone_unregister - Cleanup the specific allocated resources
*
* This function disables the thermal zone and frees the different
- * ressources allocated specific to the thermal OF.
+ * resources allocated specific to the thermal OF.
*
* @tz: a pointer to the thermal zone structure
*/
diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
index ba43399d0b38..457639be2b32 100644
--- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
@@ -332,7 +332,7 @@ static inline int ti_bandgap_validate(struct ti_bandgap *bgp, int id)
* ti_bandgap_read_counter() - read the sensor counter
* @bgp: pointer to bandgap instance
* @id: sensor id
- * @interval: resulting update interval in miliseconds
+ * @interval: resulting update interval in milliseconds
*/
static void ti_bandgap_read_counter(struct ti_bandgap *bgp, int id,
int *interval)
@@ -352,7 +352,7 @@ static void ti_bandgap_read_counter(struct ti_bandgap *bgp, int id,
* ti_bandgap_read_counter_delay() - read the sensor counter delay
* @bgp: pointer to bandgap instance
* @id: sensor id
- * @interval: resulting update interval in miliseconds
+ * @interval: resulting update interval in milliseconds
*/
static void ti_bandgap_read_counter_delay(struct ti_bandgap *bgp, int id,
int *interval)
@@ -394,7 +394,7 @@ static void ti_bandgap_read_counter_delay(struct ti_bandgap *bgp, int id,
* ti_bandgap_read_update_interval() - read the sensor update interval
* @bgp: pointer to bandgap instance
* @id: sensor id
- * @interval: resulting update interval in miliseconds
+ * @interval: resulting update interval in milliseconds
*
* Return: 0 on success or the proper error code
*/
@@ -427,7 +427,7 @@ exit:
* ti_bandgap_write_counter_delay() - set the counter_delay
* @bgp: pointer to bandgap instance
* @id: sensor id
- * @interval: desired update interval in miliseconds
+ * @interval: desired update interval in milliseconds
*
* Return: 0 on success or the proper error code
*/
@@ -471,7 +471,7 @@ static int ti_bandgap_write_counter_delay(struct ti_bandgap *bgp, int id,
* ti_bandgap_write_counter() - set the bandgap sensor counter
* @bgp: pointer to bandgap instance
* @id: sensor id
- * @interval: desired update interval in miliseconds
+ * @interval: desired update interval in milliseconds
*/
static void ti_bandgap_write_counter(struct ti_bandgap *bgp, int id,
u32 interval)
@@ -486,7 +486,7 @@ static void ti_bandgap_write_counter(struct ti_bandgap *bgp, int id,
* ti_bandgap_write_update_interval() - set the update interval
* @bgp: pointer to bandgap instance
* @id: sensor id
- * @interval: desired update interval in miliseconds
+ * @interval: desired update interval in milliseconds
*
* Return: 0 on success or the proper error code
*/
@@ -576,7 +576,8 @@ int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data)
* @bgp: pointer to bandgap instance
* @id: sensor id
*
- * Return: data stored by set function with sensor id on success or NULL
+ * Return: the stored sensor data, which may be %NULL, or an ERR_PTR()
+ * if @bgp or @id is invalid.
*/
void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id)
{
@@ -1077,6 +1078,10 @@ void ti_bandgap_remove(struct platform_device *pdev)
if (!soc_device_match(soc_no_cpu_notifier))
cpu_pm_unregister_notifier(&bgp->nb);
+ /* Stop the alerts first: they queue work on the sensors' data */
+ if (TI_BANDGAP_HAS(bgp, TALERT))
+ free_irq(bgp->irq, bgp);
+
/* Remove sensor interfaces */
for (i = 0; i < bgp->conf->sensor_count; i++) {
if (bgp->conf->sensors[i].unregister_cooling)
@@ -1093,9 +1098,6 @@ void ti_bandgap_remove(struct platform_device *pdev)
clk_put(bgp->fclock);
clk_put(bgp->div_clk);
- if (TI_BANDGAP_HAS(bgp, TALERT))
- free_irq(bgp->irq, bgp);
-
if (TI_BANDGAP_HAS(bgp, TSHUT))
free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL);
}
diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
index 0cf0826b805a..4bf3b2e0fc68 100644
--- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
+++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
@@ -195,6 +195,10 @@ int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
data = ti_bandgap_get_sensor_data(bgp, id);
+ /* Work queued by the talert IRQ must not outlive the data */
+ if (!IS_ERR_OR_NULL(data))
+ cancel_work_sync(&data->thermal_wq);
+
if (!IS_ERR_OR_NULL(data) && data->ti_thermal) {
if (data->our_zone)
thermal_zone_device_unregister(data->ti_thermal);
diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h
index 46a2633d33aa..36f3da2d6a7c 100644
--- a/include/uapi/linux/thermal.h
+++ b/include/uapi/linux/thermal.h
@@ -81,11 +81,11 @@ enum thermal_genl_event {
THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */
THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */
THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, /* CPU capability changed */
- THERMAL_GENL_EVENT_THRESHOLD_ADD, /* A thresold has been added */
- THERMAL_GENL_EVENT_THRESHOLD_DELETE, /* A thresold has been deleted */
+ THERMAL_GENL_EVENT_THRESHOLD_ADD, /* A threshold has been added */
+ THERMAL_GENL_EVENT_THRESHOLD_DELETE, /* A threshold has been deleted */
THERMAL_GENL_EVENT_THRESHOLD_FLUSH, /* All thresolds have been deleted */
- THERMAL_GENL_EVENT_THRESHOLD_UP, /* A thresold has been crossed the way up */
- THERMAL_GENL_EVENT_THRESHOLD_DOWN, /* A thresold has been crossed the way down */
+ THERMAL_GENL_EVENT_THRESHOLD_UP, /* A threshold has been crossed the way up */
+ THERMAL_GENL_EVENT_THRESHOLD_DOWN, /* A threshold has been crossed the way down */
__THERMAL_GENL_EVENT_MAX,
};
#define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1)
diff --git a/tools/thermal/tmon/sysfs.c b/tools/thermal/tmon/sysfs.c
index cb1108bc9249..5d64aff12a86 100644
--- a/tools/thermal/tmon/sysfs.c
+++ b/tools/thermal/tmon/sysfs.c
@@ -225,7 +225,7 @@ static int find_tzone_cdev(struct dirent *nl, char *tz_name,
cdev_name, cdev_name_linked, cdev_id);
tzi->cdev_binding |= (1 << cdev_id);
- /* find the trip point in which the cdev is binded to
+ /* find the trip point in which the cdev is bound to
* in this tzone
*/
snprintf(cdev_trip_name, sizeof(cdev_trip_name) - 1,
diff --git a/tools/thermal/tmon/tmon.c b/tools/thermal/tmon/tmon.c
index 7eb3216a27f4..70a2ec50a461 100644
--- a/tools/thermal/tmon/tmon.c
+++ b/tools/thermal/tmon/tmon.c
@@ -171,7 +171,7 @@ static void prepare_logging(void)
memset(binding_str, 0, sizeof(binding_str));
for (j = 0; j < 32; j++)
- binding_str[j] = (ptdata.tzi[i].cdev_binding & (1 << j)) ?
+ binding_str[j] = (ptdata.tzi[i].cdev_binding & (1U << j)) ?
'1' : '0';
fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n",
@@ -230,7 +230,7 @@ int main(int argc, char **argv)
switch (c) {
case 'c':
no_control = 0;
- strncpy(ctrl_cdev, optarg, CDEV_NAME_SIZE);
+ snprintf(ctrl_cdev, sizeof(ctrl_cdev), "%s", optarg);
break;
case 'd':
start_daemon_mode();
diff --git a/tools/thermal/tmon/tmon.h b/tools/thermal/tmon/tmon.h
index 44d16d778f04..b7767056f189 100644
--- a/tools/thermal/tmon/tmon.h
+++ b/tools/thermal/tmon/tmon.h
@@ -77,7 +77,7 @@ struct tz_info {
char type[256]; /* e.g. acpitz */
int instance;
int passive; /* active zone has passive node to force passive mode */
- int nr_cdev; /* number of cooling device binded */
+ int nr_cdev; /* number of cooling devices bound */
int nr_trip_pts;
struct trip_point tp[MAX_NR_TRIP];
unsigned long cdev_binding; /* bitmap for attached cdevs */
diff --git a/tools/thermal/tmon/tui.c b/tools/thermal/tmon/tui.c
index 7f5dd2b87f15..dbc3ca95c724 100644
--- a/tools/thermal/tmon/tui.c
+++ b/tools/thermal/tmon/tui.c
@@ -238,7 +238,7 @@ void show_cooling_device(void)
unsigned long trip_binding =
ptdata.tzi[i].trip_binding[cdev_inst];
int k = 0; /* per zone trip point id that
- * binded to this cdev, one to
+ * is bound to this cdev, one to
* many possible based on the
* binding bitmask.
*/