From 29d1b393044c0514a056f01cfea7ab1044521366 Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Thu, 2 Jul 2026 23:26:03 +0000 Subject: [PATCH 1/3] net: stmmac: dwmac-sun55i: Add support for A733 GMAC variant A733 also ships a dwmac 4 GMAC controller, but its glue registers differ: they are in a separate memory region instead of the syscon, and provide wider fields for RGMII TX delay fine tunning. We also have a discrete ahb reset, and we must delay glue register setup until clock/reset of the GMAC is ready. Note that we now use the externally-supplied RGMII clock as the GMAC transmitting clock, since the internal one somehow doesn't work. This doesn't match vendor driver behavior and requires investigation. Signed-off-by: Yao Zi --- .../ethernet/stmicro/stmmac/dwmac-sun55i.c | 155 +++++++++++++++++- 1 file changed, 146 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun55i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun55i.c index 862df173d963f7..7ed04f1ec4c175 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun55i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun55i.c @@ -28,6 +28,7 @@ /* RMII specific bits */ #define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */ /* Generic system control EMAC_CLK bits */ +#define SYSCON_ETXDC_H_MASK GENMASK(17, 16) #define SYSCON_ETXDC_MASK GENMASK(12, 10) #define SYSCON_ERXDC_MASK GENMASK(9, 5) /* EMAC PHY Interface Type */ @@ -37,8 +38,20 @@ #define SYSCON_ETCS_EXT_GMII 0x1 #define SYSCON_ETCS_INT_GMII 0x2 +#define SYSCON_ETXDC_FULL_MASK GENMASK(4, 0) + +struct sun55i_priv_data { + void __iomem *glue; + u32 glue_setting; +}; + +typedef int (*gmac_plat_init)(struct device *dev, + struct plat_stmmacenet_data *plat, + struct stmmac_resources *res); + static int sun55i_gmac200_set_syscon(struct device *dev, - struct plat_stmmacenet_data *plat) + struct plat_stmmacenet_data *plat, + struct stmmac_resources *res) { struct device_node *node = dev->of_node; struct regmap *regmap; @@ -55,10 +68,10 @@ static int sun55i_gmac200_set_syscon(struct device *dev, "tx-delay must be a multiple of 100ps\n"); val /= 100; dev_dbg(dev, "set tx-delay to %x\n", val); - if (!FIELD_FIT(SYSCON_ETXDC_MASK, val)) + if (!FIELD_FIT(SYSCON_ETXDC_FULL_MASK, val)) return dev_err_probe(dev, -EINVAL, "TX clock delay exceeds maximum (%u00ps > %lu00ps)\n", - val, FIELD_MAX(SYSCON_ETXDC_MASK)); + val, FIELD_MAX(SYSCON_ETXDC_FULL_MASK)); reg |= FIELD_PREP(SYSCON_ETXDC_MASK, val); } @@ -102,14 +115,125 @@ static int sun55i_gmac200_set_syscon(struct device *dev, return 0; } +static struct mac_device_info *sun60i_dwmac_setup(void *opriv) +{ + struct stmmac_priv *stmmac_priv = opriv; + struct sun55i_priv_data *priv = stmmac_priv->plat->bsp_priv; + + stmmac_priv->hw = devm_kzalloc(stmmac_priv->device, + sizeof(*stmmac_priv->hw), GFP_KERNEL); + if (!stmmac_priv->hw) + return NULL; + + writel(priv->glue_setting, priv->glue); + udelay(100); + + dwmac4_setup(stmmac_priv); + + return stmmac_priv->hw; +} + +static int sun60i_gmac210_plat_init(struct device *dev, + struct plat_stmmacenet_data *plat, + struct stmmac_resources *res) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sun55i_priv_data *priv = plat->bsp_priv; + struct device_node *node = dev->of_node; + u32 val, reg = 0; + + priv->glue = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(priv->glue)) + return dev_err_probe(dev, PTR_ERR(priv->glue), + "Unable to map glue region\n"); + + if (!of_property_read_u32(node, "tx-internal-delay-ps", &val)) { + if (val % 100) + return dev_err_probe(dev, -EINVAL, + "tx-delay must be a multiple of 100ps\n"); + val /= 100; + dev_dbg(dev, "set tx-delay to %x\n", val); + if (!FIELD_FIT(SYSCON_ETXDC_FULL_MASK, val)) + return dev_err_probe(dev, -EINVAL, + "TX clock delay exceeds maximum (%u00ps > %lu00ps)\n", + val, FIELD_MAX(SYSCON_ETXDC_MASK)); + + reg |= FIELD_PREP(SYSCON_ETXDC_MASK, + FIELD_GET(GENMASK(2, 0), val)); + reg |= FIELD_PREP(SYSCON_ETXDC_H_MASK, + FIELD_GET(GENMASK(4, 3), val)); + } + + if (!of_property_read_u32(node, "rx-internal-delay-ps", &val)) { + if (val % 100) + return dev_err_probe(dev, -EINVAL, + "rx-delay must be a multiple of 100ps\n"); + val /= 100; + dev_dbg(dev, "set rx-delay to %x\n", val); + if (!FIELD_FIT(SYSCON_ERXDC_MASK, val)) + return dev_err_probe(dev, -EINVAL, + "RX clock delay exceeds maximum (%u00ps > %lu00ps)\n", + val, FIELD_MAX(SYSCON_ERXDC_MASK)); + + reg |= FIELD_PREP(SYSCON_ERXDC_MASK, val); + } + + switch (plat->phy_interface) { + case PHY_INTERFACE_MODE_MII: + /* default */ + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + /* + * It seems the "internal GMAC transmit clock" isn't correctly + * supplied, and we must rely on the external RGMII clock + * supplied through RGMII0-CLKIN. Why? + */ + reg |= SYSCON_EPIT | SYSCON_ETCS_EXT_GMII; + break; + case PHY_INTERFACE_MODE_RMII: + reg |= SYSCON_RMII_EN; + break; + default: + return dev_err_probe(dev, -EINVAL, "Unsupported interface mode: %s", + phy_modes(plat->phy_interface)); + } + + priv->glue_setting = reg; + + res->tx_irq[0] = platform_get_irq_byname(pdev, "tx0_irq"); + if (res->tx_irq[0] < 0) + return dev_err_probe(dev, res->tx_irq[0], + "Unable to request tx0_irq\n"); + + res->rx_irq[0] = platform_get_irq_byname(pdev, "rx0_irq"); + if (res->rx_irq[0] < 0) + return dev_err_probe(dev, res->rx_irq[0], + "Unable to request rx0_irq\n"); + + plat->flags |= STMMAC_FLAG_MULTI_MSI_EN; + plat->setup = sun60i_dwmac_setup; + + return 0; +} + + static int sun55i_gmac200_probe(struct platform_device *pdev) { struct plat_stmmacenet_data *plat_dat; struct stmmac_resources stmmac_res; struct device *dev = &pdev->dev; + struct sun55i_priv_data *priv; + gmac_plat_init init; struct clk *clk; int ret; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) return ret; @@ -118,29 +242,42 @@ static int sun55i_gmac200_probe(struct platform_device *pdev) if (IS_ERR(plat_dat)) return PTR_ERR(plat_dat); + plat_dat->bsp_priv = priv; + /* BSP disables it */ plat_dat->flags |= STMMAC_FLAG_SPH_DISABLE; plat_dat->host_dma_width = 32; - ret = sun55i_gmac200_set_syscon(dev, plat_dat); + clk = devm_clk_get_optional_enabled(dev, "mbus"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), + "Failed to get or enable mbus clock\n"); + + init = of_device_get_match_data(dev); + ret = init(dev, plat_dat, &stmmac_res); if (ret) return ret; - clk = devm_clk_get_enabled(dev, "mbus"); + clk = devm_clk_get_optional_enabled(dev, "phy"); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), - "Failed to get or enable MBUS clock\n"); + "Failed to get or enable phy clock\n"); ret = devm_regulator_get_enable_optional(dev, "phy"); - if (ret) + if (ret && ret != -ENODEV) return dev_err_probe(dev, ret, "Failed to get or enable PHY supply\n"); return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); } static const struct of_device_id sun55i_gmac200_match[] = { - { .compatible = "allwinner,sun55i-a523-gmac200" }, - { } + { + .compatible = "allwinner,sun55i-a523-gmac200", + .data = sun55i_gmac200_set_syscon, + }, { + .compatible = "allwinner,sun60i-a733-gmac210", + .data = sun60i_gmac210_plat_init, + }, { } }; MODULE_DEVICE_TABLE(of, sun55i_gmac200_match); From 062611750a864b794fadbbef3cdccc6e65dc2c6d Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Thu, 25 Jun 2026 23:36:55 +0000 Subject: [PATCH 2/3] arm64: dts: allwinner: Describe GMAC controller for A733 Signed-off-by: Yao Zi --- .../arm64/boot/dts/allwinner/sun60i-a733.dtsi | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun60i-a733.dtsi b/arch/arm64/boot/dts/allwinner/sun60i-a733.dtsi index db0d113426f051..78029a5fc83a25 100644 --- a/arch/arm64/boot/dts/allwinner/sun60i-a733.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun60i-a733.dtsi @@ -208,6 +208,16 @@ interrupt-controller; #interrupt-cells = <3>; + gmac_pins: gmac-pins { + pins = "PH0", "PH1", "PH2", "PH3", "PH4", "PH5", + "PH6", "PH7", "PH8", "PH9", "PH10", + "PH11", "PH12", "PH13", "PH14", "PH15"; + function = "rgmii0"; + allwinner,pinmux = <5>; + drive-strength = <20>; + bias-pull-up; + }; + mmc0_pins: mmc0-pins { pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; function = "sdc0"; @@ -319,6 +329,56 @@ #size-cells = <0>; }; + gmac: gmac@4500000 { + compatible = "allwinner,sun60i-a733-gmac210", + "snps,dwmac-5.20"; + reg = <0x0 0x4500000 0x0 0x8000>, + <0x0 0x4508000 0x0 0x8000>; + clocks = <&ccu CLK_GMAC0>, <&ccu CLK_GMAC0_MBUS>, <&ccu CLK_GMAC0_PHY>; + clock-names = "stmmaceth", "mbus", "phy"; + resets = <&ccu RST_BUS_GMAC0_AXI>, <&ccu RST_BUS_GMAC0>; + reset-names = "stmmaceth", "ahb"; + interrupts = , + , + ; + interrupt-names = "macirq", + "tx0_irq", + "rx0_irq"; + pinctrl-names = "default"; + pinctrl-0 = <&gmac_pins>; + status = "disabled"; + + snps,fixed-burst; + snps,ent-tx-lpi-clockgating; + snps,axi-config = <&gmac_stmmac_axi_setup>; + snps,mtl-rx-config = <&gmac_mtl_rx_setup>; + snps,mtl-tx-config = <&gmac_mtl_tx_setup>; + + mdio: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + gmac_stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <0xf>; + snps,rd_osr_lmt = <0xf>; + snps,blen = <256 128 64 32 16 8 4>; + }; + + gmac_mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <1>; + queue0 {}; + }; + + gmac_mtl_tx_setup: tx_queues-config { + snps,tx-queues-to-use = <1>; + queue0 {}; + }; + }; + r_ccu: r_ccu@7010000 { compatible = "allwinner,sun60iw2-r-ccu"; reg = <0x0 0x07010000 0x0 0x340>; From ceea69ba4811a77b10f6bdc32ffee53826a7427c Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Thu, 2 Jul 2026 23:17:10 +0000 Subject: [PATCH 3/3] arm64: dts: allwinner: Enable GbE port on Demo AIOT The expected GMAC delay differs a lot from the value provided by the vendor. It might be caused by we're working with another GMAC TX clock, i.e., the one supplied through SoC's RGMII0-CLKIN pin. Signed-off-by: Yao Zi --- .../dts/allwinner/sun60i-a733-demo-aiot.dts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun60i-a733-demo-aiot.dts b/arch/arm64/boot/dts/allwinner/sun60i-a733-demo-aiot.dts index 818535d50e95a6..b315d807238634 100644 --- a/arch/arm64/boot/dts/allwinner/sun60i-a733-demo-aiot.dts +++ b/arch/arm64/boot/dts/allwinner/sun60i-a733-demo-aiot.dts @@ -27,6 +27,32 @@ }; }; +&gmac { + assigned-clocks = <&ccu CLK_GMAC0_PHY>; + assigned-clock-rates = <25000000>; + tx-internal-delay-ps = <1000>; + rx-internal-delay-ps = <2000>; + phy-mode = "rgmii"; + phy-handle = <&rgmii_phy>; + status = "okay"; +}; + +&mdio { + status = "okay"; + + rgmii_phy: phy@1 { + compatible = "ethernet-phy-id001c.c916", + "ethernet-phy-ieee802.3-c22"; + reg = <1>; + assigned-clocks = <&ccu CLK_GMAC0_PHY>; + assigned-clock-rates = <25000000>; + clocks = <&ccu CLK_GMAC0_PHY>; + reset-gpios = <&pio 7 16 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <30000>; + }; +}; + &mmc0 { /* Missing v(q)mmc-supply which is SWOUT2/CLDO3 of AXP318 */ bus-width = <4>;