[PATCH] usb: mtu3: fix system suspend in gadget mode after soft disconnect
Ryan Brue
ryanbrue.dev at gmail.com
Tue Sep 8 16:10:23 PDT 2026
ssusb_gadget_suspend() removes the D+ pullup for system suspend by calling
mtu3_dev_on_off(mtu, 0) directly. That writes the hardware SOFT_CONN bit
but leaves mtu->softconnect alone, deliberately: ssusb_gadget_resume()
needs it to decide whether to pull up again.
wait_for_ip_sleep() then asks ssusb_gadget_ip_sleep_check() whether the IP
can sleep, and that function tests mtu->softconnect -- the gadget's intent
rather than the state of the pin. So it answers "pulled up, the IP can
sleep" for a device that was just disconnected, and the poll for
SSUSB_IP_SLEEP_STS runs to its 100 ms timeout:
mtu3 11271000.usb: gadget (high-speed) pullup D-
mtu3 11271000.usb: ip sleep failed!!!
mtu3 11271000.usb: gadget (high-speed) pullup D+
mtu3 11271000.usb: PM: dpm_run_callback(): mtu3_suspend returns -16
PM: Some devices failed to suspend, or early wake event detected
and the suspend fails. The rule the hardware follows is stated in that
same function: the IP cannot sleep while D+ is not pulled up.
On an MT8173 tablet (Amazon Fire HD 10 2017, dr_mode = "peripheral") this
makes system suspend impossible whenever VBUS is present, which on a board
whose only USB port is also its charging input means whenever it is
charging. Unbinding the UDC, which clears mtu->softconnect through
mtu3_gadget_pullup() and so makes the check agree with the pin again, is
enough to let the same suspend succeed with the driver still bound and the
charger still attached:
gadget bound (softconnect=1): suspend returns -16, "ip sleep failed!!!"
gadget unbound (softconnect=0): suspend completes, 20 s of 20 s
So record what mtu3_dev_on_off() actually wrote and test that instead.
Runtime suspend is unaffected: it does not soft disconnect, so the pullup
is still set and the IP-sleep poll is still performed.
Fixes: e69027c25361 ("usb: mtu3: allow system suspend during active gadget connection")
Assisted-by: LLM
Signed-off-by: Ryan Brue <ryanbrue.dev at gmail.com>
---
drivers/usb/mtu3/mtu3.h | 1 +
drivers/usb/mtu3/mtu3_core.c | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index ba5a63669e5f..7f4fb87d8438 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -361,6 +361,7 @@ struct mtu3 {
unsigned is_self_powered:1;
unsigned test_mode:1;
unsigned softconnect:1;
+ unsigned pullup:1;
unsigned u1_enable:1;
unsigned u2_enable:1;
unsigned u3_capable:1;
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index a40bf5bad2d5..5ce61240194f 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -346,6 +346,7 @@ void mtu3_dev_on_off(struct mtu3 *mtu, int is_on)
else
mtu3_hs_softconn_set(mtu, is_on);
+ mtu->pullup = !!is_on;
dev_info(mtu->dev, "gadget (%s) pullup D%s\n",
usb_speed_string(mtu->speed), is_on ? "+" : "-");
}
@@ -1023,7 +1024,7 @@ bool ssusb_gadget_ip_sleep_check(struct ssusb_mtk *ssusb)
return true;
/* device is started and pullup D+, ip can sleep */
- if (mtu->is_active && mtu->softconnect)
+ if (mtu->is_active && mtu->pullup)
return true;
/* ip can't sleep if not pullup D+ when support device mode */
---
base-commit: df2908090cda368b01ff43709f51890076c56157
change-id: 20260908-rbrue-suez-upstreaming-mtu3-fix-suspend-usb-gadget-mode-c69c5104b25b
Best regards,
--
Ryan Brue <ryanbrue.dev at gmail.com>
More information about the Linux-mediatek
mailing list