Skip to contentSkip to conversation

usb: dwc3: Keep the host bus across suspend-to-idle

PATCH In review Arun Iyengar 3 participants 6 messages Nov 10 – Nov 11

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover usb: dwc3: Keep the host bus across suspend-to-idle

In host mode, dwc3_suspend_common() tears the core down for every system suspend unless the controller is a wakeup source (dwc3_core_exit(): PHYs off, core reset). That has two consequences for xhci-plat on top of it:

1. xhci_resume() is not told that the controller was reset. It writes the saved operational registers back and asks for a state restore (USBCMD.CRS), which fails on a freshly reset core, and only then falls back to a reinit:

     xhci-hcd xhci-hcd.1.auto: xHC error in resume, USBSTS 0x401, Reinit

xhci-plat already has xhci_plat_priv.power_lost for this (cdns3 sets it). Patch 1 sets it from dwc3 whenever the core was re-initialised and clears it when the core was kept for wakeup.

2. Every device behind the root ports was put into U3 by the bus suspend and expects to keep its power session; resetting the core ends all of them. On resume the USB core has to reset-resume every device, which only works for devices that come back within the 2 s usb_port_resume() waits. A SanDisk Extreme SSD and a card reader behind a hub do not (2.4-3 s, measured): they are disconnected and enumerated again after each resume, which kills a mounted filesystem. Patch 2 keeps the core, its PHYs and the suspended bus across suspend-to-idle, as the wakeup path already does and as PCI xHCI controllers do, and marks the device as being on the awake path so a power domain with GENPD_FLAG_ACTIVE_WAKEUP stays on. Suspend-to-RAM, hibernation and needs_full_reinit controllers are unchanged.

Patch 2 depends on patch 1 (it clears power_lost on the kept-core path). Both are changes to the dwc3 core; no glue driver is involved (the RK3588 nodes bind to the "snps,dwc3" core driver directly).

Tested on an Orange Pi 5 (RK3588S), suspend-to-idle through rtcwake:
- Patch 1: 55 cycles on a kernel with patch 1 (and an ohci-platform change sent separately); the "xHC error in resume, USBSTS 0x401" message is gone, the controller re-initialises without the failed restore, no other change in behaviour.
- Patch 2: 47 cycles on a kernel with both patches: every USB device kept its device number, all 7 SuperSpeed links were back after each resume, no "root hub lost power or was reset" and no re-enumeration in dmesg, USB drives readable afterwards. Without patch 2 the SSD and the card reader were disconnected and re-enumerated after every resume and their mounted filesystems went offline.
- Not tested: suspend-to-RAM and hibernation (unchanged by this series; the test platform has no working deep suspend), other dwc3 platforms.

This series was developed with Claude (Anthropic) as a coding assistant:
it wrote the code and the commit messages, and I reviewed and tested every change as described above.

---
Arun Iyengar (2):
usb: dwc3: Tell xhci-plat when the core lost its state on resume
usb: dwc3: Keep the host bus suspended, not torn down, across suspend-to-idle

 drivers/usb/dwc3/core.c | 43 +++++++++++++++++++++++++++++++++++++++----
 drivers/usb/dwc3/core.h |  5 +++++
 drivers/usb/dwc3/host.c | 29 ++++++++++++++++++++++++++---
 3 files changed, 70 insertions(+), 7 deletions(-)

---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20261110-dwc3-s2idle-6e332174c57b

Best regards,

Signature · 1 line
Arun Iyengar <[email protected]>
#1 lore raw

PATCH 1/2 usb: dwc3: Tell xhci-plat when the core lost its state on resume

In host mode without wakeup, dwc3_suspend_common() tears the core down with dwc3_core_exit() and dwc3_resume_common() initialises it again, so the xHCI registers are back at their reset values when xhci-plat resumes. xhci_resume() is not told about this: it writes the saved operational registers back and asks the controller to restore its internal state (USBCMD.CRS), which cannot work on a freshly reset core. It fails with USBSTS.SRE, and xhci_resume() only then falls back to a full reset:

