* [PATCH v4 01/13] soundwire: amd: fix SDW command timeout return value handling
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 02/13] soundwire: amd: return response on IMM status cleanup timeout Vijendar Mukunda
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
amd_sdw_send_cmd_get_resp() returned u64 but was returning -ETIMEDOUT
(a negative int) on readl_poll_timeout() failures, which silently
widens to a large u64 value.
Change the return type to int and pass the raw u64 response through an
out-parameter. Timeout is detected directly from readl_poll_timeout()
return value.
Update all callers: amd_program_scp_addr(), _amd_sdw_xfer_msg(),
amd_sdw_read_and_process_ping_status(), and amd_sdw_read_ping_status()
to check the return value and handle timeout explicitly.
Fixes: d8f48fbdfd9a ("soundwire: amd: Add support for AMD Manager driver")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 54 +++++++++++++++++++--------------
1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index a3316efdf8ac..307cbfdb9442 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -250,10 +250,9 @@ static void amd_sdw_ctl_word_prep(u32 *lower_word, u32 *upper_word, struct sdw_m
*lower_word = lower_data;
}
-static u64 amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lower_data,
- u32 upper_data)
+static int amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lower_data,
+ u32 upper_data, u64 *response)
{
- u64 resp;
u32 lower_resp, upper_resp;
u32 sts;
int ret;
@@ -291,9 +290,8 @@ static u64 amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lo
amd_manager->instance);
return ret;
}
- resp = upper_resp;
- resp = (resp << 32) | lower_resp;
- return resp;
+ *response = ((u64)upper_resp << 32) | lower_resp;
+ return 0;
}
static enum sdw_command_response
@@ -309,19 +307,22 @@ amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
scp_msg.buf = &msg->addr_page1;
scp_msg.flags = SDW_MSG_FLAG_WRITE;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
- response_buf[0] = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data);
+ if (amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[0])) {
+ dev_err_ratelimited(amd_manager->dev,
+ "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
+ return SDW_CMD_TIMEOUT;
+ }
scp_msg.addr = SDW_SCP_ADDRPAGE2;
scp_msg.buf = &msg->addr_page2;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
- response_buf[1] = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data);
+ if (amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[1])) {
+ dev_err_ratelimited(amd_manager->dev,
+ "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
+ return SDW_CMD_TIMEOUT;
+ }
for (index = 0; index < 2; index++) {
- if (response_buf[index] == -ETIMEDOUT) {
- dev_err_ratelimited(amd_manager->dev,
- "SCP_addrpage command timeout for Slave %d\n",
- msg->dev_num);
- return SDW_CMD_TIMEOUT;
- } else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
+ if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
dev_err_ratelimited(amd_manager->dev,
"SCP_addrpage NACKed for Slave %d\n",
@@ -366,11 +367,7 @@ static enum sdw_command_response amd_sdw_fill_msg_resp(struct amd_sdw_manager *a
if (msg->flags == SDW_MSG_FLAG_READ)
msg->buf[offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
} else {
- if (response == -ETIMEDOUT) {
- dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n",
- msg->dev_num);
- return SDW_CMD_TIMEOUT;
- } else if (response & AMD_SDW_MCP_RESP_NACK) {
+ if (response & AMD_SDW_MCP_RESP_NACK) {
dev_err_ratelimited(amd_manager->dev,
"command response NACK received for Slave %d\n",
msg->dev_num);
@@ -390,7 +387,11 @@ static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struc
u32 upper_data = 0, lower_data = 0;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, msg, cmd_offset);
- response = amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data);
+ if (amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response)) {
+ dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n",
+ msg->dev_num);
+ return SDW_CMD_TIMEOUT;
+ }
return amd_sdw_fill_msg_resp(amd_manager, msg, response, cmd_offset);
}
@@ -446,7 +447,12 @@ static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_man
u64 response;
mutex_lock(&amd_manager->bus.msg_lock);
- response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0);
+ if (amd_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
+ mutex_unlock(&amd_manager->bus.msg_lock);
+ dev_err_ratelimited(amd_manager->dev, "SDW%x ping status timeout\n",
+ amd_manager->instance);
+ return;
+ }
mutex_unlock(&amd_manager->bus.msg_lock);
amd_sdw_process_ping_status(response, amd_manager);
}
@@ -457,7 +463,11 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
u64 response;
u32 slave_stat;
- response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0);
+ if (amd_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
+ dev_err_ratelimited(amd_manager->dev, "SDW%x ping status timeout\n",
+ amd_manager->instance);
+ return 0;
+ }
/* slave status from ping response */
slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response);
slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 02/13] soundwire: amd: return response on IMM status cleanup timeout
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 01/13] soundwire: amd: fix SDW command timeout return value handling Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 03/13] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout Vijendar Mukunda
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
amd_sdw_send_cmd_get_resp() reads the hardware response after
IMM_RES_VALID is set, then writes IMM_RES_VALID to acknowledge and
polls until the bit clears (status cleanup handshake).
When the cleanup poll timed out, the code returned the error without
writing *response, causing callers to report SDW_CMD_TIMEOUT and
discard a valid response - upper_resp and lower_resp had already been
read before the cleanup write.
The cleanup timeout indicates the hardware was slow to acknowledge
the clear of IMM_RES_VALID, not that the command exchange failed.
Log the anomaly, then write *response and return 0 so callers receive
the valid data. The stuck IMM_RES_VALID bit is cleared at the next
invocation by the existing bad-state guard.
Fixes: d8f48fbdfd9a ("soundwire: amd: Add support for AMD Manager driver")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 307cbfdb9442..0b83126b5ce6 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -285,11 +285,9 @@ static int amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lo
writel(AMD_SDW_IMM_RES_VALID, amd_manager->mmio + ACP_SW_IMM_CMD_STS);
ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_IMM_CMD_STS, sts,
!(sts & AMD_SDW_IMM_RES_VALID), ACP_DELAY_US, AMD_SDW_TIMEOUT);
- if (ret) {
+ if (ret)
dev_err(amd_manager->dev, "SDW%x cmd status retry failed\n",
amd_manager->instance);
- return ret;
- }
*response = ((u64)upper_resp << 32) | lower_resp;
return 0;
}
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 03/13] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 01/13] soundwire: amd: fix SDW command timeout return value handling Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 02/13] soundwire: amd: return response on IMM status cleanup timeout Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 04/13] soundwire: amd: assert msg_lock is held in amd_sdw_read_ping_status() Vijendar Mukunda
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
amd_sdw_read_ping_status() returned 0 on a PING command timeout.
This was indistinguishable from a valid "no peripherals attached"
response, causing sdw_show_ping_status() to log a spurious disconnect
warning even when devices were actually present.
Add ping_slave_stat to struct amd_sdw_manager to cache the slave_stat
from the last successful ping response. On timeout, the cached value is
returned instead of 0, so sdw_show_ping_status() sees the last known
good state. The timeout itself is still reported via
dev_err_ratelimited() - the cache does not suppress the error.
ping_slave_stat is initialised to U32_MAX in probe and reset to
U32_MAX under msg_lock before any failable step in the POWER_OFF_MODE
resume path, so a partially-failed resume cannot leave stale
pre-suspend data behind for a subsequent ping timeout to report.
Fixes: d22c1c2fcfb0 ("soundwire: amd: fix SDW command timeout return value handling")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 18 +++++++++++++++++-
include/linux/soundwire/sdw_amd.h | 4 ++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 0b83126b5ce6..b75d0104e28c 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -451,6 +451,9 @@ static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_man
amd_manager->instance);
return;
}
+ amd_manager->ping_slave_stat =
+ FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response) |
+ (FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8);
mutex_unlock(&amd_manager->bus.msg_lock);
amd_sdw_process_ping_status(response, amd_manager);
}
@@ -464,12 +467,13 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
if (amd_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
dev_err_ratelimited(amd_manager->dev, "SDW%x ping status timeout\n",
amd_manager->instance);
- return 0;
+ return amd_manager->ping_slave_stat == U32_MAX ? 0 : amd_manager->ping_slave_stat;
}
/* slave status from ping response */
slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response);
slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
dev_dbg(amd_manager->dev, "slave_stat:0x%x\n", slave_stat);
+ amd_manager->ping_slave_stat = slave_stat;
return slave_stat;
}
@@ -1085,6 +1089,7 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
dev_err(dev, "mmio not found\n");
return -ENOMEM;
}
+ amd_manager->ping_slave_stat = U32_MAX;
amd_manager->instance = pdata->instance;
amd_manager->mmio = amd_manager->acp_mmio +
(amd_manager->instance * SDW_MANAGER_REG_OFFSET);
@@ -1412,6 +1417,17 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
}
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
+ /*
+ * POWER_OFF_MODE tears the bus down completely; invalidate the
+ * cached ping status before any failable step so that a
+ * partially-failed resume leaves the first post-resume ping
+ * timeout returning 0 rather than stale pre-suspend data.
+ * CLK_STOP_MODE leaves peripherals attached, so the cache
+ * remains valid there.
+ */
+ mutex_lock(&amd_manager->bus.msg_lock);
+ amd_manager->ping_slave_stat = U32_MAX;
+ mutex_unlock(&amd_manager->bus.msg_lock);
if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
ret = amd_sdw_host_wake_enable(amd_manager, false);
if (ret)
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 470360a2723c..e4375542aad2 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -76,6 +76,9 @@ struct sdw_amd_dai_runtime {
* @acp_rev: acp pci device revision id
* @clk_stopped: flag set to true when clock is stopped
* @power_mode_mask: flag interprets amd SoundWire manager power mode
+ * @ping_slave_stat: cached slave status from the last ping
+ * transaction that returned a response;
+ * initialised to U32_MAX ("no valid reading yet")
* @dai_runtime_array: dai runtime array
*/
struct amd_sdw_manager {
@@ -105,6 +108,7 @@ struct amd_sdw_manager {
u32 wake_en_mask;
u32 power_mode_mask;
u32 acp_rev;
+ u32 ping_slave_stat;
bool clk_stopped;
struct sdw_amd_dai_runtime **dai_runtime_array;
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 04/13] soundwire: amd: assert msg_lock is held in amd_sdw_read_ping_status()
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (2 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 03/13] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 05/13] soundwire: amd: fix work drain ordering in remove path Vijendar Mukunda
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
amd_sdw_read_ping_status() is always called from sdw_show_ping_status()
which holds bus->msg_lock. Add lockdep_assert_held() to document this
locking requirement and catch any future callers that omit the lock.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index b75d0104e28c..5e9d228d09d0 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -464,6 +464,7 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
u64 response;
u32 slave_stat;
+ lockdep_assert_held(&bus->msg_lock);
if (amd_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
dev_err_ratelimited(amd_manager->dev, "SDW%x ping status timeout\n",
amd_manager->instance);
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 05/13] soundwire: amd: fix work drain ordering in remove path
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (3 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 04/13] soundwire: amd: assert msg_lock is held in amd_sdw_read_ping_status() Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 06/13] soundwire: amd: pm_runtime guard " Vijendar Mukunda
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
amd_sdw_manager_remove() cancelled amd_sdw_work but not
amd_sdw_irq_thread. Since amd_sdw_irq_thread() calls
schedule_work(&amd_sdw_work), an in-flight irq_thread item can
re-queue amd_sdw_work after its cancel returns, defeating the
cancellation.
Fix by calling amd_disable_sdw_interrupts() first to quiesce the
hardware IRQ source, then cancel_work_sync() for amd_sdw_irq_thread,
then cancel_work_sync() for amd_sdw_work. The existing
cancel_work_sync(amd_sdw_work) is also moved to after
amd_disable_sdw_interrupts() so that any work item queued between the
old cancel position and the interrupt disable cannot escape draining.
synchronize_irq() is deliberately not used before the
cancel_work_sync() calls. Once SoundWire interrupts are masked, no new
IRQ deliveries can occur. An IRQ handler already in flight may still
queue amd_sdw_irq_thread, so cancel_work_sync() is used to drain both
amd_sdw_irq_thread and any amd_sdw_work items it may have scheduled.
This fully quiesces the driver workqueues, making synchronize_irq()
unnecessary.
Fixes: f93b697ed98e ("soundwire: amd: cancel pending slave status handling workqueue during remove sequence")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 5e9d228d09d0..a82714cc2f6c 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1169,8 +1169,9 @@ static void amd_sdw_manager_remove(struct platform_device *pdev)
int ret;
pm_runtime_disable(&pdev->dev);
- cancel_work_sync(&amd_manager->amd_sdw_work);
amd_disable_sdw_interrupts(amd_manager);
+ cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
+ cancel_work_sync(&amd_manager->amd_sdw_work);
sdw_bus_master_delete(&amd_manager->bus);
ret = amd_disable_sdw_manager(amd_manager);
if (ret)
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 06/13] soundwire: amd: pm_runtime guard in remove path
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (4 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 05/13] soundwire: amd: fix work drain ordering in remove path Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 07/13] soundwire: amd: fix ctx leak when sdw_amd_startup() fails Vijendar Mukunda
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
Guard pm_runtime_disable() so it is only called when runtime PM
was actually enabled. amd_sdw_manager_start() calls pm_runtime_enable()
only at the very end, after several fallible hardware init steps. If
sdw_amd_startup() fails mid-loop (one manager started, the next fails
before pm_runtime_enable()), sdw_amd_exit() triggers
platform_device_unregister() for all managers. Calling
pm_runtime_disable() on the partially-started manager finds
disable_depth already at its initial value of 1, silently increments it
to 2 and returns without a warning, so a later pm_runtime_enable() would
only bring it back to 1 and leave runtime PM disabled. Use
pm_runtime_enabled() to skip the call when it was never paired with an
enable.
Fixes: f93b697ed98e ("soundwire: amd: cancel pending slave status handling workqueue during remove sequence")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index a82714cc2f6c..ccd97bc6b757 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1168,7 +1168,8 @@ static void amd_sdw_manager_remove(struct platform_device *pdev)
struct amd_sdw_manager *amd_manager = dev_get_drvdata(&pdev->dev);
int ret;
- pm_runtime_disable(&pdev->dev);
+ if (pm_runtime_enabled(&pdev->dev))
+ pm_runtime_disable(&pdev->dev);
amd_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 07/13] soundwire: amd: fix ctx leak when sdw_amd_startup() fails
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (5 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 06/13] soundwire: amd: pm_runtime guard " Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 08/13] soundwire: amd: propagate amd_init_sdw_manager() error on resume Vijendar Mukunda
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
sdw_amd_probe() allocates a context and registers platform devices via
sdw_amd_probe_controller(), then calls sdw_amd_startup() to start each
manager. If sdw_amd_startup() fails, the error was returned directly,
leaving *sdw_ctx non-NULL with the allocated context and its registered
platform devices leaked.
Call sdw_amd_exit() on the failure path to unregister the platform
devices and free ctx, then clear *sdw_ctx so callers observe a
consistent NULL on error.
Fixes: ed5e8741b8db ("soundwire: amd: refactor amd soundwire manager device node creation")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_init.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c
index 8e419ddfa516..88004d59322e 100644
--- a/drivers/soundwire/amd_init.c
+++ b/drivers/soundwire/amd_init.c
@@ -166,11 +166,18 @@ static int sdw_amd_startup(struct sdw_amd_ctx *ctx)
int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx)
{
+ int ret;
+
*sdw_ctx = sdw_amd_probe_controller(res);
if (!*sdw_ctx)
return -ENODEV;
- return sdw_amd_startup(*sdw_ctx);
+ ret = sdw_amd_startup(*sdw_ctx);
+ if (ret) {
+ sdw_amd_exit(*sdw_ctx);
+ *sdw_ctx = NULL;
+ }
+ return ret;
}
EXPORT_SYMBOL_NS(sdw_amd_probe, "SOUNDWIRE_AMD_INIT");
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 08/13] soundwire: amd: propagate amd_init_sdw_manager() error on resume
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (6 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 07/13] soundwire: amd: fix ctx leak when sdw_amd_startup() fails Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 09/13] soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend Vijendar Mukunda
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
In amd_resume_runtime() POWER_OFF_MODE, amd_init_sdw_manager() was
called without capturing its return value. The function performs
hardware bus resets using readl_poll_timeout() and can return
-ETIMEDOUT if the hardware does not respond. When that happens,
execution fell through to amd_enable_sdw_interrupts() and
amd_enable_sdw_manager() on hardware that may still be in a broken
reset state.
Fix by capturing the return value and returning early on failure.
Fixes: 81ff58ff71ad ("soundwire: amd: add runtime pm ops for AMD SoundWire manager driver")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index ccd97bc6b757..8e1339ed44ad 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1452,7 +1452,9 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
ret = amd_sdw_clk_init_ctrl(amd_manager);
if (ret)
return ret;
- amd_init_sdw_manager(amd_manager);
+ ret = amd_init_sdw_manager(amd_manager);
+ if (ret)
+ return ret;
amd_enable_sdw_interrupts(amd_manager);
ret = amd_enable_sdw_manager(amd_manager);
if (ret)
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 09/13] soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (7 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 08/13] soundwire: amd: propagate amd_init_sdw_manager() error on resume Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 10/13] soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths Vijendar Mukunda
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
amd_deinit_sdw_manager() was a small helper that bundled
amd_disable_sdw_interrupts() and amd_disable_sdw_manager() together.
Expand the two POWER_OFF_MODE call sites in amd_suspend() and
amd_suspend_runtime() to call those two functions directly, then remove
the helper. No functional change.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 8e1339ed44ad..570a4f0c8372 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -146,12 +146,6 @@ static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
writel(0x00, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK);
}
-static int amd_deinit_sdw_manager(struct amd_sdw_manager *amd_manager)
-{
- amd_disable_sdw_interrupts(amd_manager);
- return amd_disable_sdw_manager(amd_manager);
-}
-
static void amd_sdw_set_frameshape(struct amd_sdw_manager *amd_manager)
{
u32 frame_size;
@@ -1332,7 +1326,8 @@ static int __maybe_unused amd_suspend(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- ret = amd_deinit_sdw_manager(amd_manager);
+ amd_disable_sdw_interrupts(amd_manager);
+ ret = amd_disable_sdw_manager(amd_manager);
if (ret)
return ret;
}
@@ -1376,7 +1371,8 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- ret = amd_deinit_sdw_manager(amd_manager);
+ amd_disable_sdw_interrupts(amd_manager);
+ ret = amd_disable_sdw_manager(amd_manager);
if (ret)
return ret;
}
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 10/13] soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (8 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 09/13] soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 11/13] soundwire: amd: fix interrupt gate and work drain ordering in PM ops Vijendar Mukunda
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
Replace open-ended acp_rev >= ACP70_PCI_REV_ID comparisons in
amd_suspend(), amd_suspend_runtime(), and amd_resume_runtime() with
explicit per-revision handling enumerating ACP63, ACP70, ACP71, and
ACP72, so that every supported revision is handled explicitly.
The host wake enable step is guarded by acp_rev at six PM call sites.
Factor that repeated block into amd_sdw_host_wake_enable_by_rev() to
avoid duplicating the switch/case at every site. The remaining device
state transitions keep an inline switch/case as one of them carries
additional PME handling.
An unrecognised revision cannot reach these PM callbacks because
amd_sdw_manager_probe() already validates acp_rev with the same switch
and fails registration for anything unknown. The switch default therefore
emits dev_err() and returns -EINVAL to make the unexpected case visible
while keeping the error path consistent across all PM operations.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 98 ++++++++++++++++++++++-----------
1 file changed, 65 insertions(+), 33 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 570a4f0c8372..a685bd7da52b 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1288,6 +1288,23 @@ static int __maybe_unused amd_pm_prepare(struct device *dev)
return 0;
}
+static int __maybe_unused amd_sdw_host_wake_enable_by_rev(struct amd_sdw_manager *amd_manager,
+ bool enable)
+{
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ break;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ return amd_sdw_host_wake_enable(amd_manager, enable);
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int __maybe_unused amd_suspend(struct device *dev)
{
struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
@@ -1303,22 +1320,18 @@ static int __maybe_unused amd_suspend(struct device *dev)
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
cancel_work_sync(&amd_manager->amd_sdw_work);
amd_sdw_wake_enable(amd_manager, false);
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
- ret = amd_sdw_host_wake_enable(amd_manager, false);
- if (ret)
- return ret;
- }
+ ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
+ if (ret)
+ return ret;
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
cancel_work_sync(&amd_manager->amd_sdw_work);
amd_sdw_wake_enable(amd_manager, false);
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
- ret = amd_sdw_host_wake_enable(amd_manager, false);
- if (ret)
- return ret;
- }
+ ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
+ if (ret)
+ return ret;
/*
* As per hardware programming sequence on AMD platforms,
* clock stop should be invoked first before powering-off
@@ -1331,10 +1344,19 @@ static int __maybe_unused amd_suspend(struct device *dev)
if (ret)
return ret;
}
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ break;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
if (ret)
return ret;
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return -EINVAL;
}
return 0;
}
@@ -1353,21 +1375,17 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
amd_sdw_wake_enable(amd_manager, true);
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
- ret = amd_sdw_host_wake_enable(amd_manager, true);
- if (ret)
- return ret;
- }
+ ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
+ if (ret)
+ return ret;
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
amd_sdw_wake_enable(amd_manager, true);
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
- ret = amd_sdw_host_wake_enable(amd_manager, true);
- if (ret)
- return ret;
- }
+ ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
+ if (ret)
+ return ret;
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
@@ -1376,7 +1394,12 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
if (ret)
return ret;
}
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ break;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
if (ret)
return ret;
@@ -1388,6 +1411,10 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
dev_dbg(amd_manager->dev, "ACP_PME_EN:0x%x\n", val);
}
}
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return -EINVAL;
}
return 0;
}
@@ -1409,11 +1436,9 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
ret = amd_sdw_clock_stop_exit(amd_manager);
if (ret)
return ret;
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
- ret = amd_sdw_host_wake_enable(amd_manager, false);
- if (ret)
- return ret;
- }
+ ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
+ if (ret)
+ return ret;
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
/*
@@ -1427,11 +1452,9 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
mutex_lock(&amd_manager->bus.msg_lock);
amd_manager->ping_slave_stat = U32_MAX;
mutex_unlock(&amd_manager->bus.msg_lock);
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
- ret = amd_sdw_host_wake_enable(amd_manager, false);
- if (ret)
- return ret;
- }
+ ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
+ if (ret)
+ return ret;
val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL);
if (val) {
val |= AMD_SDW_CLK_RESUME_REQ;
@@ -1457,10 +1480,19 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
return ret;
amd_sdw_set_frameshape(amd_manager);
}
- if (amd_manager->acp_rev >= ACP70_PCI_REV_ID) {
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ break;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D0);
if (ret)
return ret;
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return -EINVAL;
}
return 0;
}
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 11/13] soundwire: amd: fix interrupt gate and work drain ordering in PM ops
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (9 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 10/13] soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 12/13] soundwire: amd: fix IRQ thread dropping STATUS_0TO7 on simultaneous wake Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 13/13] soundwire: amd: fix STATUS_8TO11 double-clear race in wake handler Vijendar Mukunda
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
The CLK_STOP_MODE branches in amd_suspend() and amd_suspend_runtime()
did not close the ACP_EXTERNAL_INTR_CNTL delivery gate before draining
amd_sdw_irq_thread and amd_sdw_work. An ISR already running when the
drains completed could re-queue work after cancel_work_sync() returned,
racing amd_sdw_clock_stop().
In both CLK_STOP_MODE paths, amd_sdw_wake_enable() was also called
before amd_enable_sdw_interrupts(). Since amd_enable_sdw_interrupts()
writes the interrupt mask unconditionally, it clobbered the wake bit
set by amd_sdw_wake_enable().
For both amd_suspend() and amd_suspend_runtime():
- CLK_STOP_MODE: add amd_disable_sdw_interrupts() and
cancel_work_sync(irq_thread) before the work drain; reopen the gate
with amd_enable_sdw_interrupts() after clock stop, then call
amd_sdw_wake_enable() so the wake bit is not clobbered.
- POWER_OFF_MODE: add amd_disable_sdw_interrupts() and
cancel_work_sync(irq_thread) before the work drain; remove the
redundant post-clock-stop amd_disable_sdw_interrupts() call.
Fixes: 86a4371b7697 ("soundwire: amd: fix for handling slave alerts after link is down")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index a685bd7da52b..d7289b840513 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1318,15 +1318,20 @@ static int __maybe_unused amd_suspend(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+ amd_disable_sdw_interrupts(amd_manager);
+ cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
- amd_sdw_wake_enable(amd_manager, false);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
if (ret)
return ret;
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
+ amd_enable_sdw_interrupts(amd_manager);
+ amd_sdw_wake_enable(amd_manager, false);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+ amd_disable_sdw_interrupts(amd_manager);
+ cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
amd_sdw_wake_enable(amd_manager, false);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
@@ -1339,7 +1344,6 @@ static int __maybe_unused amd_suspend(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- amd_disable_sdw_interrupts(amd_manager);
ret = amd_disable_sdw_manager(amd_manager);
if (ret)
return ret;
@@ -1374,14 +1378,21 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
return 0;
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
- amd_sdw_wake_enable(amd_manager, true);
+ amd_disable_sdw_interrupts(amd_manager);
+ cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
+ cancel_work_sync(&amd_manager->amd_sdw_work);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
if (ret)
return ret;
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
+ amd_enable_sdw_interrupts(amd_manager);
+ amd_sdw_wake_enable(amd_manager, true);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+ amd_disable_sdw_interrupts(amd_manager);
+ cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
+ cancel_work_sync(&amd_manager->amd_sdw_work);
amd_sdw_wake_enable(amd_manager, true);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
if (ret)
@@ -1389,7 +1400,6 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- amd_disable_sdw_interrupts(amd_manager);
ret = amd_disable_sdw_manager(amd_manager);
if (ret)
return ret;
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 12/13] soundwire: amd: fix IRQ thread dropping STATUS_0TO7 on simultaneous wake
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (10 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 11/13] soundwire: amd: fix interrupt gate and work drain ordering in PM ops Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
2026-10-26 4:43 ` [PATCH v4 13/13] soundwire: amd: fix STATUS_8TO11 double-clear race in wake handler Vijendar Mukunda
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
When a wake interrupt and a device-state change arrive simultaneously,
the IRQ thread used an early return after detecting the wake bit,
silently discarding STATUS_0TO7.
When the manager is already at D0, pm_request_resume() is a no-op and
amd_resume_runtime() is never called, so the dropped ATTACHED event
cannot be recovered and jack detection stops working.
Remove the early return: call amd_sdw_process_wake_event() inside a
conditional block and fall through to the normal slave-status processing
path. Mask AMD_SDW_WAKE_STAT_MASK out of the local status_change_8to11
copy after handling the wake event so the remaining interrupt bits are
processed without re-entering the wake path.
Fixes: 66c878838f37 ("soundwire: amd: handle SoundWire wake enable interrupt")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index d7289b840513..16dd17f7a343 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1019,8 +1019,11 @@ static void amd_sdw_irq_thread(struct work_struct *work)
dev_dbg(amd_manager->dev, "[SDW%d] SDW INT: 0to7=0x%x, 8to11=0x%x\n",
amd_manager->instance, status_change_0to7, status_change_8to11);
- if (status_change_8to11 & AMD_SDW_WAKE_STAT_MASK)
- return amd_sdw_process_wake_event(amd_manager);
+
+ if (status_change_8to11 & AMD_SDW_WAKE_STAT_MASK) {
+ amd_sdw_process_wake_event(amd_manager);
+ status_change_8to11 &= ~AMD_SDW_WAKE_STAT_MASK;
+ }
if (status_change_8to11 & AMD_SDW_PREQ_INTR_STAT) {
amd_sdw_read_and_process_ping_status(amd_manager);
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v4 13/13] soundwire: amd: fix STATUS_8TO11 double-clear race in wake handler
2026-10-26 4:43 [PATCH v4 00/13] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (11 preceding siblings ...)
2026-10-26 4:43 ` [PATCH v4 12/13] soundwire: amd: fix IRQ thread dropping STATUS_0TO7 on simultaneous wake Vijendar Mukunda
@ 2026-10-26 4:43 ` Vijendar Mukunda
12 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-10-26 4:43 UTC (permalink / raw)
To: vkoul
Cc: broonie, alsa-devel, yung-chuan.liao, pierre-louis.bossart,
Basavaraj.Hiregoudar, Sunil-kumar.Dommati, venkataprasad.potturu,
Syed.SabaKareem, Mario.Limonciello, Richard.Gong, linux-sound,
linux-kernel, Vijendar Mukunda
amd_sdw_process_wake_event() cleared ACP_SW_STATE_CHANGE_STATUS_8TO11,
and amd_sdw_irq_thread() cleared it again at the tail. A new interrupt
arriving between the two clears would be wiped before being read,
causing an event to be silently lost.
Remove the ACP_SW_STATE_CHANGE_STATUS_8TO11 clear from
amd_sdw_process_wake_event(), leaving the single tail clear in
amd_sdw_irq_thread() as the only write to that register per invocation.
Fixes: 66c878838f37 ("soundwire: amd: handle SoundWire wake enable interrupt")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 16dd17f7a343..38a9aa1af59a 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1002,7 +1002,6 @@ static void amd_sdw_process_wake_event(struct amd_sdw_manager *amd_manager)
dev_dbg(amd_manager->dev, "SoundWire Wake event reported\n");
pm_request_resume(amd_manager->dev);
writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
- writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_8TO11);
}
static void amd_sdw_irq_thread(struct work_struct *work)
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread