Commit fef6bc37 authored by John Youn's avatar John Youn Committed by Felipe Balbi

usb: dwc2: Add delay to core soft reset

Add a delay to the core soft reset function to account for the IDDIG
debounce filter.

If the current mode is host, either due to the force mode bit being
set (which persists after core reset) or the connector id pin, a core
soft reset will temporarily reset the mode to device and a delay from
the IDDIG debounce filter will occur before going back to host mode.
Tested-by: default avatarStefan Wahren <stefan.wahren@i2se.com>
Signed-off-by: default avatarJohn Youn <johnyoun@synopsys.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent d0f0ac56
...@@ -238,6 +238,77 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) ...@@ -238,6 +238,77 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
return ret; return ret;
} }
/**
* dwc2_wait_for_mode() - Waits for the controller mode.
* @hsotg: Programming view of the DWC_otg controller.
* @host_mode: If true, waits for host mode, otherwise device mode.
*/
static void dwc2_wait_for_mode(struct dwc2_hsotg *hsotg,
bool host_mode)
{
ktime_t start;
ktime_t end;
unsigned int timeout = 110;
dev_vdbg(hsotg->dev, "Waiting for %s mode\n",
host_mode ? "host" : "device");
start = ktime_get();
while (1) {
s64 ms;
if (dwc2_is_host_mode(hsotg) == host_mode) {
dev_vdbg(hsotg->dev, "%s mode set\n",
host_mode ? "Host" : "Device");
break;
}
end = ktime_get();
ms = ktime_to_ms(ktime_sub(end, start));
if (ms >= (s64)timeout) {
dev_warn(hsotg->dev, "%s: Couldn't set %s mode\n",
__func__, host_mode ? "host" : "device");
break;
}
usleep_range(1000, 2000);
}
}
/**
* dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce
* filter is enabled.
*/
static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
{
u32 gsnpsid;
u32 ghwcfg4;
if (!dwc2_hw_is_otg(hsotg))
return false;
/* Check if core configuration includes the IDDIG filter. */
ghwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4);
if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN))
return false;
/*
* Check if the IDDIG debounce filter is bypassed. Available
* in core version >= 3.10a.
*/
gsnpsid = dwc2_readl(hsotg->regs + GSNPSID);
if (gsnpsid >= DWC2_CORE_REV_3_10a) {
u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS)
return false;
}
return true;
}
/* /*
* Do core a soft reset of the core. Be careful with this because it * Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core. * resets all the internal state machines of the core.
...@@ -246,9 +317,30 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) ...@@ -246,9 +317,30 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
{ {
u32 greset; u32 greset;
int count = 0; int count = 0;
bool wait_for_host_mode = false;
dev_vdbg(hsotg->dev, "%s()\n", __func__); dev_vdbg(hsotg->dev, "%s()\n", __func__);
/*
* If the current mode is host, either due to the force mode
* bit being set (which persists after core reset) or the
* connector id pin, a core soft reset will temporarily reset
* the mode to device. A delay from the IDDIG debounce filter
* will occur before going back to host mode.
*
* Determine whether we will go back into host mode after a
* reset and account for this delay after the reset.
*/
if (dwc2_iddig_filter_enabled(hsotg)) {
u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
u32 gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
if (!(gotgctl & GOTGCTL_CONID_B) ||
(gusbcfg & GUSBCFG_FORCEHOSTMODE)) {
wait_for_host_mode = true;
}
}
/* Core Soft Reset */ /* Core Soft Reset */
greset = dwc2_readl(hsotg->regs + GRSTCTL); greset = dwc2_readl(hsotg->regs + GRSTCTL);
greset |= GRSTCTL_CSFTRST; greset |= GRSTCTL_CSFTRST;
...@@ -277,6 +369,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg) ...@@ -277,6 +369,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
} }
} while (!(greset & GRSTCTL_AHBIDLE)); } while (!(greset & GRSTCTL_AHBIDLE));
if (wait_for_host_mode)
dwc2_wait_for_mode(hsotg, true);
return 0; return 0;
} }
......
...@@ -882,6 +882,7 @@ struct dwc2_hsotg { ...@@ -882,6 +882,7 @@ struct dwc2_hsotg {
#define DWC2_CORE_REV_2_92a 0x4f54292a #define DWC2_CORE_REV_2_92a 0x4f54292a
#define DWC2_CORE_REV_2_94a 0x4f54294a #define DWC2_CORE_REV_2_94a 0x4f54294a
#define DWC2_CORE_REV_3_00a 0x4f54300a #define DWC2_CORE_REV_3_00a 0x4f54300a
#define DWC2_CORE_REV_3_10a 0x4f54310a
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags { union dwc2_hcd_internal_flags {
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#define GOTGCTL_ASESVLD (1 << 18) #define GOTGCTL_ASESVLD (1 << 18)
#define GOTGCTL_DBNC_SHORT (1 << 17) #define GOTGCTL_DBNC_SHORT (1 << 17)
#define GOTGCTL_CONID_B (1 << 16) #define GOTGCTL_CONID_B (1 << 16)
#define GOTGCTL_DBNCE_FLTR_BYPASS (1 << 15)
#define GOTGCTL_DEVHNPEN (1 << 11) #define GOTGCTL_DEVHNPEN (1 << 11)
#define GOTGCTL_HSTSETHNPEN (1 << 10) #define GOTGCTL_HSTSETHNPEN (1 << 10)
#define GOTGCTL_HNPREQ (1 << 9) #define GOTGCTL_HNPREQ (1 << 9)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment