]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
econet: en7528: add USB support 22498/head
authorAhmed Naseef <naseefkm@gmail.com>
Thu, 19 Mar 2026 11:55:28 +0000 (15:55 +0400)
committerHauke Mehrtens <hauke@hauke-m.de>
Sun, 10 May 2026 17:44:56 +0000 (19:44 +0200)
Add USB host support for EN7528 SoC:

- New phy-en7528-usb driver with U2 slew rate calibration and U3
  RX impedance tuning, based on GPL vendor code and the Airoha
  AN7581 USB PHY driver
- xHCI LTSSM timing quirk for TD 6.5 compliance (patch 915)
- USB PHY and xHCI DTS nodes with IPPC register mapping
- VBUS power via regulator-fixed for DASAN H660GM-A
- Enable USB, xHCI, PHY, and regulator kernel configs

Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22498
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
target/linux/econet/dts/en7528.dtsi
target/linux/econet/dts/en7528_dasan_h660gm-a-generic.dts
target/linux/econet/dts/en7528_dasan_h660gm-a.dtsi
target/linux/econet/en7528/config-6.12
target/linux/econet/image/en7528.mk
target/linux/econet/patches-6.12/914-phy-add-en7528-usb-phy-driver.patch [new file with mode: 0644]
target/linux/econet/patches-6.12/915-usb-xhci-mtk-add-en7528-ltssm-quirk.patch [new file with mode: 0644]

index 1ccc298d211506d0c6cde28f019edb1a70e36e85..bbd7f9786d82387f3b44d46394fca10b22b20a90 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <dt-bindings/interrupt-controller/mips-gic.h>
 #include <dt-bindings/clock/en7523-clk.h>
+#include <dt-bindings/phy/phy.h>
 #include <dt-bindings/reset/airoha,en7523-reset.h>
 
 / {
                };
        };
 
+       usb_phy: usb-phy@1fa80000 {
+               compatible = "econet,en7528-usb-phy";
+               reg = <0x1fa80000 0x1400>;
+               #phy-cells = <1>;
+       };
+
+       usb: usb@1fb90000 {
+               compatible = "econet,en7528-xhci";
+               reg = <0x1fb90000 0x3e00>,
+                     <0x1fb93e00 0x100>;
+               reg-names = "mac", "ippc";
+               interrupt-parent = <&gic>;
+               interrupts = <GIC_SHARED 17 IRQ_TYPE_LEVEL_HIGH>;
+               phys = <&usb_phy PHY_TYPE_USB2>, <&usb_phy PHY_TYPE_USB3>;
+               usb3-lpm-capable;
+               status = "disabled";
+       };
+
        uart: serial@1fbf0000 {
                compatible = "airoha,en7523-uart";
                reg = <0x1fbf0000 0x30>;
index 9473f2b390d347924be54e3f7a89de704597e322..1406e76c69426cac5ad94114037e6e7fa0fe23ff 100644 (file)
@@ -20,6 +20,8 @@
                        color = <LED_COLOR_ID_GREEN>;
                        function = LED_FUNCTION_USB;
                        gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
+                       linux,default-trigger = "usbport";
+                       trigger-sources = <&usb>;
                };
 
                led-lan1-green {
index d77a915dcc996e2925e137ac9ba8aa8d53a82665..bd1901670254cd920b07a97f38c5aa17368dac71 100644 (file)
                };
        };
 
-       gpio_export {
-               compatible = "gpio-export";
-
-               usb-power {
-                       gpio-export,name = "usb-power";
-                       gpio-export,output = <1>;
-                       gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
-               };
+       reg_usb_vbus: regulator-usb-vbus {
+               compatible = "regulator-fixed";
+               regulator-name = "usb-vbus";
+               regulator-min-microvolt = <5000000>;
+               regulator-max-microvolt = <5000000>;
+               gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
+               enable-active-high;
        };
 
        keys {
        };
 };
 
+&usb {
+       status = "okay";
+       vbus-supply = <&reg_usb_vbus>;
+};
+
 &pcie0 {
        status = "okay";
 };
