[PATCH 05/15] thermal/drivers/samsung: acpm-tmu: Implement .change_mode() callback
From: Tudor Ambarus
Date: Mon Oct 26 2026 - 12:02:57 EST
Because acpm-tmu did not implement the .change_mode() callback and
instead enabled the hardware sensor manually after registration, the
hardware sensor state was out of sync with the thermal core:
1. Disabling a thermal zone (e.g. via /sys/class/thermal/thermal_zone*/mode
or thermal_of_zone_unregister()) only set tz->mode to
THERMAL_DEVICE_DISABLED and stopped software polling, while leaving
sensor->enabled true and the ACPM TMU sensor active in hardware.
Consequently, crossing a trip threshold still fired the hardware IRQ
and ran acpm_tmu_thread_fn(), where thermal_zone_device_update()
returned early without updating the trip window. Direct sysfs and
hwmon temperature reads via thermal_zone_get_temp() also continued to
resume the device and query ACPM over IPC on a disabled zone.
2. System suspend/resume paths in acpm_tmu_control() unconditionally
toggled all registered sensors without checking the thermal zone mode
or current hardware state. As a result, acpm_tmu_suspend() issued
redundant disable IPC calls for already-disabled zones,
acpm_tmu_resume() unconditionally re-enabled zones that had been
disabled via sysfs prior to suspend, and suspend error recovery sent
redundant enable IPC calls to sensors that had not yet been disabled.
3. devm_thermal_of_zone_register() enables the zone and triggers an
initial thermal_zone_device_update() before returning. Because
sensor->enabled was still false during registration, the initial
acpm_tmu_get_temp() call failed with -EAGAIN and scheduled a retry,
requiring a manual sensor enable and a second
thermal_zone_device_update() call in acpm_tmu_probe().
Implement the .change_mode() callback to manage sensor hardware state
transitions directly through the thermal core and record the requested
zone mode in sensor->mode. Update acpm_tmu_control() to only toggle
sensors whose zone mode is THERMAL_DEVICE_ENABLED and whose
sensor->enabled state differs from the target state. Drop the redundant
manual sensor enablement and thermal_zone_device_update() call from
acpm_tmu_probe().
Fixes: 50a2c5d4062a ("thermal/drivers/samsung: Add Exynos ACPM TMU driver GS101")
Signed-off-by: Tudor Ambarus <tudor.ambarus@xxxxxxxxxx>
---
drivers/thermal/samsung/acpm-tmu.c | 59 ++++++++++++++++++++++++++------------
1 file changed, 40 insertions(+), 19 deletions(-)
diff --git a/drivers/thermal/samsung/acpm-tmu.c b/drivers/thermal/samsung/acpm-tmu.c
index 55ee97218f47..69793fce3bf1 100644
--- a/drivers/thermal/samsung/acpm-tmu.c
+++ b/drivers/thermal/samsung/acpm-tmu.c
@@ -68,6 +68,7 @@ struct acpm_tmu_sensor {
struct thermal_zone_device *tzd;
struct acpm_tmu_priv *priv;
struct mutex lock; /* protects sensor state */
+ enum thermal_device_mode mode;
bool enabled;
};
@@ -188,9 +189,12 @@ static int acpm_tmu_control(struct acpm_tmu_priv *priv, bool on)
if (!sensor->tzd)
continue;
- mutex_lock(&sensor->lock);
- ret = acpm_tmu_op_tz_control(sensor, on);
- mutex_unlock(&sensor->lock);
+ scoped_guard(mutex, &sensor->lock) {
+ ret = 0;
+ if (sensor->mode == THERMAL_DEVICE_ENABLED &&
+ sensor->enabled != on)
+ ret = acpm_tmu_op_tz_control(sensor, on);
+ }
if (ret && !err)
err = ret;
}
@@ -313,7 +317,34 @@ static int acpm_tmu_set_trips(struct thermal_zone_device *tz, int low, int high)
return ret;
}
+static int acpm_tmu_change_mode(struct thermal_zone_device *tz,
+ enum thermal_device_mode mode)
+{
+ struct acpm_tmu_sensor *sensor = thermal_zone_device_priv(tz);
+ struct acpm_tmu_priv *priv = sensor->priv;
+ struct device *dev = priv->dev;
+ bool enable = (mode == THERMAL_DEVICE_ENABLED);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &sensor->lock) {
+ ret = 0;
+ if (sensor->enabled != enable)
+ ret = acpm_tmu_op_tz_control(sensor, enable);
+ if (!ret)
+ sensor->mode = mode;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
static const struct thermal_zone_device_ops acpm_tmu_sensor_ops = {
+ .change_mode = acpm_tmu_change_mode,
.get_temp = acpm_tmu_get_temp,
.set_trips = acpm_tmu_set_trips,
};
@@ -463,17 +494,17 @@ static int acpm_tmu_probe(struct platform_device *pdev)
for (i = 0; i < priv->num_sensors; i++) {
struct acpm_tmu_sensor *sensor = &priv->sensors[i];
+ struct thermal_zone_device *tzd;
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);
+ tzd = devm_thermal_of_zone_register(dev, i, sensor,
+ &acpm_tmu_sensor_ops);
+ if (IS_ERR(tzd)) {
+ ret = PTR_ERR(tzd);
if (ret == -ENODEV) {
- sensor->tzd = NULL;
dev_dbg(dev, "Sensor %d not used in DT, skipping\n", i);
continue;
}
@@ -481,17 +512,7 @@ static int acpm_tmu_probe(struct platform_device *pdev)
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);
+ sensor->tzd = tzd;
ret = devm_thermal_add_hwmon_sysfs(dev, sensor->tzd);
if (ret)
--
2.56.0.257.gf7497c7d27-goog