]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
usb: dwc3: Support USB3340x ULPI PHY high-speed negotiation.
authorIngo Rohloff <ingo.rohloff@lauterbach.com>
Thu, 5 Mar 2026 12:14:52 +0000 (13:14 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 11 Mar 2026 14:00:28 +0000 (15:00 +0100)
The Microchip USB3340x ULPI PHY requires a delay when switching to the
high-speed transmitter. See:
    http://ww1.microchip.com/downloads/en/DeviceDoc/80000645A.pdf
    Module 2 "Device Enumeration Failure with Link IP Systems"

For details on the behavior and fix, refer to the AMD (formerly Xilinx)
forum post: "USB stuck in full speed mode with USB3340 ULPI PHY, ZynqMP."

This patch uses the USB PHY Vendor-ID and Product-ID to detect the
USB3340 PHY and then applies the necessary fix if this PHY is found.

Signed-off-by: Ingo Rohloff <ingo.rohloff@lauterbach.com>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://patch.msgid.link/20260305121452.54082-2-ingo.rohloff@lauterbach.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/ulpi.c

index cacc4ec9f7ce7f7beb1fecfb1e0c1d2f96e3e7e6..58899b1fa96d29de63e534db13716b7152471ff1 100644 (file)
@@ -782,6 +782,24 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
        return 0;
 }
 
+static void dwc3_ulpi_setup(struct dwc3 *dwc)
+{
+       int index;
+       u32 reg;
+
+       /* Don't do anything if there is no ULPI PHY */
+       if (!dwc->ulpi)
+               return;
+
+       if (dwc->enable_usb2_transceiver_delay) {
+               for (index = 0; index < dwc->num_usb2_ports; index++) {
+                       reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
+                       reg |= DWC3_GUSB2PHYCFG_XCVRDLY;
+                       dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+               }
+       }
+}
+
 /**
  * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
  * @dwc: Pointer to our controller context structure
@@ -1363,6 +1381,8 @@ int dwc3_core_init(struct dwc3 *dwc)
                dwc->ulpi_ready = true;
        }
 
+       dwc3_ulpi_setup(dwc);
+
        if (!dwc->phys_ready) {
                ret = dwc3_core_get_phy(dwc);
                if (ret)
index 67bcc8dccc89c16fb2c8e492b99c6c12c126eb8d..7d08451842230e3b5d8d4c25c5b2284511b7d7ee 100644 (file)
 #define DWC3_GUSB2PHYCFG_SUSPHY                BIT(6)
 #define DWC3_GUSB2PHYCFG_ULPI_UTMI     BIT(4)
 #define DWC3_GUSB2PHYCFG_ENBLSLPM      BIT(8)
+#define DWC3_GUSB2PHYCFG_XCVRDLY       BIT(9)
 #define DWC3_GUSB2PHYCFG_PHYIF(n)      (n << 3)
 #define DWC3_GUSB2PHYCFG_PHYIF_MASK    DWC3_GUSB2PHYCFG_PHYIF(1)
 #define DWC3_GUSB2PHYCFG_USBTRDTIM(n)  (n << 10)
@@ -1163,6 +1164,8 @@ struct dwc3_glue_ops {
  *     3       - Reserved
  * @dis_metastability_quirk: set to disable metastability quirk.
  * @dis_split_quirk: set to disable split boundary.
+ * @enable_usb2_transceiver_delay: Set to insert a delay before the
+ *                     assertion of the TxValid signal during a HS Chirp.
  * @sys_wakeup: set if the device may do system wakeup.
  * @wakeup_configured: set if the device is configured for remote wakeup.
  * @suspended: set to track suspend event due to U3/L2.
@@ -1406,6 +1409,7 @@ struct dwc3 {
        unsigned                dis_metastability_quirk:1;
 
        unsigned                dis_split_quirk:1;
+       unsigned                enable_usb2_transceiver_delay:1;
        unsigned                async_callbacks:1;
        unsigned                sys_wakeup:1;
        unsigned                wakeup_configured:1;
index 57daad15f502da56420efda071e6a839ec10767a..a256b7f5d78b479150dc441e323aac6f307d4706 100644 (file)
 #include <linux/delay.h>
 #include <linux/time64.h>
 #include <linux/ulpi/regs.h>
+#include <linux/ulpi/driver.h>
 
 #include "core.h"
 #include "io.h"
 
+#define USB_VENDOR_MICROCHIP 0x0424
+
 #define DWC3_ULPI_ADDR(a) \
                ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \
                DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
@@ -83,6 +86,26 @@ static const struct ulpi_ops dwc3_ulpi_ops = {
        .write = dwc3_ulpi_write,
 };
 
+static void dwc3_ulpi_detect_config(struct dwc3 *dwc)
+{
+       struct ulpi *ulpi = dwc->ulpi;
+
+       switch (ulpi->id.vendor) {
+       case USB_VENDOR_MICROCHIP:
+               switch (ulpi->id.product) {
+               case 0x0009:
+                       /* Microchip USB3340 ULPI PHY */
+                       dwc->enable_usb2_transceiver_delay = true;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+}
+
 int dwc3_ulpi_init(struct dwc3 *dwc)
 {
        /* Register the interface */
@@ -92,6 +115,8 @@ int dwc3_ulpi_init(struct dwc3 *dwc)
                return PTR_ERR(dwc->ulpi);
        }
 
+       dwc3_ulpi_detect_config(dwc);
+
        return 0;
 }