[PATCH] usb: musb: Drop the D+ pullup during system suspend
From: Nguyen Minh Tien
Date: Sun Sep 27 2026 - 12:42:01 EST
When musb_suspend() clears DEVCTL, the host sees a disconnect. But VBUS
is still there, so the controller can start a new session on its own
and, with SOFTCONN still set, pull D+ up again while its interrupts are
masked. The host then fails to enumerate the gadget ("unable to
enumerate USB device") and gives up, and nothing at resume makes it try
again. On a T113-S3 board the gadget link never survived an s2idle
cycle.
Clear SOFTCONN once the context is saved; musb_restore_context() puts
it back on resume. This is what the FIXME asked for, as USB can't wake
us in time with the interrupts masked. On da8xx, which keeps the
session over suspend, the gadget now disconnects too.
Fixes: 6fc6f4b87cb3 ("usb: musb: Disable interrupts on suspend, enable them on resume")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Nguyen Minh Tien <tien.nguyenminh@xxxxxxxxxxxxxxxxxx>
---
I found this on a T113-S3 board (sunxi, s2idle, Intel xHCI host): ssh
over the gadget never came back after a suspend. With the patch, all 30
cycles I ran re-enumerated after resume.
To check for regressions, I also tried a BeagleBone Black (AM335x,
dsps glue, suspend to RAM). There the link came back after every
resume (10 cycles without the patch, 30 with it), probably because
am335x_phy_suspend() powers the PHY off.
drivers/usb/musb/musb_core.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 73ac25f536..272683f14d 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -2825,18 +2825,24 @@ static int musb_suspend(struct device *dev)
spin_lock_irqsave(&musb->lock, flags);
+ musb_save_context(musb);
+
if (is_peripheral_active(musb)) {
- /* FIXME force disconnect unless we know USB will wake
- * the system up quickly enough to respond ...
+ /*
+ * We can't answer a host with the interrupts off, so drop the
+ * D+ pullup. musb_restore_context() puts back the state saved
+ * above.
*/
+ u8 power = musb_readb(musb->mregs, MUSB_POWER);
+
+ musb_writeb(musb->mregs, MUSB_POWER,
+ power & ~MUSB_POWER_SOFTCONN);
} else if (is_host_active(musb)) {
/* we know all the children are suspended; sometimes
* they will even be wakeup-enabled.
*/
}
- musb_save_context(musb);
-
spin_unlock_irqrestore(&musb->lock, flags);
return 0;
}
base-commit: 165768bb70265b5c38cf0b73fafd75be235f8b14
--
2.34.1