From 25da86fe62da34608c92ec363987f84ca728f38e Mon Sep 17 00:00:00 2001 From: Jianwei Zheng Date: Thu, 7 Jul 2022 15:32:34 +0800 Subject: [PATCH] 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 Change-Id: I7cd09bce993dcee8e5bfcaddd5fe884cdfab6b52 --- drivers/usb/dwc2/platform.c | 70 ++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index f9b5c267bd7b..2c8f5f45255f 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -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;