Linux USB
 help / color / mirror / Atom feed
* [PATCH RESEND] usb: dwc3: tell xHCI that it lost its state when the core is powered off
@ 2026-11-12 11:40 Joonhoe Kim
  2026-11-12 12:00 ` sashiko-bot
  0 siblings, 1 reply; 3+ messages in thread
From: Joonhoe Kim @ 2026-11-12 11:40 UTC (permalink / raw)
  To: Thinh.Nguyen, gregkh; +Cc: Arun Iyengar, mathias.nyman, linux-usb, linux-kernel

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.

Fixes: c4a5153e87fd ("usb: dwc3: core: Power-off core/PHYs on system_suspend in host mode")
Assisted-by: LLM
Signed-off-by: Joonhoe Kim <26rote@gmail.com>
---
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/20261112-dwc3-s2idle-optin-v1-0-842e3f8413d0@gmail.com/

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(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fd5c2cd36c59..b5490b9a8474 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -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 ||
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 608daeb7ef10..1aa898b343e6 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -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;
 
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index c5674161b2b0..c7143a8e3e32 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -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
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH RESEND] usb: dwc3: tell xHCI that it lost its state when the core is powered off
  2026-11-12 11:40 [PATCH RESEND] usb: dwc3: tell xHCI that it lost its state when the core is powered off Joonhoe Kim
@ 2026-11-12 12:00 ` sashiko-bot
  2026-11-12 14:15   ` Joonhoe Kim
  0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-11-12 12:00 UTC (permalink / raw)
  To: Joonhoe Kim; +Cc: linux-usb

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 <26rote@gmail.com>

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.

> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index fd5c2cd36c595..b5490b9a8474f 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -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.

> diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
> index c5674161b2b08..c7143a8e3e326 100644
> --- a/drivers/usb/dwc3/host.c
> +++ b/drivers/usb/dwc3/host.c

[ ... ]

> @@ -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;
> +}

[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

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261112114053.412-1-26rote@gmail.com?part=1

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH RESEND] usb: dwc3: tell xHCI that it lost its state when the core is powered off
  2026-11-12 12:00 ` sashiko-bot
@ 2026-11-12 14:15   ` Joonhoe Kim
  0 siblings, 0 replies; 3+ messages in thread
From: Joonhoe Kim @ 2026-11-12 14:15 UTC (permalink / raw)
  To: sashiko-reviews
  Cc: sashiko-bot, Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman,
	linux-usb

On Thu, 12 Nov 2026 12:00:08 +0000, sashiko-bot@kernel.org wrote:
> [Severity: Medium]
> Does the DWC3_GCTL_PRTCAP_OTG case in dwc3_suspend_common() also need to
> set dwc->xhci_power_lost?
[...]
> [Severity: High]
> This problem wasn't introduced by this patch, but in OTG host mode
> does dwc3_suspend_common() power off the core via
> dwc3_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.

> [Severity: Medium]
> Can dwc->xhci_power_lost remain 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/20261112141512.1840-1-26rote@gmail.com/

Thanks,
Joonhoe Kim

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-11-12 14:15 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-12 11:40 [PATCH RESEND] usb: dwc3: tell xHCI that it lost its state when the core is powered off Joonhoe Kim
2026-11-12 12:00 ` sashiko-bot
2026-11-12 14:15   ` Joonhoe Kim

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox