When starting the dwc2 core, it may not be seen in the correct mode,
when the dwc2_core_reset() gets called. This is seen on stm32mp1 upon
first call to reconfig_usbd(), a warning can be seen:
- "Waiting for GINTSTS_CURMODE_HOST timeout"
When the core gets initialized, wait for the debounce filters, so the
correct mode gets read and polled later in dwc2_core_reset().
The routines are added to dwc2_core.c, and inspired by the linux dwc2
driver.
Fixes: c5d685b8993c ("usb: dwc2: Unify flush and reset logic with v4.20a
support")
Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/usb/common/dwc2_core.c | 45 +++++++++++++++++++++++++++++++++++++++
drivers/usb/common/dwc2_core.h | 1 +
drivers/usb/gadget/dwc2_udc_otg.c | 3 +++
3 files changed, 49 insertions(+)
diff --git a/drivers/usb/common/dwc2_core.c b/drivers/usb/common/dwc2_core.c
index 37007134e5b3..31ecd100f90c 100644
--- a/drivers/usb/common/dwc2_core.c
+++ b/drivers/usb/common/dwc2_core.c
@@ -129,3 +129,48 @@ int dwc2_flush_rx_fifo(struct dwc2_core_regs *regs)
return 0;
}
+
+static unsigned int dwc2_op_mode(struct dwc2_core_regs *regs)
+{
+ return FIELD_GET(GHWCFG2_OP_MODE_MASK,
readl(®s->global_regs.ghwcfg2));
+}
+
+/* Returns true if the controller is capable of DRD. */
+static bool dwc2_hw_is_otg(struct dwc2_core_regs *regs)
+{
+ unsigned int op_mode = dwc2_op_mode(regs);
+
+ return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
+ (op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
+ (op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
+}
+
+/*
+ * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce
+ * filter is enabled.
+ */
+bool dwc2_iddig_filter_enabled(struct dwc2_core_regs *regs)
+{
+ u32 snpsid;
+
+ if (!dwc2_hw_is_otg(regs))
+ return false;
+
+ /* Check if core configuration includes the IDDIG filter. */
+ if (!FIELD_GET(GHWCFG4_IDDIG_FILT_EN,
readl(®s->global_regs.ghwcfg4)))
+ return false;
+
+ /*
+ * Check if the IDDIG debounce filter is bypassed. Available
+ * in core version >= 3.10a.
+ */
+ snpsid = readl(®s->global_regs.gsnpsid);
+ if (FIELD_GET(GSNPSID_VER_MASK, snpsid) >= 0x310a) {
+ u32 gotgctl = readl(®s->global_regs.gotgctl);
+
+ if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS)
+ return false;
+ }
+
+ return true;
+}
diff --git a/drivers/usb/common/dwc2_core.h b/drivers/usb/common/dwc2_core.h
index 1897ad7cb540..7c26b7f7fc19 100644
--- a/drivers/usb/common/dwc2_core.h
+++ b/drivers/usb/common/dwc2_core.h
@@ -128,6 +128,7 @@ struct dwc2_core_regs {
int dwc2_core_reset(struct dwc2_core_regs *regs);
int dwc2_flush_tx_fifo(struct dwc2_core_regs *regs, const int num);
int dwc2_flush_rx_fifo(struct dwc2_core_regs *regs);
+bool dwc2_iddig_filter_enabled(struct dwc2_core_regs *regs);
/* Core Global Register */
#define GOTGCTL_CHIRPEN BIT(27)
diff --git a/drivers/usb/gadget/dwc2_udc_otg.c
b/drivers/usb/gadget/dwc2_udc_otg.c
index 2129bfa1edb6..4dc049d64b14 100644
--- a/drivers/usb/gadget/dwc2_udc_otg.c
+++ b/drivers/usb/gadget/dwc2_udc_otg.c
@@ -1150,6 +1150,9 @@ static int dwc2_udc_otg_probe(struct udevice *dev)
}
}
+ if (dwc2_iddig_filter_enabled(usbotg_reg))
+ mdelay(100);
+
ret = dwc2_udc_probe(plat);
if (ret)
return ret;
--
2.43.0