[PATCH 1/2] ASoC: cs35l33: drain threaded IRQ before runtime suspend

From: Runyu Xiao

Date: Thu Jun 11 2026 - 12:21:51 EST


cs35l33_runtime_suspend() currently switches the codec into
regcache_cache_only(true) and powers it down without first quiescing the
threaded IRQ registered by devm_request_threaded_irq(). That leaves a
window where cs35l33_irq_thread() can still run after suspend has closed
off live register access.

A running system can reach this during runtime PM while the driver still
has critical fault IRQs unmasked. If the threaded handler runs in that
window, it reads volatile INT_STATUS_1/2 after cache_only has been
enabled, ignores the regmap_read() failures, and can still drive the
AMP_SHORT_RLS, CAL_ERR_RLS, OTE_RLS, and OTW_RLS release paths.

Use disable_irq() before entering cache_only/power-off so any in-flight
threaded handler is drained and no new IRQ thread can run during the
suspended state. Re-enable the IRQ only after runtime_resume() has
restored live register access with regcache_sync(). Since probe only
warns if devm_request_threaded_irq() fails, track whether the IRQ was
actually installed before disabling or re-enabling it.

Fixes: 3333cb7187b9 ("ASoC: cs35l33: Initial commit of the cs35l33 CODEC driver.")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Runyu Xiao <runyu.xiao@xxxxxxxxxx>
---
sound/soc/codecs/cs35l33.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index c927592f90c9..af9dad199084 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -40,6 +40,7 @@ struct cs35l33_private {
struct regmap *regmap;
struct gpio_desc *reset_gpio;
bool amp_cal;
+ bool irq_requested;
int mclk_int;
struct regulator_bulk_data core_supplies[2];
int num_core_supplies;
@@ -881,6 +882,9 @@ static int cs35l33_runtime_resume(struct device *dev)
goto err;
}

+ if (cs35l33->irq_requested)
+ enable_irq(to_i2c_client(dev)->irq);
+
return 0;

err:
@@ -900,6 +904,10 @@ static int cs35l33_runtime_suspend(struct device *dev)
/* redo the calibration in next power up */
cs35l33->amp_cal = false;

+ /* Drain and block the threaded IRQ before cache_only/power-off. */
+ if (cs35l33->irq_requested)
+ disable_irq(to_i2c_client(dev)->irq);
+
regcache_cache_only(cs35l33->regmap, true);
regcache_mark_dirty(cs35l33->regmap);
regulator_bulk_disable(cs35l33->num_core_supplies,
@@ -1154,10 +1162,12 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client)
}

ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
- cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
- "cs35l33", cs35l33);
+ cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l33", cs35l33);
if (ret != 0)
dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+ else
+ cs35l33->irq_requested = true;

/* We could issue !RST or skip it based on AMP topology */
cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
--
2.34.1