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(&regs->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(&regs->global_regs.ghwcfg4)))
+               return false;
+
+       /*
+        * Check if the IDDIG debounce filter is bypassed. Available
+        * in core version >= 3.10a.
+        */
+       snpsid = readl(&regs->global_regs.gsnpsid);
+       if (FIELD_GET(GSNPSID_VER_MASK, snpsid) >= 0x310a) {
+               u32 gotgctl = readl(&regs->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

Reply via email to