usb: dwc3: tell xHCI that it lost its state when the core is powered off
Subsystems (2)
PATCH RESEND 1/1 usb: dwc3: tell xHCI that it lost its state when the core is powered off
In host mode, dwc3_suspend_common() powers the core off for system suspend (dwc3_core_exit()) when the controller may not wake the system or needs a full re-init. The xHCI registers and the state saved with the Controller Save State command are gone then, but nothing tells
xhci-plat: xhci_resume() takes the "state intact" path, writes the saved registers back and asks the controller to restore its state (CRS).
xhci-plat has a flag for exactly this, power_lost (commit 668cc6bc1178 ("usb: host: xhci-plat: allow upper layers to signal power loss"), used by cdns3), but dwc3 never sets it.
On a Lenovo TB323FU (SM8850, DWC3 + eUSB2 PHY) with a USB 2.0 hub, a USB 3 flash drive and a Billboard device attached, and USB wakeup disabled, s2idle then broke the host controller on resume: either the controller wrote to an address that was not mapped
arm-smmu 15000000.iommu: Unhandled context fault: fsr=0x402,
iova=0xd85e7c300, fsynr=0x3d0011, cbfrsynra=0x40, cb=13
xhci-hcd xhci-hcd.2.auto: WARNING: Host System Error usb 1-1: hub failed to enable device, error -108
or its command ring would not stop
xhci-hcd xhci-hcd.2.auto: Abort failed to stop command ring: -110 xhci-hcd xhci-hcd.2.auto: xHCI host controller not responding, assume dead xhci-hcd xhci-hcd.2.auto: HC died; cleaning up
usb 1-1: PM: **** DPM device timeout ****
Kernel panic - not syncing: usb 1-1: unrecoverable failure
With wakeup enabled (the core stays powered) the same setup survived every cycle.
Remember that the core was powered off and hand it to xhci-plat as power_lost from a resume_quirk, so that xhci_resume() resets the controller and the devices are re-enumerated. Unlike a static XHCI_RESET_ON_RESUME this only applies to the resumes that follow a power-off; resumes with the core kept powered (wakeup enabled) and runtime resume keep the restore path.
Same kernel and setup, 30 s RTC s2idle, wakeup disabled: without this change 1/1 cycles lost the controller, with it 3/3 cycles kept all devices. Only tested on this device.
---
Resend: no replies since 2026-09-29; unchanged, still applies to next-20261105.
Arun Iyengar's RFC for the Rockchip glue builds on this patch [1]. On an Orange Pi 5 (RK3588S), with the core torn down for suspend-to-idle, he saw no "xHC error in resume" in 10 of 10 cycles.
[1] https://lore.kernel.org/all/[email protected]/
host.c now includes ../host/xhci.h (for hcd_to_xhci_priv()) in place of the three xHCI headers it used, as cdns3's host.c does.
drivers/usb/dwc3/core.c | 2 ++ drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/host.c | 20 +++++++++++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-)
drivers/usb/dwc3/core.c +2 -0
@@ -2572,8 +2572,10 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)if (!PMSG_IS_AUTO (msg) && (!device_may_wakeup (dwc->dev) || dwc->needs_full_reinit)) {dwc3_core_exit (dwc); + dwc->xhci_power_lost =1 ;break ; } + dwc->xhci_power_lost =0 ;/* Let controller to suspend HSPHY before PHY driver suspends */ if (dwc->dis_u2_susphy_quirk ||
drivers/usb/dwc3/core.h +3 -0
@@ -1172,6 +1172,8 @@ struct dwc3_glue_ops {* @suspended: set to track suspend event due to U3/L2. * @susphy_state: state of DWC3_GUSB2PHYCFG_SUSPHY + DWC3_GUSB3PIPECTL_SUSPHY * before PM suspend. +* @xhci_power_lost: the core was powered off for system suspend in host +* mode; xHCI must not try to restore its state on resume * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. * @max_cfg_eps: current max number of IN eps used across all USB configs.
@@ -1420,6 +1422,7 @@ struct dwc3 { unsigned wakeup_configured:1; unsigned suspended:1; unsigned susphy_state:1; + unsigned xhci_power_lost:1;u16 imod_interval;
drivers/usb/dwc3/host.c +17 -3
@@ -14,9 +14,7 @@#include <linux/usb/hcd.h> #include <linux/bitfield.h> -#include "../host/xhci-port.h" -#include "../host/xhci-ext-caps.h" -#include "../host/xhci-caps.h" +#include "../host/xhci.h" #include "../host/xhci-plat.h" #include "core.h"
@@ -78,7 +76,23 @@ static void dwc3_xhci_plat_start(struct usb_hcd *hcd)dwc3_enable_susphy (dwc, true); } +/* +* dwc3_suspend_common() powers the core off for system suspend in host mode +* when the controller may not wake the system. The xHCI registers and its +* saved internal state are gone then: tell xhci-plat, so that xhci_resume() +* resets the controller instead of restoring registers and asking it to +* restore a state it no longer has. +*/ +static int dwc3_xhci_resume_quirk (struct usb_hcd *hcd) +{ +struct dwc3 *dwc =dev_get_drvdata (hcd->self.controller->parent); + +hcd_to_xhci_priv (hcd)->power_lost = dwc->xhci_power_lost; +return 0 ; +} +static const struct xhci_plat_priv dwc3_xhci_plat_quirk = { + .resume_quirk = dwc3_xhci_resume_quirk, .plat_start = dwc3_xhci_plat_start, };
base-commit: 6375e61c01e93e35ee7acd336a689ac1fae4b509
Signature · 1 line
2.43.0
[email protected]
AI reviewThank you for your contribution! Sashiko AI review found 2 potential issues to consider: - [Medium] usb: dwc3: stale xhci_power_lost after resume in dwc3_xhci_resume_quirk() - [Medium] usb: dwc3…
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider:
- [Medium] usb: dwc3: stale xhci_power_lost after resume in dwc3_xhci_resume_quirk()
- [Medium] usb: dwc3: missing xhci_power_lost set for OTG host in dwc3_suspend_common()
It also noticed 1 related pre-existing issue this patch does not introduce:
- [High] usb: dwc3: missing xhci_power_lost for OTG mode in dwc3_suspend_common() [1]
--
commit 1536e0b07384ff8dbfd40131768e7772947697b4
Author: Joonhoe Kim <[email protected]>
usb: dwc3: tell xHCI that it lost its state when the core is powered off
This commit records when dwc3_suspend_common() powers off the DWC3 core during host-mode system suspend and passes that state to xhci-plat as power_lost via a resume_quirk callback.
drivers/usb/dwc3/core.c static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) · line 2578
Quoted 12 lines
drivers/usb/dwc3/core.c +2
-0@@ -2572,8 +2572,10 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)if (!PMSG_IS_AUTO (msg) && (!device_may_wakeup (dwc->dev) || dwc->needs_full_reinit)) {dwc3_core_exit (dwc); + dwc->xhci_power_lost =1 ;break ; } + dwc->xhci_power_lost =0 ;
[Severity: Medium]
Does the DWC3_GCTL_PRTCAP_OTG case in dwc3_suspend_common() also need to set dwc->xhci_power_lost?
When the controller operates in OTG mode with the host role active (dwc->current_otg_role == DWC3_OTG_ROLE_HOST), dwc3_otg_update() registers the xhci-hcd device while dwc->current_dr_role stays DWC3_GCTL_PRTCAP_OTG. On system suspend, the DWC3_GCTL_PRTCAP_OTG branch unconditionally calls dwc3_core_exit(dwc) without setting dwc->xhci_power_lost:
drivers/usb/dwc3/core.c:dwc3_suspend_common() {
...
case DWC3_GCTL_PRTCAP_OTG:
/* do nothing during runtime_suspend */
if (PMSG_IS_AUTO(msg))
break;
...
dwc3_otg_exit(dwc);
dwc3_core_exit(dwc);
break;
}On resume, dwc3_resume_common() reinitializes the core from scratch, but dwc3_xhci_resume_quirk() still sees xhci_power_lost == 0 and xhci_resume() attempts to restore state onto the reset controller.
[Severity: High]
This problem wasn't introduced by this patch, but in OTG host mode
[1] does dwc3_suspend_common() power off the core via dwc3_core_exit() without telling xhci-plat that power was lost? When a dual-role controller in DWC3_GCTL_PRTCAP_OTG mode is acting as host, system suspend tears down the core in the DWC3_GCTL_PRTCAP_OTG branch while leaving xhci_power_lost unset, so xhci_resume() tries to restore saved registers to a reset controller and triggers unhandled IOMMU context faults or command ring timeouts.
Quoted 4 lines
drivers/usb/dwc3/host.c +0
-0
[ ... ]
Quoted 8 lines
drivers/usb/dwc3/host.c +4
-0@@ -78,7 +76,23 @@ static void dwc3_xhci_plat_start(struct usb_hcd *hcd)dwc3_enable_susphy (dwc, true); } +/* +* dwc3_suspend_common() powers the core off for system suspend in host mode +* when the controller may not wake the system. The xHCI registers and its +* saved internal state are gone then: tell xhci-plat, so that xhci_resume()
drivers/usb/dwc3/host.c static void dwc3_xhci_plat_start(struct usb_hcd *hcd) · line 92
Quoted 10 lines
drivers/usb/dwc3/host.c +10
-0+* resets the controller instead of restoring registers and asking it to +* restore a state it no longer has. +*/ +static int dwc3_xhci_resume_quirk (struct usb_hcd *hcd) +{ +struct dwc3 *dwc =dev_get_drvdata (hcd->self.controller->parent); + +hcd_to_xhci_priv (hcd)->power_lost = dwc->xhci_power_lost; +return 0 ; +}
[Severity: Medium]
Can dwc->xhci_power_lost remain 1 after resume and cause an unnecessary xHCI reset on a later aborted suspend?
Neither dwc3_xhci_resume_quirk() nor dwc3_resume_common() clears dwc->xhci_power_lost after a power-off suspend/resume cycle. If a subsequent system suspend aborts after the child xhci-hcd device runs xhci_plat_suspend() but before the parent dwc3 device runs dwc3_suspend_common(), dpm_resume() calls dwc3_xhci_resume_quirk(), which reads the stale dwc->xhci_power_lost == 1 and forces xhci_resume() to reset the controller and re-enumerate all attached devices even though the core stayed powered.
[1] https://sashiko.dev/#/bug/linux-d6c3b577-381b-4dd2-a215-d44eec3c0bbf
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Quoted 3 lines · On Thu, 12 Nov 2026 12:00:08 +0000, [email protected] wrote:
[Severity: Medium]
Does theDWC3_GCTL_PRTCAP_OTGcase indwc3_suspend_common()also need to setdwc->xhci_power_lost?
[...]
Quoted 4 lines
[Severity: High]
This problem wasn't introduced by this patch, but in OTG host mode doesdwc3_suspend_common()power off the core viadwc3_core_exit()without telling xhci-plat that power was lost?
Yes, both are the same gap. The OTG case always powers the core off for system suspend, so v2 sets the flag there too when the host role is active. OTG mode is not tested; this board switches roles through usb-role-switch and never runs in DWC3_GCTL_PRTCAP_OTG.
Quoted 3 lines
[Severity: Medium]
Candwc->xhci_power_lostremain 1 after resume and cause an unnecessary xHCI reset on a later aborted suspend?
Yes. v2 clears the flag in the resume quirk once xhci-plat has read it.
v2: https://lore.kernel.org/all/[email protected]/
Thanks,
Joonhoe Kim