diff --git a/target/linux/econet/dts/en7528.dtsi b/target/linux/econet/dts/en7528.dtsi index 1ccc298d211..bbd7f9786d8 100644 --- a/target/linux/econet/dts/en7528.dtsi +++ b/target/linux/econet/dts/en7528.dtsi @@ -3,6 +3,7 @@ #include #include +#include #include / { @@ -131,6 +132,24 @@ }; }; + 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 = ; + 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>; diff --git a/target/linux/econet/dts/en7528_dasan_h660gm-a-generic.dts b/target/linux/econet/dts/en7528_dasan_h660gm-a-generic.dts index 9473f2b390d..1406e76c694 100644 --- a/target/linux/econet/dts/en7528_dasan_h660gm-a-generic.dts +++ b/target/linux/econet/dts/en7528_dasan_h660gm-a-generic.dts @@ -20,6 +20,8 @@ color = ; function = LED_FUNCTION_USB; gpios = <&gpio0 8 GPIO_ACTIVE_LOW>; + linux,default-trigger = "usbport"; + trigger-sources = <&usb>; }; led-lan1-green { diff --git a/target/linux/econet/dts/en7528_dasan_h660gm-a.dtsi b/target/linux/econet/dts/en7528_dasan_h660gm-a.dtsi index d77a915dcc9..bd190167025 100644 --- a/target/linux/econet/dts/en7528_dasan_h660gm-a.dtsi +++ b/target/linux/econet/dts/en7528_dasan_h660gm-a.dtsi @@ -77,14 +77,13 @@ }; }; - 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 { @@ -105,6 +104,11 @@ }; }; +&usb { + status = "okay"; + vbus-supply = <®_usb_vbus>; +}; + &pcie0 { status = "okay"; }; diff --git a/target/linux/econet/en7528/config-6.12 b/target/linux/econet/en7528/config-6.12 index 81f75829a17..f5778340282 100644 --- a/target/linux/econet/en7528/config-6.12 +++ b/target/linux/econet/en7528/config-6.12 @@ -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 diff --git a/target/linux/econet/image/en7528.mk b/target/linux/econet/image/en7528.mk index b6e132c18ce..9f68905af60 100644 --- a/target/linux/econet/image/en7528.mk +++ b/target/linux/econet/image/en7528.mk @@ -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 index 00000000000..308703c630a --- /dev/null +++ b/target/linux/econet/patches-6.12/914-phy-add-en7528-usb-phy-driver.patch @@ -0,0 +1,358 @@ +From: Ahmed Naseef +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 +--- 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 index 00000000000..78e5ca5d4cd --- /dev/null +++ b/target/linux/econet/patches-6.12/915-usb-xhci-mtk-add-en7528-ltssm-quirk.patch @@ -0,0 +1,58 @@ +From: Ahmed Naseef +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 +--- 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"}, + { }, + };