From dde6656910b347eb1c7da5e1e0de7894f6100d4e Mon Sep 17 00:00:00 2001 From: William Wu Date: Mon, 13 Sep 2021 15:14:01 +0800 Subject: [PATCH] usb: dwc3: core: fix duplicate phy init when switch device According to the programming guide, it needs to reset the device with DCTL.CSftRst when switching from host to device. The current code use dwc3_core_soft_reset() to do DCTL.CSftRst, it will also duplicate phy init which has been done in runtime resume routine, this cause the phy init/exit operations are unbalanced. Without this patch, the dwc3 gadget resume fail on RK3568 EVB1 with the following log: dwc3 fcc00000.dwc3: failed to enable ep0out It's because that the init_count of usb3 phy is not 0 when resume, so the dwc3 fail to call usb3 phy init, and the 3.0 pipe clock is not be running. Fixes: b48bcb27ae8f ("FROMGIT: usb: dwc3: core: Do core softreset when switch mode") Signed-off-by: William Wu Change-Id: I58ec26f9f007c94f8979eeeb9a9d683c6db9548f --- drivers/usb/dwc3/core.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 919090293487..3c9ab4930ca4 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -114,13 +114,12 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) dwc->current_dr_role = mode; } -static int dwc3_core_soft_reset(struct dwc3 *dwc); - static void __dwc3_set_mode(struct work_struct *work) { struct dwc3 *dwc = work_to_dwc(work); unsigned long flags; int ret; + int retries = 1000; u32 reg; mutex_lock(&dwc->mutex); @@ -201,7 +200,26 @@ static void __dwc3_set_mode(struct work_struct *work) } break; case DWC3_GCTL_PRTCAP_DEVICE: - dwc3_core_soft_reset(dwc); + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= DWC3_DCTL_CSFTRST; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32)) + retries = 10; + + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + goto done; + + if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32)) + msleep(20); + else + udelay(1); + } while (--retries); +done: + if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A)) + msleep(50); dwc3_event_buffers_setup(dwc);