xhci-hcd xhci-hcd.1.auto: xHC error in resume, USBSTS 0x401, Reinit

xhci-plat already has a way for the glue to report this:
xhci_plat_priv.power_lost, which cdns3 sets the same way. Set it from dwc3_resume_common() whenever the core was re-initialised (and clear it when it was kept running for wakeup), so xhci_resume() goes straight to the reset and re-init path.

---
 drivers/usb/dwc3/core.c |  3 +++
 drivers/usb/dwc3/core.h |  3 +++
 drivers/usb/dwc3/host.c | 29 ++++++++++++++++++++++++++---
 3 files changed, 32 insertions(+), 3 deletions(-)
drivers/usb/dwc3/core.c +3 -0
index fd5c2cd36c59..2b31f146e43f 100644
@@ -2639,8 +2639,10 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
 			if (ret)
 				return ret;
 			dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, true);
+			dwc3_host_resume(dwc, true);
 			break;
 		}
+		dwc3_host_resume(dwc, false);
 		/* Restore GUSB2PHYCFG bits that were modified in suspend */
 		for (i = 0; i < dwc->num_usb2_ports; i++) {
 			reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
@@ -2672,6 +2674,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
 		dwc3_otg_init(dwc);
 		if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
 			dwc3_otg_host_init(dwc);
+			dwc3_host_resume(dwc, true);
 		} else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
 			dwc3_gadget_resume(dwc);
 		}
drivers/usb/dwc3/core.h +3 -0
index f3dabb37f806..862818e081af 100644
@@ -1677,11 +1677,14 @@ static inline void dwc3_pre_run_stop(struct dwc3 *dwc, bool is_on)
 #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
 int dwc3_host_init(struct dwc3 *dwc);
 void dwc3_host_exit(struct dwc3 *dwc);
+void dwc3_host_resume(struct dwc3 *dwc, bool power_lost);
 #else
 static inline int dwc3_host_init(struct dwc3 *dwc)
 { return 0; }
 static inline void dwc3_host_exit(struct dwc3 *dwc)
 { }
+static inline void dwc3_host_resume(struct dwc3 *dwc, bool power_lost)
+{ }
 #endif
 
 #if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
drivers/usb/dwc3/host.c +26 -3
index c5674161b2b0..fb09b4f58414 100644
@@ -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"
 
@@ -223,6 +221,31 @@ int dwc3_host_init(struct dwc3 *dwc)
 }
 EXPORT_SYMBOL_GPL(dwc3_host_init);
 
+/**
+ * dwc3_host_resume - tell xhci-plat whether the controller lost its state
+ * @dwc: pointer to our controller context structure
+ * @power_lost: true if the core was re-initialised during system resume
+ *
+ * When the core is torn down in system suspend (dwc3_core_exit()) and
+ * initialised again on resume, the xHCI registers are back at their reset
+ * values. xhci_resume() must then reset and re-initialise the host instead
+ * of trying to restore its saved register state with CRS, which fails
+ * (USBSTS.SRE) on a freshly reset controller.
+ */
+void dwc3_host_resume(struct dwc3 *dwc, bool power_lost)
+{
+	struct usb_hcd *hcd;
+
+	if (!dwc->xhci)
+		return;
+
+	hcd = platform_get_drvdata(dwc->xhci);
+	if (!hcd)
+		return;
+
+	hcd_to_xhci_priv(hcd)->power_lost = power_lost;
+}
+
 void dwc3_host_exit(struct dwc3 *dwc)
 {
 	if (dwc->sys_wakeup)
Signature · 1 line
2.55.0
#2 lore raw

Re: [PATCH 1/2] usb: dwc3: Tell xhci-plat when the core lost its state on resume

Sashiko has reviewed this patch and found no issues.
Quoted 12 lines

From: Arun Iyengar <[email protected]>

In host mode without wakeup, dwc3_suspend_common() tears the core down with dwc3_core_exit() and dwc3_resume_common() initialises it again, so the xHCI registers are back at their reset values when xhci-plat resumes. xhci_resume() is not told about this: it writes the saved operational registers back and asks the controller to restore its internal state (USBCMD.CRS), which cannot work on a freshly reset core. It fails with USBSTS.SRE, and xhci_resume() only then falls back to a full reset:

xhci-hcd xhci-hcd.1.auto: xHC error in resume, USBSTS 0x401, Reinit

Quoted 7 lines

xhci-plat already has a way for the glue to report this:
xhci_plat_priv.power_lost, which cdns3 sets the same way. Set it from dwc3_resume_common() whenever the core was re-initialised (and clear it when it was kept running for wakeup), so xhci_resume() goes straight to the reset and re-init path.

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
#3 lore raw

PATCH 2/2 usb: dwc3: Keep the host bus suspended, not torn down, across suspend-to-idle

In reply to Arun Iyengar

In host mode, dwc3_suspend_common() tears the core down for system suspend (dwc3_core_exit(): PHYs off, core reset) unless the controller is a wakeup source. By then xhci-plat has already suspended the bus:
the root ports and every device behind them are in U3/suspend and expect to keep their power session, as the USB specification requires of a suspended bus (Documentation/driver-api/usb/persist.rst). Resetting the core ends all of them. On resume xhci-plat finds a reset controller ("root hub lost power or was reset") and the USB core has to reset-resume every device, which only works for devices that come back within the 2 s that usb_port_resume() waits for a connection.

Devices that are cut off in U3 do not necessarily come back that fast. On an Orange Pi 5 (RK3588S) the root port of the Type-A USB3 host is back in Polling 110 ms after resume, but a SanDisk Extreme drive that was suspended on it does not answer: the link goes to SS.Inactive after ~1.1 s, the drive drops its terminations at ~2.4 s and only connects again after ~3 s. A card reader behind a USB3 hub on the other controller loses VBUS when the hub is reset as part of its reset-resume and needs ~2.35 s. Both are disconnected and enumerated again on every resume, which kills a filesystem mounted on them. With the bus kept suspended they resume from U3 in milliseconds.

Suspend-to-idle does not power anything down behind the drivers' back, so there is no reason to end the power sessions there. Keep the core, its PHYs and the suspended ports, as is already done for wakeup, and mark the device as being on the awake path so that a power domain with GENPD_FLAG_ACTIVE_WAKEUP stays on as well. This matches what PCI xHCI controllers do in any system sleep state that keeps auxiliary power. Suspend-to-RAM, hibernation and controllers that need a full reinit (needs_full_reinit) still tear the core down.

---
 drivers/usb/dwc3/core.c | 40 ++++++++++++++++++++++++++++++++++++----
 drivers/usb/dwc3/core.h |  2 ++
 2 files changed, 38 insertions(+), 4 deletions(-)
drivers/usb/dwc3/core.c +36 -4
index 2b31f146e43f..8b335ba7cde4 100644
@@ -16,6 +16,7 @@
 #include <linux/spinlock.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/suspend.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/io.h>
@@ -2539,6 +2540,29 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
 	return ret;
 }
 
