From b6106ce211a8f83be543d94abae2e3d8142ebd72 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 11 Aug 2026 17:13:56 +0300 Subject: [PATCH 1/4] Fixup: soundwire: bus: add CLOCK_STOP_MODE1 support back sdw_get_clk_stop_mode() converts slave->dev.driver with drv_to_sdw_driver() and dereferences the result unconditionally. A Peripheral can be attached and enumerated on the bus while it has no driver bound to it, for example after its codec driver module has been removed, and in that case slave->dev.driver is NULL. sdw_bus_prep_clk_stop() walks every Peripheral which has a device number and is in ATTACHED or ALERT state, so the Manager's runtime suspend reaches such an unbound Peripheral and the container_of() arithmetic turns the NULL pointer into a small negative offset: BUG: unable to handle page fault for address: fffffffffffffff8 RIP: 0010:sdw_bus_prep_clk_stop+0x93/0x1d0 [soundwire_bus] Call Trace: sdw_cdns_clock_stop+0xbe/0x1d0 [soundwire_cadence] intel_stop_bus+0xc1/0x100 [soundwire_intel] intel_suspend_runtime+0x7b/0x160 [soundwire_intel] __rpm_callback+0x57/0x210 rpm_suspend+0xfc/0x660 pm_runtime_work+0xa4/0xb0 Take sdw_dev_lock and check slave->probed before looking at the driver, the same way sdw_slave_clk_stop_callback() and the other clock stop helpers do, and fall back to the mode advertised by the Peripheral property when no driver is bound. Fixes: 922ebc47f10c ("soundwire: bus: add CLOCK_STOP_MODE1 support back") Signed-off-by: Peter Ujfalusi --- drivers/soundwire/bus.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 65cc514cbd632a..dafb4a3e450022 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -957,17 +957,26 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, static enum sdw_clk_stop_mode sdw_get_clk_stop_mode(struct sdw_slave *slave) { - struct device *dev = &slave->dev; - struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + enum sdw_clk_stop_mode mode; + + mode = slave->prop.clk_stop_mode1 ? SDW_CLK_STOP_MODE1 : SDW_CLK_STOP_MODE0; + + mutex_lock(&slave->sdw_dev_lock); /* * Query for clock stop mode if Slave implements * ops->get_clk_stop_mode, else read from property. */ - if (drv->ops && drv->ops->get_clk_stop_mode) - return drv->ops->get_clk_stop_mode(slave); + if (slave->probed) { + struct sdw_driver *drv = drv_to_sdw_driver(slave->dev.driver); + + if (drv->ops && drv->ops->get_clk_stop_mode) + mode = drv->ops->get_clk_stop_mode(slave); + } + + mutex_unlock(&slave->sdw_dev_lock); - return slave->prop.clk_stop_mode1 ? SDW_CLK_STOP_MODE1 : SDW_CLK_STOP_MODE0; + return mode; } static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, From f7e2df65917ec687a9bbf44aed7b69fbde07a46c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 11 Aug 2026 11:27:10 +0800 Subject: [PATCH 2/4] Revert "soundwire: Intel: stop sdw clock in system suspend" Will add a wake_enable paramter to bus_clock ops first. This reverts commit cce69890ef2172ef49c83ff91bd37192925ab4f4. --- drivers/soundwire/intel_auxdevice.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index b6801fe95176cf..a8407560bf4fda 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -670,8 +670,7 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } - /* No need to keep the SoundWire clock active in system suspend */ - ret = sdw_intel_stop_bus(sdw, true); + ret = sdw_intel_stop_bus(sdw, false); if (ret < 0) { dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret); return ret; From c9aee9c5ff09736f0b40ca829eff640867505dea Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 11 Aug 2026 11:28:36 +0800 Subject: [PATCH 3/4] soundwire: Intel: add wake_enable parameter to stop_bus ops Currently, we assume the bus is wakeable when the bus clock stops. But in some case like system suspend, we want to stop the bus but keep the bus unwakeable. No function change in this commit. Signed-off-by: Bard Liao --- drivers/soundwire/intel.h | 6 +++--- drivers/soundwire/intel_auxdevice.c | 6 +++--- drivers/soundwire/intel_bus_common.c | 8 ++++---- include/linux/soundwire/sdw_intel.h | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 86abc465260faf..7a2e7e73ad632a 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -188,10 +188,10 @@ static inline int sdw_intel_start_bus_after_clock_stop(struct sdw_intel *sdw) return -ENOTSUPP; } -static inline int sdw_intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) +static inline int sdw_intel_stop_bus(struct sdw_intel *sdw, bool clock_stop, bool wake_enable) { if (SDW_INTEL_CHECK_OPS(sdw, stop_bus)) - return SDW_INTEL_OPS(sdw, stop_bus)(sdw, clock_stop); + return SDW_INTEL_OPS(sdw, stop_bus)(sdw, clock_stop, wake_enable); return -ENOTSUPP; } @@ -261,7 +261,7 @@ int intel_start_bus(struct sdw_intel *sdw); int intel_start_bus_after_reset(struct sdw_intel *sdw); void intel_check_clock_stop(struct sdw_intel *sdw); int intel_start_bus_after_clock_stop(struct sdw_intel *sdw); -int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop); +int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop, bool wake_enable); /* common bank switch routines */ int intel_pre_bank_switch(struct sdw_intel *sdw); diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index a8407560bf4fda..77e09e0e99538b 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -670,7 +670,7 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } - ret = sdw_intel_stop_bus(sdw, false); + ret = sdw_intel_stop_bus(sdw, false, false); if (ret < 0) { dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret); return ret; @@ -696,14 +696,14 @@ static int __maybe_unused intel_suspend_runtime(struct device *dev) clock_stop_quirks = sdw->link_res->clock_stop_quirks; if (clock_stop_quirks & SDW_INTEL_CLK_STOP_TEARDOWN) { - ret = sdw_intel_stop_bus(sdw, false); + ret = sdw_intel_stop_bus(sdw, false, false); if (ret < 0) { dev_err(dev, "%s: cannot stop bus during teardown: %d\n", __func__, ret); return ret; } } else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET || !clock_stop_quirks) { - ret = sdw_intel_stop_bus(sdw, true); + ret = sdw_intel_stop_bus(sdw, true, true); if (ret < 0) { dev_err(dev, "%s: cannot stop bus during clock_stop: %d\n", __func__, ret); diff --git a/drivers/soundwire/intel_bus_common.c b/drivers/soundwire/intel_bus_common.c index ad1f8ebdbfc9b1..fa35eb2fc76328 100644 --- a/drivers/soundwire/intel_bus_common.c +++ b/drivers/soundwire/intel_bus_common.c @@ -202,11 +202,11 @@ int intel_start_bus_after_clock_stop(struct sdw_intel *sdw) return 0; } -int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) +int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop, bool wake_enable) { struct device *dev = sdw->cdns.dev; struct sdw_cdns *cdns = &sdw->cdns; - bool wake_enable = false; + bool en = false; int ret; cancel_delayed_work_sync(&cdns->attach_dwork); @@ -216,7 +216,7 @@ int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) if (ret < 0) dev_err(dev, "%s: cannot stop clock: %d\n", __func__, ret); else - wake_enable = true; + en = wake_enable; } ret = sdw_cdns_enable_interrupt(cdns, false); @@ -231,7 +231,7 @@ int intel_stop_bus(struct sdw_intel *sdw, bool clock_stop) return ret; } - sdw_intel_shim_wake(sdw, wake_enable); + sdw_intel_shim_wake(sdw, en); return 0; } diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 9c943500953712..9710f2dc04e298 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -424,7 +424,7 @@ struct sdw_intel_hw_ops { int (*start_bus)(struct sdw_intel *sdw); int (*start_bus_after_reset)(struct sdw_intel *sdw); int (*start_bus_after_clock_stop)(struct sdw_intel *sdw); - int (*stop_bus)(struct sdw_intel *sdw, bool clock_stop); + int (*stop_bus)(struct sdw_intel *sdw, bool clock_stop, bool wake_enable); int (*link_power_up)(struct sdw_intel *sdw); int (*link_power_down)(struct sdw_intel *sdw); From 93ee292911bd4c1e83c2b9c047752993748619c3 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 10 Feb 2026 17:12:54 +0800 Subject: [PATCH 4/4] soundwire: Intel: stop sdw clock in system suspend Perform clock stop with proper mode so that the Peripherals can enter the deserved power state. And keep the bus unwakeable because there is no need to wake up the bus in system suspend. Signed-off-by: Bard Liao --- drivers/soundwire/intel_auxdevice.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 77e09e0e99538b..dfff44a30c7ce8 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -670,7 +670,8 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } - ret = sdw_intel_stop_bus(sdw, false, false); + /* Perform clock stop with proper mode and keep the bus unwakeable in system suspend. */ + ret = sdw_intel_stop_bus(sdw, true, false); if (ret < 0) { dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret); return ret;