[PATCH 03/15] thermal/drivers/samsung: acpm-tmu: Fix acpm_tmu_resume() error handling
From: Tudor Ambarus
Date: Mon Oct 26 2026 - 12:03:22 EST
Unlike .suspend(), system resume cannot be aborted: device_resume()
clears dev->power.is_suspended before invoking .resume(), and on error
merely logs the failure and continues into normal system operation
without re-enabling runtime PM on the driver's behalf.
acpm_tmu_resume() treats errors by rolling back into a suspended state,
causing three problems:
1. acpm_tmu_suspend() disables runtime PM via pm_runtime_force_suspend().
If ops->resume() fails before pm_runtime_force_resume() runs, or if
acpm_tmu_control() fails and the rollback path calls
pm_runtime_force_suspend(), dev->power.disable_depth remains at 1
with the device runtime-suspended. Every subsequent
pm_runtime_resume_and_get() then fails with -EACCES, breaking thermal
zone operations and causing future system suspends to abort in
acpm_tmu_suspend().
2. Calling ops->suspend() on error leaves the ACPM TMU firmware in the
suspended state while Linux considers the device awake, so subsequent
thermal zone operations run against suspended firmware and the next
acpm_tmu_suspend() issues an unbalanced ops->suspend() call.
3. Rolling back sensors via acpm_tmu_control_rollback() when a single
sensor fails to re-enable disables sensors that already resumed and
skips remaining sensors, leaving active CPU clusters without thermal
monitoring.
Mirror acpm_tmu_suspend() in reverse order by calling
pm_runtime_force_resume() at the start of acpm_tmu_resume(), and drop
the pm_runtime_force_suspend() and ops->suspend() error rollback paths.
Re-enable sensors on a best-effort basis so any sensor that fails
remains marked !sensor->enabled for the thermal core's post-resume
update, and drop the now-unused rollback parameter from
acpm_tmu_control().
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 | 48 ++++++++++----------------------------
1 file changed, 12 insertions(+), 36 deletions(-)
diff --git a/drivers/thermal/samsung/acpm-tmu.c b/drivers/thermal/samsung/acpm-tmu.c
index f9802080acd7..f490551de69a 100644
--- a/drivers/thermal/samsung/acpm-tmu.c
+++ b/drivers/thermal/samsung/acpm-tmu.c
@@ -172,7 +172,7 @@ static void acpm_tmu_control_rollback(struct acpm_tmu_priv *priv, int start_idx)
}
}
-static int acpm_tmu_control(struct acpm_tmu_priv *priv, bool on, bool rollback)
+static int acpm_tmu_control(struct acpm_tmu_priv *priv, bool on)
{
struct device *dev = priv->dev;
int i, ret, err = 0;
@@ -191,16 +191,8 @@ static int acpm_tmu_control(struct acpm_tmu_priv *priv, bool on, bool rollback)
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;
- }
- }
+ if (ret && !err)
+ err = ret;
}
pm_runtime_put_sync(dev);
@@ -539,7 +531,7 @@ static void acpm_tmu_remove(struct platform_device *pdev)
* immediately, preventing clock leaks when the driver is removed.
*/
pm_runtime_dont_use_autosuspend(&pdev->dev);
- acpm_tmu_control(priv, false, false);
+ acpm_tmu_control(priv, false);
}
static int acpm_tmu_suspend(struct device *dev)
@@ -549,7 +541,7 @@ static int acpm_tmu_suspend(struct device *dev)
const struct acpm_tmu_ops *ops = &handle->ops->tmu;
int ret, restore_ret;
- ret = acpm_tmu_control(priv, false, false);
+ ret = acpm_tmu_control(priv, false);
if (ret)
goto err_restore_sensors;
@@ -571,7 +563,7 @@ static int acpm_tmu_suspend(struct device *dev)
restore_ret);
err_restore_sensors:
- restore_ret = acpm_tmu_control(priv, true, false);
+ restore_ret = acpm_tmu_control(priv, true);
if (restore_ret)
dev_err(dev, "Failed to re-enable TMU after suspend failure: %d\n",
restore_ret);
@@ -584,34 +576,18 @@ 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;
+ int ret;
ret = pm_runtime_force_resume(dev);
if (ret)
- goto err_suspend_acpm;
+ return ret;
- ret = acpm_tmu_control(priv, true, true);
+ /* APB clock not required for this specific msg */
+ ret = ops->resume(handle, priv->mbox_chan_id);
if (ret)
- goto err_suspend_pm;
-
- return 0;
+ return ret;
-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;
+ return acpm_tmu_control(priv, true);
}
static int acpm_tmu_runtime_suspend(struct device *dev)
--
2.56.0.257.gf7497c7d27-goog