Commit 630a1dec authored by Kuen-Han Tsai's avatar Kuen-Han Tsai Committed by Greg Kroah-Hartman
Browse files

usb: dwc3: Abort suspend on soft disconnect failure



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: 9f8a67b6 ("usb: dwc3: gadget: fix gadget suspend/resume")
Cc: stable <stable@kernel.org>
Acked-by: default avatarThinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: default avatarKuen-Han Tsai <khtsai@google.com>
Link: https://lore.kernel.org/r/20250528100315.2162699-1-khtsai@google.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7e2c421e
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -2422,6 +2422,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)) &
@@ -2440,7 +2441,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;
@@ -2475,7 +2478,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);
		}

+9 −13
Original line number Diff line number Diff line
@@ -4821,26 +4821,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)