+/*
+ * In host mode, system suspend suspends the bus: xhci-plat puts the root ports
+ * and with them every attached device into U3/suspend, and the USB core
+ * expects them to keep that power session (see
+ * Documentation/driver-api/usb/persist.rst). Tearing the core down here ends
+ * all power sessions: the devices then have to go through a reset-resume, and
+ * a device that takes longer than the USB core allows to come back (it has to
+ * recover its link from scratch, a hub behind the port is reset and switches
+ * its own ports off) is disconnected, e.g. with a mounted filesystem.
+ *
+ * Only do that when the core will lose its state anyway or the system goes
+ * into a state that powers it down: keep it for wakeup, and for
+ * suspend-to-idle, where nothing is powered off behind the drivers' back.
+ */
+static bool dwc3_host_core_exit_needed(struct dwc3 *dwc)
+{
+	if (dwc->needs_full_reinit)
+		return true;
+	if (device_may_wakeup(dwc->dev))
+		return false;
+	return pm_suspend_target_state != PM_SUSPEND_TO_IDLE;
+}
+
 static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
 {
 	u32 reg;
@@ -2569,12 +2593,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
 		dwc3_core_exit(dwc);
 		break;
 	case DWC3_GCTL_PRTCAP_HOST:
-		if (!PMSG_IS_AUTO(msg) &&
-		    (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) {
+		dwc->host_core_exited = false;
+		if (!PMSG_IS_AUTO(msg) && dwc3_host_core_exit_needed(dwc)) {
 			dwc3_core_exit(dwc);
+			dwc->host_core_exited = true;
 			break;
 		}
 
+		/*
+		 * The core and its PHYs stay powered to keep the suspended bus:
+		 * keep the power domain on as well.
+		 */
+		if (!PMSG_IS_AUTO(msg))
+			device_set_awake_path(dwc->dev);
+
 		/* Let controller to suspend HSPHY before PHY driver suspends */
 		if (dwc->dis_u2_susphy_quirk ||
 		    dwc->dis_enblslpm_quirk) {
@@ -2633,8 +2665,8 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
 		dwc3_gadget_resume(dwc);
 		break;
 	case DWC3_GCTL_PRTCAP_HOST:
-		if (!PMSG_IS_AUTO(msg) &&
-		    (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) {
+		if (!PMSG_IS_AUTO(msg) && dwc->host_core_exited) {
+			dwc->host_core_exited = false;
 			ret = dwc3_core_init_for_resume(dwc);
 			if (ret)
 				return ret;
drivers/usb/dwc3/core.h +2 -0
index 862818e081af..c5de3c7ee7ec 100644
@@ -1133,6 +1133,7 @@ struct dwc3_glue_ops {
  * @usb3_lpm_capable: set if hadrware supports Link Power Management
  * @usb2_lpm_disable: set to disable usb2 lpm for host
  * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget
+ * @host_core_exited: the host-mode core was torn down in system suspend
  * @needs_full_reinit: set to indicate the core may lose power and need full
  *			initialization during system pm
  * @disable_scramble_quirk: set if we enable the disable scramble quirk
@@ -1403,6 +1404,7 @@ struct dwc3 {
 	unsigned		usb2_lpm_disable:1;
 	unsigned		usb2_gadget_lpm_disable:1;
 	unsigned		needs_full_reinit:1;
+	unsigned		host_core_exited:1;
 
 	unsigned		disable_scramble_quirk:1;
 	unsigned		u2exit_lfps_quirk:1;
Signature · 1 line
2.55.0
#4 lore raw

Re: [PATCH 2/2] usb: dwc3: Keep the host bus suspended, not torn down, across suspend-to-idle

Thank you for your contribution! Sashiko AI review found 3 potential issues to consider: - [High] usb: dwc3: dwc3_google_suspend() resets core in s2idle when wakeup is off - [High] usb: dwc3…

Thank you for your contribution! Sashiko AI review found 3 potential issues to consider:
- [High] usb: dwc3: dwc3_google_suspend() resets core in s2idle when wakeup is off
- [High] usb: dwc3: dwc3_host_core_exit_needed() breaks parent glue resets in s2idle
- [High] usb: dwc3: device_set_awake_path() fails to keep non-wakeup genpd on in s2idle
--

commit 2b3424195227cb5706b93edc71af29a9569a4ddf
Author: Arun Iyengar <[email protected]>

usb: dwc3: Keep the host bus suspended, not torn down, across suspend-to-idle

This commit skips tearing down the DWC3 core in host mode during suspend-to-idle when full reinitialization is not required, and marks the device as being on the awake path to keep its power domain active.

Quoted 4 lines
drivers/usb/dwc3/core.c +0 -0
index 2b31f146e43fc..8b335ba7cde4d 100644

[ ... ]

Quoted 16 lines
drivers/usb/dwc3/core.c +12 -0
@@ -2539,6 +2540,29 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
 	return ret;
 }
 
+/*
+ * In host mode, system suspend suspends the bus: xhci-plat puts the root ports
+ * and with them every attached device into U3/suspend, and the USB core
+ * expects them to keep that power session (see
+ * Documentation/driver-api/usb/persist.rst). Tearing the core down here ends
+ * all power sessions: the devices then have to go through a reset-resume, and
+ * a device that takes longer than the USB core allows to come back (it has to
+ * recover its link from scratch, a hub behind the port is reset and switches
+ * its own ports off) is disconnected, e.g. with a mounted filesystem.
+ *
+ * Only do that when the core will lose its state anyway or the system goes
+ * into a state that powers it down: keep it for wakeup, and for

drivers/usb/dwc3/core.c static int dwc3_core_init_for_resume(struct dwc3 *dwc) · line 2564

Quoted 10 lines
drivers/usb/dwc3/core.c +10 -0
+ * suspend-to-idle, where nothing is powered off behind the drivers' back.
+ */
+static bool dwc3_host_core_exit_needed(struct dwc3 *dwc)
+{
+	if (dwc->needs_full_reinit)
+		return true;
+	if (device_may_wakeup(dwc->dev))
+		return false;
+	return pm_suspend_target_state != PM_SUSPEND_TO_IDLE;
+}

[Severity: High]
Does skipping dwc3_core_exit() in PM_SUSPEND_TO_IDLE break parent glue drivers that reset or power down the hardware in their own .suspend() callbacks?

Parent glue drivers that populate a child snps,dwc3 device via of_platform_populate() bind it to dwc3_probe(), where needs_full_reinit is false. During PM_SUSPEND_TO_IDLE, device .suspend() callbacks still run:
for example, dwc3_of_simple_suspend() asserts simple->resets when simple->need_reset is set (such as on rockchip,rk3399-dwc3), dwc3_meson_g12a_suspend() powers off and exits the PHYs before pulsing reset on resume, and dwc3_exynos_suspend() disables the vdd33/vdd10 regulators.

Because dwc3_suspend_common() now leaves host_core_exited as false on those platforms, dwc3_resume_common() skips dwc3_core_init_for_resume() and dwc3_set_prtcap() and passes power_lost = false to xhci-plat after the parent glue driver has reset or power-cycled the controller, leaving the host controller uninitialized.

Quoted 6 lines
drivers/usb/dwc3/core.c +1 -0
+
 static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
 {
 	u32 reg;
@@ -2569,12 +2593,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
 		dwc3_core_exit(dwc);

drivers/usb/dwc3/core.c static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) · line 2601

Quoted 10 lines
drivers/usb/dwc3/core.c +3 -2
 		break;
 	case DWC3_GCTL_PRTCAP_HOST:
-		if (!PMSG_IS_AUTO(msg) &&
-		    (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) {
+		dwc->host_core_exited = false;
+		if (!PMSG_IS_AUTO(msg) && dwc3_host_core_exit_needed(dwc)) {
 			dwc3_core_exit(dwc);
+			dwc->host_core_exited = true;
 			break;
 		}

[Severity: High]
Does dwc3_google_suspend() also need to check !google->dwc.host_core_exited instead of device_may_wakeup()?

drivers/usb/dwc3/dwc3-google.c:dwc3_google_suspend() {
	if (google->dwc.current_dr_role == DWC3_GCTL_PRTCAP_HOST) {
		/*
		 * Follow dwc3_suspend_common() guidelines for deciding between
		 * a full teardown and hibernation.
		 */
		if (PMSG_IS_AUTO(msg) || device_may_wakeup(google->dev)) {
			...
			return 0;
		}
	}

	reset_control_bulk_assert(google->num_rsts, google->rsts);
	clk_bulk_disable_unprepare(google->num_clks, google->clks);
}

When wakeup is disabled during PM_SUSPEND_TO_IDLE, dwc3_suspend_common() now keeps the core active and sets host_core_exited to false, but dwc3_google_suspend() still falls through and asserts google->rsts. On resume, dwc3_google_resume() deasserts the resets and dwc3_resume_common() sees host_core_exited == false, skipping dwc3_core_init_for_resume() and dwc3_set_prtcap() on a reset controller.

drivers/usb/dwc3/core.c static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) · line 2608

Quoted 7 lines
drivers/usb/dwc3/core.c +6 -0
+		/*
+		 * The core and its PHYs stay powered to keep the suspended bus:
+		 * keep the power domain on as well.
+		 */
+		if (!PMSG_IS_AUTO(msg))
+			device_set_awake_path(dwc->dev);

[Severity: High]
What happens on SoCs where the DWC3 power domain does not set GENPD_FLAG_ACTIVE_WAKEUP?

drivers/pmdomain/core.c:genpd_finish_suspend() {
	if (device_awake_path(dev) && genpd_is_active_wakeup(genpd) &&
	    !device_out_band_wakeup(dev))
		return 0;
	...
	genpd_sync_power_off(genpd, true, 0);
}

genpd_finish_suspend() still runs during PM_SUSPEND_TO_IDLE and only honors device_awake_path() when the domain sets GENPD_FLAG_ACTIVE_WAKEUP. On platforms whose DWC3 power domain does not set that flag (for example, RK3568_PD_PIPE on RK3566/RK3568, where usb_host0_xhci and usb_host1_xhci bind to dwc3_probe() with needs_full_reinit == false), genpd still stops clocks and powers off the domain during suspend-to-idle, and dwc3_resume_common() then skips dwc3_core_init_for_resume() because host_core_exited is false.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
#5 lore raw

Re: [PATCH 2/2] usb: dwc3: Keep the host bus suspended, not torn down, across suspend-to-idle

In reply to Arun Iyengar

From: Vladyslav Tymoshchuk <[email protected]>

Quoted 5 lines · Arun Iyengar

Suspend-to-idle does not power anything down behind the drivers' back, so there is no reason to end the power sessions there. Keep the core, its PHYs and the suspended ports, as is already done for wakeup, and mark the device as being on the awake path so that a power domain with GENPD_FLAG_ACTIVE_WAKEUP stays on as well.

A data point from a Qualcomm board where the first sentence does not hold. I have not run this series; what follows is the existing "kept for wakeup" path, which this patch extends to every suspend-to-idle.

On SM8750 (OnePlus Pad 3, v7.3-rc5 plus board patches) the controller node is a wakeup source, so dwc3 keeps the core in host mode. The USB GDSCs are PWRSTS_OFF_ON in gcc-sm8750 and the qcom GDSC driver does not set GENPD_FLAG_ACTIVE_WAKEUP, so genpd powers them off in s2idle all the same. With a USB stick on the port the xHCI then comes back like this, in 3 of 3 cycles of 150 s:

arm-smmu 15000000.iommu: Unhandled context fault: fsr=0x402, iova=0x669fd3200, fsynr=0x370011, cbfrsynra=0x40, cb=5 xhci-hcd xhci-hcd.3.auto: WARNING: Host System Error
xhci-hcd xhci-hcd.3.auto: xHCI host not responding to stop endpoint command
xhci-hcd xhci-hcd.3.auto: xHCI host controller not responding, assume dead
xhci-hcd xhci-hcd.3.auto: HC died; cleaning up

With the GDSCs in retention [1], as SM8650 and X1E80100 have them, the same 150 s cycle passed 5 of 5 times, and two shorter cycles of 25 s passed as well.

This is the third point of the Sashiko review seen on hardware:
device_set_awake_path() does nothing for a domain without the flag, and the core is treated as kept although its power went away. I would expect 2/2 to bring this failure to SM8750 controllers with wakeup disabled too, unless [1] is applied.

[1] https://lore.kernel.org/all/[email protected]/