usb: dwc3: Abort suspend on soft disconnect failure
commit 630a1dec3b0eba2a695b9063f1c205d585cbfec9 upstream.
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: 9f8a67b65a ("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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
27199ab790
commit
3b1407caac
@@ -2364,6 +2364,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
{
|
||||
u32 reg;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) {
|
||||
dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) &
|
||||
@@ -2382,7 +2383,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;
|
||||
@@ -2417,7 +2420,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -4788,8 +4788,15 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
int ret;
|
||||
|
||||
ret = dwc3_gadget_soft_disconnect(dwc);
|
||||
if (ret)
|
||||
goto err;
|
||||
/*
|
||||
* Attempt to reset the controller's state. Likely no
|
||||
* communication can be established until the host
|
||||
* performs a port reset.
|
||||
*/
|
||||
if (ret && dwc->softconnect) {
|
||||
dwc3_gadget_soft_connect(dwc);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->gadget_driver)
|
||||
@@ -4797,17 +4804,6 @@ int dwc3_gadget_suspend(struct dwc3 *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)
|
||||
dwc3_gadget_soft_connect(dwc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||
|
||||
Reference in New Issue
Block a user