* [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes
@ 2026-09-10 19:00 Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 1/8] soundwire: amd: fix SDW command timeout return value handling Vijendar Mukunda
` (8 more replies)
0 siblings, 9 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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
This series collects eight bug fixes for the AMD SoundWire manager
driver (ACP6.3/7.0/7.1/7.2). The patches address correctness issues
in command response handling, slave status caching, work-queue teardown
ordering, resource cleanup on probe failure, and power-management
suspend/resume sequences.
Patch 1 fixes a silent type widening in amd_sdw_send_cmd_get_resp()
where -ETIMEDOUT was returned through a u64, producing a large positive
value instead of an error.
Patch 2 caches the last successful ping slave status so that a command
timeout during sdw_show_ping_status() does not report a spurious
"no peripherals attached" warning.
Patch 3 fixes the work-drain sequence in amd_sdw_manager_remove(): the
interrupt delivery gate must be closed before cancel_work_sync() to
prevent an in-flight IRQ handler from re-queuing work after the cancel
returns. A pm_runtime_disable() imbalance on partially-started managers
is also corrected.
Patch 4 adds the missing sdw_amd_exit() call and ctx pointer clear on
the sdw_amd_startup() failure path in sdw_amd_probe().
Patch 5 propagates the amd_init_sdw_manager() return value in
amd_resume_runtime() instead of ignoring it.
Patch 6 removes the amd_deinit_sdw_manager() helper and expands its
two call sites in the POWER_OFF suspend path to direct calls, making
the sequence explicit with no functional change.
Patch 7 replaces open-ended acp_rev >= ACP70_PCI_REV_ID comparisons
in the PM callbacks with explicit per-revision handling covering
ACP63, ACP70, ACP71 and ACP72, with a dev_err + -EINVAL default. The
repeated host-wake-by-revision block is factored into a helper,
amd_sdw_host_wake_enable_by_rev().
Patch 8 fixes the interrupt gate and work drain ordering in all four
suspend mode/path combinations: the ACP_EXTERNAL_INTR_CNTL gate must
be closed before cancel_work_sync() calls to prevent a racing ISR from
re-queuing work after the drain completes.
Vijendar Mukunda (8):
soundwire: amd: fix SDW command timeout return value handling
soundwire: amd: cache ping slave status to avoid spurious disconnect
on timeout
soundwire: amd: fix work drain ordering and pm_runtime guard in remove
path
soundwire: amd: fix ctx leak when sdw_amd_startup() fails
soundwire: amd: propagate amd_init_sdw_manager() error on resume
soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend
soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths
soundwire: amd: fix interrupt gate and work drain ordering in PM ops
drivers/soundwire/amd_init.c | 9 +-
drivers/soundwire/amd_manager.c | 204 ++++++++++++++++++++----------
include/linux/soundwire/sdw_amd.h | 3 +
3 files changed, 148 insertions(+), 68 deletions(-)
--
2.48.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/8] soundwire: amd: fix SDW command timeout return value handling
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
@ 2026-09-10 19:00 ` Vijendar Mukunda
2026-09-11 17:48 ` Mario Limonciello
2026-09-10 19:00 ` [PATCH 2/8] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout Vijendar Mukunda
` (7 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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. In
amd_sdw_read_and_process_ping_status(), the mutex unlock is also moved
to after amd_sdw_process_ping_status() so the lock is held across the
full status processing step.
Fixes: d8f48fbdfd9a ("soundwire: amd: Add support for AMD Manager driver")
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 56 +++++++++++++++++++--------------
1 file changed, 33 insertions(+), 23 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index a3316efdf8ac..fa3f4e797edd 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,9 +447,14 @@ 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);
- mutex_unlock(&amd_manager->bus.msg_lock);
+ 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;
+ }
amd_sdw_process_ping_status(response, amd_manager);
+ mutex_unlock(&amd_manager->bus.msg_lock);
}
static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
@@ -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.48.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/8] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 1/8] soundwire: amd: fix SDW command timeout return value handling Vijendar Mukunda
@ 2026-09-10 19:00 ` Vijendar Mukunda
2026-09-13 19:59 ` Pierre-Louis Bossart
2026-09-10 19:00 ` [PATCH 3/8] soundwire: amd: fix work drain ordering and pm_runtime guard in remove path Vijendar Mukunda
` (6 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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
Add ping_slave_stat to struct amd_sdw_manager to cache the slave_stat
from the last successful ping response. On timeout,
amd_sdw_read_ping_status() returns the cached value (or 0 if no
successful response yet) to avoid the spurious "no peripherals
attached" warning from sdw_show_ping_status().
ping_slave_stat is initialised to U32_MAX in probe and reset to
U32_MAX under msg_lock on entry to the POWER_OFF_MODE resume path,
before any hardware step that can fail and return early, so that a
partially-failed resume cannot leave a stale pre-suspend value behind
for a subsequent ping timeout to report.
Add lockdep_assert_held() in amd_sdw_read_ping_status() to document
that the caller must hold msg_lock.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 21 ++++++++++++++++++++-
include/linux/soundwire/sdw_amd.h | 3 +++
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index fa3f4e797edd..a57b59609bfe 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -453,6 +453,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);
amd_sdw_process_ping_status(response, amd_manager);
mutex_unlock(&amd_manager->bus.msg_lock);
}
@@ -463,15 +466,19 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
u64 response;
u32 slave_stat;
+ /* Called by sdw_show_ping_status() which holds msg_lock. */
+ 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);
- 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;
}
@@ -1087,6 +1094,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);
@@ -1414,6 +1422,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 on entry, before any step that can fail and
+ * return early, 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..12537904e34a 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -76,6 +76,8 @@ 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 successful ping response;
+ * initialised to U32_MAX ("no valid reading yet")
* @dai_runtime_array: dai runtime array
*/
struct amd_sdw_manager {
@@ -105,6 +107,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.48.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 3/8] soundwire: amd: fix work drain ordering and pm_runtime guard in remove path
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 1/8] soundwire: amd: fix SDW command timeout return value handling Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 2/8] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout Vijendar Mukunda
@ 2026-09-10 19:00 ` Vijendar Mukunda
2026-09-13 20:05 ` Pierre-Louis Bossart
2026-09-10 19:00 ` [PATCH 4/8] soundwire: amd: fix ctx leak when sdw_amd_startup() fails Vijendar Mukunda
` (5 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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.
Also 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 | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index a57b59609bfe..bbe1e73ed255 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1172,9 +1172,11 @@ 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);
- cancel_work_sync(&amd_manager->amd_sdw_work);
+ 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);
sdw_bus_master_delete(&amd_manager->bus);
ret = amd_disable_sdw_manager(amd_manager);
if (ret)
--
2.48.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 4/8] soundwire: amd: fix ctx leak when sdw_amd_startup() fails
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (2 preceding siblings ...)
2026-09-10 19:00 ` [PATCH 3/8] soundwire: amd: fix work drain ordering and pm_runtime guard in remove path Vijendar Mukunda
@ 2026-09-10 19:00 ` Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 5/8] soundwire: amd: propagate amd_init_sdw_manager() error on resume Vijendar Mukunda
` (4 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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.48.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 5/8] soundwire: amd: propagate amd_init_sdw_manager() error on resume
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (3 preceding siblings ...)
2026-09-10 19:00 ` [PATCH 4/8] soundwire: amd: fix ctx leak when sdw_amd_startup() fails Vijendar Mukunda
@ 2026-09-10 19:00 ` Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 6/8] soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend Vijendar Mukunda
` (3 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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 bbe1e73ed255..2d2f628230ee 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1456,7 +1456,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.48.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 6/8] soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (4 preceding siblings ...)
2026-09-10 19:00 ` [PATCH 5/8] soundwire: amd: propagate amd_init_sdw_manager() error on resume Vijendar Mukunda
@ 2026-09-10 19:00 ` Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 7/8] soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths Vijendar Mukunda
` (2 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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 2d2f628230ee..6af224ca54a3 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;
@@ -1336,7 +1330,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;
}
@@ -1380,7 +1375,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.48.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 7/8] soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (5 preceding siblings ...)
2026-09-10 19:00 ` [PATCH 6/8] soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend Vijendar Mukunda
@ 2026-09-10 19:00 ` Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 8/8] soundwire: amd: fix interrupt gate and work drain ordering in PM ops Vijendar Mukunda
2026-09-11 17:52 ` [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Mario Limonciello
8 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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 | 97 ++++++++++++++++++++++-----------
1 file changed, 64 insertions(+), 33 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 6af224ca54a3..0cb5f4694e9a 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1292,6 +1292,22 @@ static int __maybe_unused amd_pm_prepare(struct device *dev)
return 0;
}
+static int 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);
@@ -1307,22 +1323,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
@@ -1335,10 +1347,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;
}
@@ -1357,21 +1378,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;
@@ -1380,7 +1397,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;
@@ -1392,6 +1414,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;
}
@@ -1413,11 +1439,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));
/*
@@ -1431,11 +1455,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;
@@ -1461,10 +1483,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.48.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 8/8] soundwire: amd: fix interrupt gate and work drain ordering in PM ops
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (6 preceding siblings ...)
2026-09-10 19:00 ` [PATCH 7/8] soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths Vijendar Mukunda
@ 2026-09-10 19:00 ` Vijendar Mukunda
2026-09-11 17:52 ` [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Mario Limonciello
8 siblings, 0 replies; 14+ messages in thread
From: Vijendar Mukunda @ 2026-09-10 19:00 UTC (permalink / raw)
To: vkoul
Cc: 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_runtime() and amd_suspend()
did not close the ACP_EXTERNAL_INTR_CNTL delivery gate before draining
amd_sdw_irq_thread and amd_sdw_work. The ACP PCI parent driver interrupt
handler dispatches SoundWire interrupts to the AMD SoundWire stack by
scheduling amd_sdw_irq_thread. Because the gate was open, an ISR already
running when the drains completed could re-queue work after
cancel_work_sync() returned, racing amd_sdw_clock_stop().
Fix both suspend paths by calling amd_disable_sdw_interrupts() first to
close the ACP-level gate before work drains.
In amd_suspend() CLK_STOP_MODE:
- Add amd_disable_sdw_interrupts() and cancel_work_sync(irq_thread)
before the existing cancel_work_sync(work).
- Call amd_enable_sdw_interrupts() after clock stop to reopen the gate.
ACP remains powered in CLK_STOP_MODE (only the SoundWire bus clock is
gated), so the interrupt path must be live to allow wake events to
reach the CPU during system sleep.
In amd_suspend() POWER_OFF_MODE:
- Add amd_disable_sdw_interrupts() and cancel_work_sync(irq_thread)
before the existing cancel_work_sync(work).
- Remove the redundant post-clock-stop amd_disable_sdw_interrupts() call;
the gate is already closed at the top of the branch.
In amd_suspend_runtime() CLK_STOP_MODE:
- Add amd_disable_sdw_interrupts() and cancel_work_sync(irq_thread)
before the existing cancel_work_sync(work).
- Call amd_enable_sdw_interrupts() to reopen the gate. ACP remains
powered in CLK_STOP_MODE (only the SoundWire bus clock is gated), so
the interrupt path must be live to allow wake events to reach the CPU.
In amd_suspend_runtime() POWER_OFF_MODE:
- Add amd_disable_sdw_interrupts() and cancel_work_sync(irq_thread)
before the existing cancel_work_sync(work).
- Remove the redundant post-clock-stop amd_disable_sdw_interrupts() call;
the gate is already closed at the top of the branch.
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 | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 0cb5f4694e9a..1a252a81269a 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1321,6 +1321,8 @@ 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);
@@ -1329,7 +1331,10 @@ static int __maybe_unused amd_suspend(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
+ amd_enable_sdw_interrupts(amd_manager);
} 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);
@@ -1342,7 +1347,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;
@@ -1377,6 +1381,9 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
return 0;
}
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, true);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
if (ret)
@@ -1384,7 +1391,11 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
+ amd_enable_sdw_interrupts(amd_manager);
} 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)
@@ -1392,7 +1403,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.48.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 1/8] soundwire: amd: fix SDW command timeout return value handling
2026-09-10 19:00 ` [PATCH 1/8] soundwire: amd: fix SDW command timeout return value handling Vijendar Mukunda
@ 2026-09-11 17:48 ` Mario Limonciello
2026-09-12 8:49 ` Mukunda,Vijendar
0 siblings, 1 reply; 14+ messages in thread
From: Mario Limonciello @ 2026-09-11 17:48 UTC (permalink / raw)
To: Vijendar Mukunda, vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Richard.Gong, linux-sound, linux-kernel
On 9/10/26 14:00, Vijendar Mukunda wrote:
> 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. In
> amd_sdw_read_and_process_ping_status(), the mutex unlock is also moved
> to after amd_sdw_process_ping_status() so the lock is held across the
> full status processing step.
>
> Fixes: d8f48fbdfd9a ("soundwire: amd: Add support for AMD Manager driver")
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
> drivers/soundwire/amd_manager.c | 56 +++++++++++++++++++--------------
> 1 file changed, 33 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
> index a3316efdf8ac..fa3f4e797edd 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,9 +447,14 @@ static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_man
> u64 response;
>
> mutex_lock(&amd_manager->bus.msg_lock);
guard(mutex) might work better here so you don't need to cover every
exit path with a mutex_unlock() manually.
> - response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0);
> - mutex_unlock(&amd_manager->bus.msg_lock);
> + 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;
> + }
> amd_sdw_process_ping_status(response, amd_manager);
> + mutex_unlock(&amd_manager->bus.msg_lock);
> }
>
> static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
> @@ -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;
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
` (7 preceding siblings ...)
2026-09-10 19:00 ` [PATCH 8/8] soundwire: amd: fix interrupt gate and work drain ordering in PM ops Vijendar Mukunda
@ 2026-09-11 17:52 ` Mario Limonciello
8 siblings, 0 replies; 14+ messages in thread
From: Mario Limonciello @ 2026-09-11 17:52 UTC (permalink / raw)
To: Vijendar Mukunda, vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Richard.Gong, linux-sound, linux-kernel
On 9/10/26 14:00, Vijendar Mukunda wrote:
> This series collects eight bug fixes for the AMD SoundWire manager
> driver (ACP6.3/7.0/7.1/7.2). The patches address correctness issues
> in command response handling, slave status caching, work-queue teardown
> ordering, resource cleanup on probe failure, and power-management
> suspend/resume sequences.
>
> Patch 1 fixes a silent type widening in amd_sdw_send_cmd_get_resp()
> where -ETIMEDOUT was returned through a u64, producing a large positive
> value instead of an error.
>
> Patch 2 caches the last successful ping slave status so that a command
> timeout during sdw_show_ping_status() does not report a spurious
> "no peripherals attached" warning.
>
> Patch 3 fixes the work-drain sequence in amd_sdw_manager_remove(): the
> interrupt delivery gate must be closed before cancel_work_sync() to
> prevent an in-flight IRQ handler from re-queuing work after the cancel
> returns. A pm_runtime_disable() imbalance on partially-started managers
> is also corrected.
>
> Patch 4 adds the missing sdw_amd_exit() call and ctx pointer clear on
> the sdw_amd_startup() failure path in sdw_amd_probe().
>
> Patch 5 propagates the amd_init_sdw_manager() return value in
> amd_resume_runtime() instead of ignoring it.
>
> Patch 6 removes the amd_deinit_sdw_manager() helper and expands its
> two call sites in the POWER_OFF suspend path to direct calls, making
> the sequence explicit with no functional change.
>
> Patch 7 replaces open-ended acp_rev >= ACP70_PCI_REV_ID comparisons
> in the PM callbacks with explicit per-revision handling covering
> ACP63, ACP70, ACP71 and ACP72, with a dev_err + -EINVAL default. The
> repeated host-wake-by-revision block is factored into a helper,
> amd_sdw_host_wake_enable_by_rev().
>
> Patch 8 fixes the interrupt gate and work drain ordering in all four
> suspend mode/path combinations: the ACP_EXTERNAL_INTR_CNTL gate must
> be closed before cancel_work_sync() calls to prevent a racing ISR from
> re-queuing work after the drain completes.
>
> Vijendar Mukunda (8):
> soundwire: amd: fix SDW command timeout return value handling
> soundwire: amd: cache ping slave status to avoid spurious disconnect
> on timeout
> soundwire: amd: fix work drain ordering and pm_runtime guard in remove
> path
> soundwire: amd: fix ctx leak when sdw_amd_startup() fails
> soundwire: amd: propagate amd_init_sdw_manager() error on resume
> soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend
> soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths
> soundwire: amd: fix interrupt gate and work drain ordering in PM ops
>
> drivers/soundwire/amd_init.c | 9 +-
> drivers/soundwire/amd_manager.c | 204 ++++++++++++++++++++----------
> include/linux/soundwire/sdw_amd.h | 3 +
> 3 files changed, 148 insertions(+), 68 deletions(-)
>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/8] soundwire: amd: fix SDW command timeout return value handling
2026-09-11 17:48 ` Mario Limonciello
@ 2026-09-12 8:49 ` Mukunda,Vijendar
0 siblings, 0 replies; 14+ messages in thread
From: Mukunda,Vijendar @ 2026-09-12 8:49 UTC (permalink / raw)
To: Mario Limonciello, vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Richard.Gong, linux-sound, linux-kernel
On 9/11/26 23:18, Mario Limonciello wrote:
>
>
> On 9/10/26 14:00, Vijendar Mukunda wrote:
>> 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. In
>> amd_sdw_read_and_process_ping_status(), the mutex unlock is also moved
>> to after amd_sdw_process_ping_status() so the lock is held across the
>> full status processing step.
>>
>> Fixes: d8f48fbdfd9a ("soundwire: amd: Add support for AMD Manager
>> driver")
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>> drivers/soundwire/amd_manager.c | 56 +++++++++++++++++++--------------
>> 1 file changed, 33 insertions(+), 23 deletions(-)
>>
>> diff --git a/drivers/soundwire/amd_manager.c
>> b/drivers/soundwire/amd_manager.c
>> index a3316efdf8ac..fa3f4e797edd 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,9 +447,14 @@ static void
>> amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_man
>> u64 response;
>> mutex_lock(&amd_manager->bus.msg_lock);
>
> guard(mutex) might work better here so you don't need to cover every
> exit path with a mutex_unlock() manually.
Thanks for the suggestion. I agree that guard(mutex) would simplify
the locking and make the error paths cleaner.
For this patch, I wanted to keep the change focused on fixing the
-ETIMEDOUT handling issue and avoid mixing it with unrelated cleanup.
The driver currently uses explicit mutex_lock() / mutex_unlock() pairs
throughout amd_manager.c, so introducing guard(mutex) in a single
function would make the locking style inconsistent.
I'll plan to follow up with a separate cleanup patch that converts the
eligible mutex-protected paths to guard(mutex) consistently across the
driver. Keeping that work separate should make both the bug fix and the
cleanup easier to review and bisect.
>
>> - response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0);
>> - mutex_unlock(&amd_manager->bus.msg_lock);
>> + 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;
>> + }
>> amd_sdw_process_ping_status(response, amd_manager);
>> + mutex_unlock(&amd_manager->bus.msg_lock);
>> }
>> static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
>> @@ -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;
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/8] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout
2026-09-10 19:00 ` [PATCH 2/8] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout Vijendar Mukunda
@ 2026-09-13 19:59 ` Pierre-Louis Bossart
0 siblings, 0 replies; 14+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-13 19:59 UTC (permalink / raw)
To: Vijendar Mukunda, vkoul
Cc: yung-chuan.liao, Basavaraj.Hiregoudar, Sunil-kumar.Dommati,
venkataprasad.potturu, Syed.SabaKareem, Mario.Limonciello,
Richard.Gong, linux-sound, linux-kernel
On 9/10/26 21:00, Vijendar Mukunda wrote:
> + * @ping_slave_stat: cached slave status from the last successful ping response;
It'd be good to clarify the definition of a 'successful ping response',
since this concept doesn't exist in the SoundWire spec.
The only valid combined responses are Command_Aborted or Command_Ignored...
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/8] soundwire: amd: fix work drain ordering and pm_runtime guard in remove path
2026-09-10 19:00 ` [PATCH 3/8] soundwire: amd: fix work drain ordering and pm_runtime guard in remove path Vijendar Mukunda
@ 2026-09-13 20:05 ` Pierre-Louis Bossart
0 siblings, 0 replies; 14+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-13 20:05 UTC (permalink / raw)
To: Vijendar Mukunda, vkoul
Cc: yung-chuan.liao, Basavaraj.Hiregoudar, Sunil-kumar.Dommati,
venkataprasad.potturu, Syed.SabaKareem, Mario.Limonciello,
Richard.Gong, linux-sound, linux-kernel
On 9/10/26 21:00, Vijendar Mukunda wrote:
> 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.
>
> Also 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.
The alternative is to do a pm_runtime_enable() in the probe(), and later
a pm_runtime_set_active().
That way if the probe is successful, then the remove() will always deal
a balanced enable.
Maybe only put a single 'fix' per patch?
> 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 | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
> index a57b59609bfe..bbe1e73ed255 100644
> --- a/drivers/soundwire/amd_manager.c
> +++ b/drivers/soundwire/amd_manager.c
> @@ -1172,9 +1172,11 @@ 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);
> - cancel_work_sync(&amd_manager->amd_sdw_work);
> + 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);
> sdw_bus_master_delete(&amd_manager->bus);
> ret = amd_disable_sdw_manager(amd_manager);
> if (ret)
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-09-13 20:07 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10 19:00 [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 1/8] soundwire: amd: fix SDW command timeout return value handling Vijendar Mukunda
2026-09-11 17:48 ` Mario Limonciello
2026-09-12 8:49 ` Mukunda,Vijendar
2026-09-10 19:00 ` [PATCH 2/8] soundwire: amd: cache ping slave status to avoid spurious disconnect on timeout Vijendar Mukunda
2026-09-13 19:59 ` Pierre-Louis Bossart
2026-09-10 19:00 ` [PATCH 3/8] soundwire: amd: fix work drain ordering and pm_runtime guard in remove path Vijendar Mukunda
2026-09-13 20:05 ` Pierre-Louis Bossart
2026-09-10 19:00 ` [PATCH 4/8] soundwire: amd: fix ctx leak when sdw_amd_startup() fails Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 5/8] soundwire: amd: propagate amd_init_sdw_manager() error on resume Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 6/8] soundwire: amd: drop amd_deinit_sdw_manager() in POWER_OFF suspend Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 7/8] soundwire: amd: replace >= ACP70 with explicit switch/case in PM paths Vijendar Mukunda
2026-09-10 19:00 ` [PATCH 8/8] soundwire: amd: fix interrupt gate and work drain ordering in PM ops Vijendar Mukunda
2026-09-11 17:52 ` [PATCH 0/8] soundwire: amd: SoundWire manager driver bug fixes Mario Limonciello
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox