usb: dwc2: Fix dwc2 OTG-HOST resume fails to recognize peripherals

This patch fix following two cases dwc2 resume does not recognize
peripherals.

1. plug in device after system suspend, then press the power-on
button to wake up,at this time the dr_mode is OTG, op_state is
still Peripheral, the Bit[0] of GINTSTS is 1 and the port power
is off, dwc2 will not resume at this time.

2. plug in device then press the power-on button to put the system
to sleep, then we press the power-on button to wake up the system,
At this time, the dr_mode is OTG, op_state is still Host, the Bit[0]
of GINTSTS is 1, dwc2 will not resume and working abnormally.

To resolve the first exception, we should call the dwc2_hsotg_resume()
directly to resume dwc2. To resolve the second exception, if the dwc2
is lost power during suspend like RK3326S platform, wo should reinit
the core to device mode, and after do dwc2_hsotg_resume, it can trigger
the ID status change interrupt if the OTG cable is still connect. Then
we can init it for host mode in the ID status change interrupt handler.
We can use the power on bit of Hprt register to distinguish whether
it is lost power during suspend.

Signed-off-by: Jianwei Zheng <jianwei.zheng@rock-chips.com>
Change-Id: I7cd09bce993dcee8e5bfcaddd5fe884cdfab6b52
This commit is contained in:
Jianwei Zheng
2022-07-07 15:32:34 +08:00
committed by William Wu
parent 5452a06eed
commit 25da86fe62

View File

@@ -798,44 +798,44 @@ static int __maybe_unused dwc2_resume(struct device *dev)
dwc2_drd_resume(dwc2);
if (dwc2_is_device_mode(dwc2)) {
if (dwc2->dr_mode == USB_DR_MODE_HOST) {
/* Reinit for Host mode if lost power */
dwc2_force_mode(dwc2, true);
if (dwc2->dr_mode == USB_DR_MODE_HOST && dwc2_is_device_mode(dwc2)) {
/* Reinit for Host mode if lost power */
dwc2_force_mode(dwc2, true);
spin_lock_irqsave(&dwc2->lock, flags);
dwc2_hsotg_disconnect(dwc2);
spin_unlock_irqrestore(&dwc2->lock, flags);
spin_lock_irqsave(&dwc2->lock, flags);
dwc2_hsotg_disconnect(dwc2);
spin_unlock_irqrestore(&dwc2->lock, flags);
dwc2->op_state = OTG_STATE_A_HOST;
/* Initialize the Core for Host mode */
dwc2_core_init(dwc2, false);
dwc2_enable_global_interrupts(dwc2);
dwc2_hcd_start(dwc2);
} else {
/* Reinit for OTG in Host mode if lost power */
if (dwc2->dr_mode == USB_DR_MODE_OTG &&
dwc2->op_state == OTG_STATE_A_HOST) {
/*
* Reinit the core to device mode, and later
* after do dwc2_hsotg_resume, it can trigger
* the ID status change interrupt if the OTG
* cable is still connected, then we can init
* for Host mode in the ID status change
* interrupt handler.
*/
spin_lock_irqsave(&dwc2->lock, flags);
dwc2_hcd_disconnect(dwc2, true);
dwc2->op_state = OTG_STATE_B_PERIPHERAL;
dwc2->lx_state = DWC2_L3;
if (!dwc2->driver)
dwc2_hsotg_core_init_disconnected(dwc2, false);
spin_unlock_irqrestore(&dwc2->lock, flags);
dwc2->op_state = OTG_STATE_A_HOST;
/* Initialize the Core for Host mode */
dwc2_core_init(dwc2, false);
dwc2_enable_global_interrupts(dwc2);
dwc2_hcd_start(dwc2);
} else if (dwc2->dr_mode == USB_DR_MODE_OTG &&
dwc2->op_state == OTG_STATE_A_HOST &&
!(dwc2_readl(dwc2, HPRT0) & HPRT0_PWR)) {
/*
* Reinit the core to device mode, and later
* after do dwc2_hsotg_resume, it can trigger
* the ID status change interrupt if the OTG
* cable is still connected, then we can init
* for Host mode in the ID status change
* interrupt handler.
*/
spin_lock_irqsave(&dwc2->lock, flags);
dwc2_hcd_disconnect(dwc2, true);
dwc2->op_state = OTG_STATE_B_PERIPHERAL;
dwc2->lx_state = DWC2_L3;
if (!dwc2->driver)
dwc2_hsotg_core_init_disconnected(dwc2, false);
spin_unlock_irqrestore(&dwc2->lock, flags);
}
ret = dwc2_hsotg_resume(dwc2);
}
ret = dwc2_hsotg_resume(dwc2);
} else if (dwc2_is_device_mode(dwc2) ||
(dwc2_is_host_mode(dwc2) &&
dwc2->dr_mode == USB_DR_MODE_OTG &&
dwc2->op_state == OTG_STATE_B_PERIPHERAL)) {
ret = dwc2_hsotg_resume(dwc2);
}
return ret;