index 81f75829a17c32dcb6c635f82af70167ec16a4dd..f5778340282b3d183282edfad1f70206bbd10cc0 100644 (file)
@@ -58,6 +58,7 @@ CONFIG_FS_IOMAP=y
 CONFIG_FUNCTION_ALIGNMENT=0
 CONFIG_FW_LOADER_PAGED_BUF=y
 CONFIG_FW_LOADER_SYSFS=y
+CONFIG_GENERIC_ALLOCATOR=y
 CONFIG_GENERIC_ATOMIC64=y
 CONFIG_GENERIC_CLOCKEVENTS=y
 CONFIG_GENERIC_CMOS_UPDATE=y
@@ -140,6 +141,7 @@ CONFIG_NET_EGRESS=y
 CONFIG_NET_FLOW_LIMIT=y
 CONFIG_NET_INGRESS=y
 CONFIG_NET_XGRESS=y
+CONFIG_NLS=y
 CONFIG_NR_CPUS=4
 CONFIG_NVMEM=y
 CONFIG_NVMEM_LAYOUTS=y
@@ -165,6 +167,7 @@ CONFIG_PCI_MSI_ARCH_FALLBACKS=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PGTABLE_LEVELS=2
 CONFIG_PHY_EN7528_PCIE=y
+CONFIG_PHY_EN7528_USB=y
 CONFIG_PTP_1588_CLOCK_OPTIONAL=y
 CONFIG_QUEUED_RWLOCKS=y
 CONFIG_QUEUED_SPINLOCKS=y
@@ -172,6 +175,8 @@ CONFIG_RANDSTRUCT_NONE=y
 CONFIG_RATIONAL=y
 CONFIG_REGMAP=y
 CONFIG_REGMAP_MMIO=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_RESET_CONTROLLER=y
 CONFIG_RFS_ACCEL=y
 CONFIG_RPS=y
@@ -217,6 +222,12 @@ CONFIG_TIMER_PROBE=y
 CONFIG_TREE_RCU=y
 CONFIG_TREE_SRCU=y
 CONFIG_UBIFS_FS=y
+CONFIG_USB=y
+CONFIG_USB_COMMON=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_XHCI_MTK=y
+# CONFIG_USB_XHCI_PLATFORM is not set
 CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
 CONFIG_USE_OF=y
 CONFIG_WEAK_ORDERING=y
index b6e132c18ce420d2926adfbaf0da6bddcdc88d3c..9f68905af602df7d9b76d151f5ac716ca5729d72 100644 (file)
@@ -10,7 +10,7 @@ TARGET_DEVICES += en7528_generic
 define Device/dasan_h660gm-a
   DEVICE_VENDOR := DASAN
   DEVICE_MODEL := H660GM-A
-  DEVICE_PACKAGES := kmod-mt7603 kmod-mt7615e kmod-mt7663-firmware-ap
+  DEVICE_PACKAGES := kmod-usb2 kmod-mt7603 kmod-mt7615e kmod-mt7663-firmware-ap
   TRX_MODEL := Dewberry
   IMAGES := tclinux.trx
   IMAGE/tclinux.trx := append-kernel | lzma | tclinux-trx
