[PATCH v5 17/25] soundwire: amd: use interrupt_enabled flag to guard IRQ work scheduling

From: Vijendar Mukunda

Date: Sat Nov 07 2026 - 12:59:52 EST


Use a boolean interrupt_enabled flag to guard IRQ work scheduling and
prevent new work from being queued by an in-flight IRQ during the window
between starting to disable hardware interrupts and the masks taking
effect.

- Add interrupt_enabled to struct amd_sdw_manager.
- Set it to true at the end of amd_enable_sdw_interrupts(), after all
hardware mask registers have been written.
- Clear it to false at the start of amd_disable_sdw_interrupts(), before
touching any hardware registers, so that any IRQ that fires during the
mask-register writes sees the flag as false and skips scheduling.
- Guard schedule_work(&amd_manager->amd_sdw_work) in
amd_sdw_irq_thread() with the flag check.

Reported-by: Lee Jones <lee@xxxxxxxxxx>
Closes: https://sashiko.dev/#/bug/linux-ab458b2e-f505-4a41-8238-a93ceb5d9c77
Fixes: 65f93e4096a0 ("soundwire: amd: add SoundWire manager interrupt handling")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@xxxxxxx>
---
drivers/soundwire/amd_manager.c | 12 +++++++++++-
include/linux/soundwire/sdw_amd.h | 4 ++++
2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 01aa051cf63f..108553b0f32e 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -129,12 +129,22 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
writel(AMD_SDW_IRQ_MASK_8TO11, amd_manager->mmio +
ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);
writel(AMD_SDW_IRQ_ERROR_MASK, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK);
+ amd_manager->interrupt_enabled = true;
}

static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
{
u32 irq_mask;

+ /*
+ * Clear the flag before updating hardware masks so that any IRQ
+ * that fires during the mask-register writes sees the flag as false
+ * and does not queue new work. Once the mask registers are written,
+ * the ACP hardware will not assert any new SDW interrupt, so no
+ * synchronize_irq() is required after this function returns.
+ */
+ amd_manager->interrupt_enabled = false;
+
mutex_lock(amd_manager->acp_sdw_lock);
irq_mask = sdw_manager_reg_mask_array[amd_manager->instance];
amd_updatel(amd_manager->acp_mmio, ACP_EXTERNAL_INTR_CNTL(amd_manager->instance),
@@ -1143,7 +1153,7 @@ static void amd_sdw_irq_thread(struct work_struct *work)
/* Check for the updated status on peripheral device */
amd_sdw_update_slave_status(status_change_0to7, status_change_8to11, amd_manager);
}
- if (status_change_8to11 || status_change_0to7)
+ if ((status_change_8to11 || status_change_0to7) && amd_manager->interrupt_enabled)
schedule_work(&amd_manager->amd_sdw_work);
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_8TO11);
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7);
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 2d26f357d842..04b95379a724 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -85,6 +85,9 @@ struct sdw_amd_dai_runtime {
* @ping_slave_stat: cached slave status from the last ping
* transaction that returned a response;
* initialised to U32_MAX ("no valid reading yet")
+ * @interrupt_enabled: set when hardware interrupts are enabled; cleared
+ * before disabling to prevent new work from being queued
+ * by an in-flight IRQ during teardown
* @dai_runtime_array: dai runtime array
*/
struct amd_sdw_manager {
@@ -117,6 +120,7 @@ struct amd_sdw_manager {
u32 ping_slave_stat;
bool clk_stopped;
bool pad_fully_torn_down;
+ bool interrupt_enabled;

struct sdw_amd_dai_runtime **dai_runtime_array;
};
--
2.53.0