summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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
27 files changed, 1028 insertions, 184 deletions
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);