diff --git a/target/linux/econet/patches-6.12/914-phy-add-en7528-usb-phy-driver.patch b/target/linux/econet/patches-6.12/914-phy-add-en7528-usb-phy-driver.patch
new file mode 100644 (file)
index 0000000..308703c
--- /dev/null
@@ -0,0 +1,358 @@
+From: Ahmed Naseef <naseefkm@gmail.com>
+Subject: phy: add EN7528 USB PHY driver
+
+Add USB PHY driver for EcoNet EN7528 SoC.
+
+Based on GPL vendor code at https://github.com/keenetic/kernel-49
+and the Airoha AN7581 USB PHY driver by Christian Marangi.
+
+Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
+--- a/drivers/phy/Kconfig
++++ b/drivers/phy/Kconfig
+@@ -93,6 +93,17 @@ config PHY_EN7528_PCIE
+         This driver provides PHY initialization for the two PCIe ports
+         on EN7528 SoC.
++config PHY_EN7528_USB
++      tristate "EcoNet EN7528 USB PHY Driver"
++      depends on ECONET || COMPILE_TEST
++      depends on OF
++      select GENERIC_PHY
++      select REGMAP_MMIO
++      help
++        Say 'Y' here to add support for EcoNet EN7528 USB PHY driver.
++        This driver creates the basic PHY instance and provides
++        initialization callback for the USB port.
++
+ source "drivers/phy/allwinner/Kconfig"
+ source "drivers/phy/amlogic/Kconfig"
+ source "drivers/phy/broadcom/Kconfig"
+--- a/drivers/phy/Makefile
++++ b/drivers/phy/Makefile
+@@ -12,6 +12,7 @@ obj-$(CONFIG_PHY_PISTACHIO_USB)              += phy-
+ obj-$(CONFIG_USB_LGM_PHY)             += phy-lgm-usb.o
+ obj-$(CONFIG_PHY_AIROHA_PCIE)         += phy-airoha-pcie.o
+ obj-$(CONFIG_PHY_EN7528_PCIE)         += phy-en7528-pcie.o
++obj-$(CONFIG_PHY_EN7528_USB)          += phy-en7528-usb.o
+ obj-y                                 += allwinner/   \
+                                          amlogic/     \
+                                          broadcom/    \
+--- /dev/null
++++ b/drivers/phy/phy-en7528-usb.c
+@@ -0,0 +1,316 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * EcoNet EN7528 USB PHY driver
++ *
++ * Based on GPL vendor code at https://github.com/keenetic/kernel-49
++ * and the Airoha AN7581 USB PHY driver by
++ * Christian Marangi <ansuelsmth@gmail.com>
++ */
++
++#include <dt-bindings/phy/phy.h>
++#include <linux/bitfield.h>
++#include <linux/math.h>
++#include <linux/module.h>
++#include <linux/phy/phy.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++/* Frequency Meter registers (shared across ports) */
++#define EN7528_USB_PHY_FMCR0                  0x100
++#define   EN7528_USB_PHY_MONCLK_SEL           GENMASK(27, 26)
++#define   EN7528_USB_PHY_FREQDET_EN           BIT(24)
++#define   EN7528_USB_PHY_CYCLECNT             GENMASK(23, 0)
++#define EN7528_USB_PHY_FMMONR0                        0x10c
++#define EN7528_USB_PHY_FMMONR1                        0x110
++#define   EN7528_USB_PHY_FRCK_EN              BIT(8)
++
++/* U2 PHY port bases */
++#define EN7528_USB_PHY_U2_P0_BASE             0x300
++#define EN7528_USB_PHY_U2_P1_BASE             0x1300
++#define EN7528_USB_PHY_NUM_U2_PORTS           2
++
++/* U2 PHY register offsets (relative to port base) */
++#define EN7528_USB_PHY_ACR0                   0x10
++#define   EN7528_USB_PHY_HSTX_SRCAL_EN                BIT(23)
++#define   EN7528_USB_PHY_HSTX_SRCTRL          GENMASK(18, 16)
++#define EN7528_USB_PHY_ACR3                   0x1c
++#define EN7528_USB_PHY_ACR3_ENABLE            0xC0240000
++
++/* U3 PHYA registers (base at +0xb00) */
++#define EN7528_USB_PHY_U3_PHYA_REG11          0xb2c
++#define   EN7528_USB_PHY_RX_IMPSEL            GENMASK(13, 12)
++
++#define EN7528_USB_PHY_FM_DET_CYCLE_CNT               1024
++#define EN7528_USB_PHY_REF_CK                 20      /* MHz */
++#define EN7528_USB_PHY_SR_COEF                        28
++#define EN7528_USB_PHY_SR_COEF_DIVISOR                1000
++#define EN7528_USB_PHY_DEFAULT_SR             4
++
++#define EN7528_USB_PHY_FREQDET_SLEEP          1000    /* 1ms */
++#define EN7528_USB_PHY_FREQDET_TIMEOUT                (EN7528_USB_PHY_FREQDET_SLEEP * 10)
++
++static const unsigned int en7528_u2_port_bases[EN7528_USB_PHY_NUM_U2_PORTS] = {
++      EN7528_USB_PHY_U2_P0_BASE,
++      EN7528_USB_PHY_U2_P1_BASE,
++};
++
++struct en7528_usb_phy_instance {
++      struct phy *phy;
++      u32 type;
++};
++
++enum en7528_usb_phy_type {
++      EN7528_PHY_USB2,
++      EN7528_PHY_USB3,
++
++      EN7528_PHY_USB_MAX,
++};
++
++struct en7528_usb_phy_priv {
++      struct device *dev;
++      struct regmap *regmap;
++
++      struct en7528_usb_phy_instance *phys[EN7528_PHY_USB_MAX];
++};
++
++static void en7528_usb_phy_slew_rate_calibration(struct en7528_usb_phy_priv *priv,
++                                                unsigned int port_id,
++                                                unsigned int port_base)
++{
++      unsigned int acr0 = port_base + EN7528_USB_PHY_ACR0;
++      u32 fm_out;
++      u32 srctrl;
++
++      /* Enable HS TX SR calibration */
++      regmap_set_bits(priv->regmap, acr0,
++                      EN7528_USB_PHY_HSTX_SRCAL_EN);
++
++      usleep_range(1000, 1500);
++
++      /* Enable free run clock */
++      regmap_set_bits(priv->regmap, EN7528_USB_PHY_FMMONR1,
++                      EN7528_USB_PHY_FRCK_EN);
++
++      /* Select monitor clock: port 0 = MONCLK_SEL 0, port 1 = MONCLK_SEL 1 */
++      regmap_update_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
++                         EN7528_USB_PHY_MONCLK_SEL,
++                         FIELD_PREP(EN7528_USB_PHY_MONCLK_SEL, port_id));
++
++      /* Set cycle count */
++      regmap_update_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
++                         EN7528_USB_PHY_CYCLECNT,
++                         FIELD_PREP(EN7528_USB_PHY_CYCLECNT,
++                                    EN7528_USB_PHY_FM_DET_CYCLE_CNT));
++
++      /* Enable frequency meter */
++      regmap_set_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
++                      EN7528_USB_PHY_FREQDET_EN);
++
++      /* Timeout can happen and we will apply default value at the end */
++      (void)regmap_read_poll_timeout(priv->regmap, EN7528_USB_PHY_FMMONR0,
++                               fm_out, fm_out,
++                               EN7528_USB_PHY_FREQDET_SLEEP,
++                               EN7528_USB_PHY_FREQDET_TIMEOUT);
++
++      /* Disable frequency meter */
++      regmap_clear_bits(priv->regmap, EN7528_USB_PHY_FMCR0,
++                        EN7528_USB_PHY_FREQDET_EN);
++
++      /* Disable free run clock */
++      regmap_clear_bits(priv->regmap, EN7528_USB_PHY_FMMONR1,
++                        EN7528_USB_PHY_FRCK_EN);
++
++      /* Disable HS TX SR calibration */
++      regmap_clear_bits(priv->regmap, acr0,
++                        EN7528_USB_PHY_HSTX_SRCAL_EN);
++
++      usleep_range(1000, 1500);
++
++      if (!fm_out) {
++              srctrl = EN7528_USB_PHY_DEFAULT_SR;
++              dev_err(priv->dev, "port%u: frequency not detected, using default SR calibration.\n",
++                      port_id);
++      } else {
++              /* (1024 / FM_OUT) * REF_CK * SR_COEF */
++              srctrl = EN7528_USB_PHY_REF_CK * EN7528_USB_PHY_SR_COEF;
++              srctrl = (srctrl * EN7528_USB_PHY_FM_DET_CYCLE_CNT) / fm_out;
++              srctrl = DIV_ROUND_CLOSEST(srctrl, EN7528_USB_PHY_SR_COEF_DIVISOR);
++              dev_dbg(priv->dev, "port%u: SR calibration applied: %x\n",
++                      port_id, srctrl);
++      }
++
++      regmap_update_bits(priv->regmap, acr0,
++                         EN7528_USB_PHY_HSTX_SRCTRL,
++                         FIELD_PREP(EN7528_USB_PHY_HSTX_SRCTRL, srctrl));
++}
++
++static int en7528_usb_phy_init(struct phy *phy)
++{
++      struct en7528_usb_phy_instance *instance = phy_get_drvdata(phy);
++      struct en7528_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
++      unsigned int i;
++
++      switch (instance->type) {
++      case PHY_TYPE_USB2:
++              /* Enable both U2 PHY ports before calibration */
++              for (i = 0; i < EN7528_USB_PHY_NUM_U2_PORTS; i++)
++                      regmap_write(priv->regmap,
++                                   en7528_u2_port_bases[i] + EN7528_USB_PHY_ACR3,
++                                   EN7528_USB_PHY_ACR3_ENABLE);
++              break;
++      case PHY_TYPE_USB3:
++              /* Combo PHY Rx R mean value too high, tune -5 Ohm */
++              regmap_update_bits(priv->regmap,
++                                 EN7528_USB_PHY_U3_PHYA_REG11,
++                                 EN7528_USB_PHY_RX_IMPSEL,
++                                 FIELD_PREP(EN7528_USB_PHY_RX_IMPSEL, 0x1));
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int en7528_usb_phy_power_on(struct phy *phy)
++{
++      struct en7528_usb_phy_instance *instance = phy_get_drvdata(phy);
++      struct en7528_usb_phy_priv *priv = dev_get_drvdata(phy->dev.parent);
++      unsigned int i;
++
++      if (instance->type != PHY_TYPE_USB2)
++              return 0;
++
++      /* Calibrate slew rate for both U2 PHY ports */
++      for (i = 0; i < EN7528_USB_PHY_NUM_U2_PORTS; i++)
++              en7528_usb_phy_slew_rate_calibration(priv, i,
++                                                   en7528_u2_port_bases[i]);
++
++      return 0;
++}
++
++static const struct phy_ops en7528_usb_phy_ops = {
++      .init           = en7528_usb_phy_init,
++      .power_on       = en7528_usb_phy_power_on,
++      .owner          = THIS_MODULE,
++};
++
++static struct phy *en7528_usb_phy_xlate(struct device *dev,
++                                      const struct of_phandle_args *args)
++{
++      struct en7528_usb_phy_priv *priv = dev_get_drvdata(dev);
++      struct en7528_usb_phy_instance *instance = NULL;
++      unsigned int index, phy_type;
++
++      if (args->args_count != 1) {
++              dev_err(dev, "invalid number of cells in 'phy' property\n");
++              return ERR_PTR(-EINVAL);
++      }
++
++      phy_type = args->args[0];
++      if (!(phy_type == PHY_TYPE_USB2 || phy_type == PHY_TYPE_USB3)) {
++              dev_err(dev, "unsupported device type: %d\n", phy_type);
++              return ERR_PTR(-EINVAL);
++      }
++
++      for (index = 0; index < EN7528_PHY_USB_MAX; index++)
++              if (priv->phys[index] &&
++                  phy_type == priv->phys[index]->type) {
++                      instance = priv->phys[index];
++                      break;
++              }
++
++      if (!instance) {
++              dev_err(dev, "failed to find appropriate phy\n");
++              return ERR_PTR(-EINVAL);
++      }
++
++      return instance->phy;
++}
++
++static const struct regmap_config en7528_usb_phy_regmap_config = {
++      .reg_bits = 32,
++      .val_bits = 32,
++      .reg_stride = 4,
++      .max_register = EN7528_USB_PHY_U2_P1_BASE + EN7528_USB_PHY_ACR3,
++};
++
++static int en7528_usb_phy_probe(struct platform_device *pdev)
++{
++      struct phy_provider *phy_provider;
++      struct en7528_usb_phy_priv *priv;
++      struct device *dev = &pdev->dev;
++      unsigned int index;
++      void *base;
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      priv->dev = dev;
++
++      base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(base))
++              return PTR_ERR(base);
++
++      priv->regmap = devm_regmap_init_mmio(dev, base,
++                                           &en7528_usb_phy_regmap_config);
++      if (IS_ERR(priv->regmap))
++              return PTR_ERR(priv->regmap);
++
++      platform_set_drvdata(pdev, priv);
++
++      for (index = 0; index < EN7528_PHY_USB_MAX; index++) {
++              struct en7528_usb_phy_instance *instance;
++              enum en7528_usb_phy_type phy_type;
++
++              switch (index) {
++              case EN7528_PHY_USB2:
++                      phy_type = PHY_TYPE_USB2;
++                      break;
++              case EN7528_PHY_USB3:
++                      phy_type = PHY_TYPE_USB3;
++                      break;
++              default:
++                      continue;
++              }
++
++              instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
++              if (!instance)
++                      return -ENOMEM;
++
++              instance->type = phy_type;
++              priv->phys[index] = instance;
++
++              instance->phy = devm_phy_create(dev, NULL, &en7528_usb_phy_ops);
++              if (IS_ERR(instance->phy))
++                      return dev_err_probe(dev, PTR_ERR(instance->phy),
++                                           "failed to create phy\n");
++
++              phy_set_drvdata(instance->phy, instance);
++      }
++
++      phy_provider = devm_of_phy_provider_register(dev,
++                                                   en7528_usb_phy_xlate);
++
++      return PTR_ERR_OR_ZERO(phy_provider);
++}
++
++static const struct of_device_id en7528_usb_phy_match[] = {
++      { .compatible = "econet,en7528-usb-phy" },
++      { },
++};
++MODULE_DEVICE_TABLE(of, en7528_usb_phy_match);
++
++static struct platform_driver en7528_usb_phy_driver = {
++      .probe          = en7528_usb_phy_probe,
++      .driver         = {
++              .name   = "en7528-usb-phy",
++              .of_match_table = en7528_usb_phy_match,
++      },
++};
++
++module_platform_driver(en7528_usb_phy_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("EcoNet EN7528 USB PHY driver");
diff --git a/target/linux/econet/patches-6.12/915-usb-xhci-mtk-add-en7528-ltssm-quirk.patch b/target/linux/econet/patches-6.12/915-usb-xhci-mtk-add-en7528-ltssm-quirk.patch
new file mode 100644 (file)
index 0000000..78e5ca5
--- /dev/null
@@ -0,0 +1,58 @@
+From: Ahmed Naseef <naseefkm@gmail.com>
+Subject: usb: xhci-mtk: add EN7528 LTSSM timing quirk
+
+EN7528 needs LTSSM Timing Parameter 5 configured to fix
+TD 6.5 compliance test failures.
+
+Based on GPL vendor code:
+https://github.com/keenetic/kernel-49/commit/ee0e0b7cf28c208cd5b892ea96180ffae0de9b7f
+
+Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
+--- a/drivers/usb/host/xhci-mtk.c
++++ b/drivers/usb/host/xhci-mtk.c
+@@ -80,6 +80,8 @@
+ #define SS_GEN2_EOF_CFG               0x990
+ #define SSG2EOF_OFFSET                0x3c
++#define XHCI_MTK_LTSSM_TIMING_PARAMETER5      0x251c
++
+ #define XSEOF_OFFSET_MASK     GENMASK(11, 0)
+ /* usb remote wakeup registers in syscon */
+@@ -191,6 +193,18 @@ static void xhci_mtk_rxfifo_depth_set(st
+       writel(value, hcd->regs + HSCH_CFG1);
+ }
++/* EN7528: fix TD 6.5 compliance test failure */
++static void xhci_mtk_ltssm_quirk(struct xhci_hcd_mtk *mtk)
++{
++      struct device *dev = mtk->dev;
++      struct usb_hcd *hcd = mtk->hcd;
++
++      if (!of_device_is_compatible(dev->of_node, "econet,en7528-xhci"))
++              return;
++
++      writel(0x203e8, hcd->regs + XHCI_MTK_LTSSM_TIMING_PARAMETER5);
++}
++
+ static void xhci_mtk_init_quirk(struct xhci_hcd_mtk *mtk)
+ {
+       /* workaround only for mt8195 */
+@@ -198,6 +212,9 @@ static void xhci_mtk_init_quirk(struct x
+       /* workaround for SoCs using SSUSB about before IPM v1.6.0 */
+       xhci_mtk_rxfifo_depth_set(mtk);
++
++      /* EN7528 LTSSM timing fix */
++      xhci_mtk_ltssm_quirk(mtk);
+ }
+ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
+@@ -846,6 +863,7 @@ static const struct dev_pm_ops xhci_mtk_
+ static const struct of_device_id mtk_xhci_of_match[] = {
+       { .compatible = "mediatek,mt8173-xhci"},
+       { .compatible = "mediatek,mt8195-xhci"},
++      { .compatible = "econet,en7528-xhci"},
+       { .compatible = "mediatek,mtk-xhci"},
+       { },
+ };