[PATCH 09/15] thermal/drivers/samsung: acpm-tmu: Fix IRQ locking and error handling

From: Tudor Ambarus

Date: Mon Oct 26 2026 - 12:05:53 EST


acpm_tmu_has_pending_irq() acquires and releases sensor->lock around the
INTPEND read, and acpm_tmu_thread_fn() re-acquires it to call
ops->clear_tz_irq(), leaving a window where concurrent .set_trips() or
.change_mode() calls can modify sensor state in between. Moreover, if
ops->clear_tz_irq() fails, acpm_tmu_thread_fn() still sets handled = true
and updates the thermal zone while INTPEND remains asserted; conversely,
returning IRQ_NONE on a level-high IRQF_ONESHOT line re-triggers the
interrupt up to 100,000 times before the spurious IRQ detector disables
it, so unthrottled dev_err() calls flood the kernel log.

Replace acpm_tmu_has_pending_irq() with acpm_tmu_clear_pending_irq() so
both the INTPEND check and ops->clear_tz_irq() run under a single
guard(mutex)(&sensor->lock), only mark the IRQ handled and update the
thermal zone when an interrupt was pending and cleared, and rate-limit
the error messages on both IRQ_NONE failure paths in the threaded IRQ
handler.

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 | 42 ++++++++++++++++++--------------------
1 file changed, 20 insertions(+), 22 deletions(-)

diff --git a/drivers/thermal/samsung/acpm-tmu.c b/drivers/thermal/samsung/acpm-tmu.c
index 65be8884a8c5..42b338d8d093 100644
--- a/drivers/thermal/samsung/acpm-tmu.c
+++ b/drivers/thermal/samsung/acpm-tmu.c
@@ -330,11 +330,12 @@ static const struct thermal_zone_device_ops acpm_tmu_sensor_ops = {
.set_trips = acpm_tmu_set_trips,
};

-static int acpm_tmu_has_pending_irq(struct acpm_tmu_sensor *sensor,
- bool *pending_irq)
+static bool acpm_tmu_clear_pending_irq(struct acpm_tmu_sensor *sensor)
{
struct acpm_tmu_priv *priv = sensor->priv;
+ const struct acpm_tmu_ops *ops = &priv->handle->ops->tmu;
unsigned long mask = sensor->group->mask;
+ bool pending_irq = false;
int i, ret;
u32 val;

@@ -343,53 +344,50 @@ static int acpm_tmu_has_pending_irq(struct acpm_tmu_sensor *sensor,
for_each_set_bit(i, &mask, EXYNOS_TMU_SENSORS_MAX_COUNT) {
ret = regmap_field_read(priv->regmap_fields[i], &val);
if (ret)
- return ret;
+ return false;

if (val) {
- *pending_irq = true;
+ pending_irq = true;
break;
}
}

- return 0;
+ if (!pending_irq)
+ return false;
+
+ ret = ops->clear_tz_irq(priv->handle, priv->mbox_chan_id,
+ sensor->group->id);
+ if (ret) {
+ dev_err_ratelimited(priv->dev,
+ "Sensor %u: failed to clear IRQ (%d)\n",
+ sensor->group->id, ret);
+ return false;
+ }
+
+ return true;
}

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);
+ dev_err_ratelimited(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)
+ if (!sensor->tzd || !acpm_tmu_clear_pending_irq(sensor))
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);
}

--
2.56.0.257.gf7497c7d27-goog