]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
usb: dwc3: Abort suspend on soft disconnect failure
authorKuen-Han Tsai <khtsai@google.com>
Wed, 28 May 2025 10:03:11 +0000 (18:03 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Jul 2025 16:35:17 +0000 (18:35 +0200)
[ Upstream commit 630a1dec3b0eba2a695b9063f1c205d585cbfec9 ]

When dwc3_gadget_soft_disconnect() fails, dwc3_suspend_common() keeps
going with the suspend, resulting in a period where the power domain is
off, but the gadget driver remains connected.  Within this time frame,
invoking vbus_event_work() will cause an error as it attempts to access
DWC3 registers for endpoint disabling after the power domain has been
completely shut down.

Abort the suspend sequence when dwc3_gadget_suspend() cannot halt the
controller and proceeds with a soft connect.

Fixes: 9f8a67b65a49 ("usb: dwc3: gadget: fix gadget suspend/resume")
Cc: stable <stable@kernel.org>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://lore.kernel.org/r/20250528100315.2162699-1-khtsai@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/dwc3/core.c
drivers/usb/dwc3/gadget.c

index 30404461ef7de31b8b205e746faf84c8a8070117..b7eaad099309c5cb37f3e45ae44d6af1e57a856c 100644 (file)
@@ -2128,6 +2128,7 @@ assert_reset:
 static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
 {
        u32 reg;
+       int ret;
 
        if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) {
                dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) &
@@ -2146,7 +2147,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
        case DWC3_GCTL_PRTCAP_DEVICE:
                if (pm_runtime_suspended(dwc->dev))
                        break;
-               dwc3_gadget_suspend(dwc);
+               ret = dwc3_gadget_suspend(dwc);
+               if (ret)
+                       return ret;
                synchronize_irq(dwc->irq_gadget);
                dwc3_core_exit(dwc);
                break;
@@ -2177,7 +2180,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
                        break;
 
                if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
-                       dwc3_gadget_suspend(dwc);
+                       ret = dwc3_gadget_suspend(dwc);
+                       if (ret)
+                               return ret;
                        synchronize_irq(dwc->irq_gadget);
                }
 
index f51d743bb3ecc63dff0d564c4982e58927fb79b7..a17af4ab20a32361344fd6b5b3bd76c19b0104d1 100644 (file)
@@ -4802,26 +4802,22 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
        int ret;
 
        ret = dwc3_gadget_soft_disconnect(dwc);
-       if (ret)
-               goto err;
-
-       spin_lock_irqsave(&dwc->lock, flags);
-       if (dwc->gadget_driver)
-               dwc3_disconnect_gadget(dwc);
-       spin_unlock_irqrestore(&dwc->lock, flags);
-
-       return 0;
-
-err:
        /*
         * Attempt to reset the controller's state. Likely no
         * communication can be established until the host
         * performs a port reset.
         */
-       if (dwc->softconnect)
+       if (ret && dwc->softconnect) {
                dwc3_gadget_soft_connect(dwc);
+               return -EAGAIN;
+       }
 
-       return ret;
+       spin_lock_irqsave(&dwc->lock, flags);
+       if (dwc->gadget_driver)
+               dwc3_disconnect_gadget(dwc);
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       return 0;
 }
 
 int dwc3_gadget_resume(struct dwc3 *dwc)