diff --git a/patch/kernel/sunxi-dev/0000-sun8i-rgmii-ethernet.patch b/patch/kernel/sunxi-dev/0000-sun8i-rgmii-ethernet.patch new file mode 100644 index 000000000..55e371a94 --- /dev/null +++ b/patch/kernel/sunxi-dev/0000-sun8i-rgmii-ethernet.patch @@ -0,0 +1,491 @@ +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +index f93f5d1..d8118d0 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +@@ -184,3 +184,12 @@ + usb1_vbus-supply = <®_usb1_vbus>; + status = "okay"; + }; ++ ++&emac { ++ phy = <&phy1>; ++ phy-mode = "mii"; ++ status = "okay"; ++ phy1: ethernet-phy@1 { ++ reg = <0>; ++ }; ++}; +\ No newline at end of file +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +index f01e10d..2249d40 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +@@ -102,20 +102,6 @@ + status = "okay"; + }; + +-&ephy { +- allwinner,ephy-addr = <0x1>; +-}; +- +-&emac { +- phy = <&phy1>; +- phy-mode = "mii"; +- status = "okay"; +- +- phy1: ethernet-phy@1 { +- reg = <1>; +- }; +-}; +- + &ir { + pinctrl-names = "default"; + pinctrl-0 = <&ir_pins_a>; +@@ -179,3 +165,12 @@ + /* USB VBUS is always on */ + status = "okay"; + }; ++ ++&emac { ++ phy = <&phy1>; ++ phy-mode = "mii"; ++ status = "okay"; ++ phy1: ethernet-phy@1 { ++ reg = <0>; ++ }; ++}; +\ No newline at end of file +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts +index b0cb417..16b9004 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts +@@ -47,6 +47,18 @@ + model = "Xunlong Orange Pi Plus"; + compatible = "xunlong,orangepi-plus", "allwinner,sun8i-h3"; + ++ reg_gmac_3v3: gmac-3v3 { ++ compatible = "regulator-fixed"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac_power_pin_orangepi>; ++ regulator-name = "gmac-3v3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ startup-delay-us = <100000>; ++ enable-active-high; ++ gpio = <&pio 3 6 GPIO_ACTIVE_HIGH>; ++ }; ++ + reg_usb3_vbus: usb3-vbus { + compatible = "regulator-fixed"; + pinctrl-names = "default"; +@@ -82,6 +94,13 @@ + }; + + &pio { ++ gmac_power_pin_orangepi: gmac_power_pin@0 { ++ allwinner,pins = "PD6"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ + usb3_vbus_pin_a: usb3_vbus_pin@0 { + allwinner,pins = "PG11"; + allwinner,function = "gpio_out"; +@@ -93,3 +112,15 @@ + &usbphy { + usb3_vbus-supply = <®_usb3_vbus>; + }; ++ ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emac_rgmii_pins>; ++ phy = <&phy1>; ++ phy-mode = "rgmii"; ++ phy-supply = <®_gmac_3v3>; ++ status = "okay"; ++ phy1: ethernet-phy@1 { ++ reg = <0>; ++ }; ++}; +\ No newline at end of file +diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi +index 7749af6..e7b6334 100644 +--- a/arch/arm/boot/dts/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/sun8i-h3.dtsi +@@ -323,15 +323,6 @@ + #size-cells = <1>; + ranges; + +- ephy: ethernet-phy@01c00030 { +- compatible = "allwinner,sun8i-h3-ephy"; +- reg = <0x01c00030 0x4>; +- clocks = <&bus_gates 128>; +- resets = <&ahb_rst 66>; +- #clock-cells = <0>; +- clock-output-names = "emac_tx"; +- }; +- + dma: dma-controller@01c02000 { + compatible = "allwinner,sun8i-h3-dma"; + reg = <0x01c02000 0x1000>; +@@ -510,6 +501,17 @@ + interrupt-controller; + #interrupt-cells = <3>; + ++ emac_rgmii_pins: emac0@0 { ++ allwinner,pins = "PD0", "PD1", "PD2", "PD3", ++ "PD4", "PD5", "PD7", ++ "PD8", "PD9", "PD10", ++ "PD12", "PD13", "PD15", ++ "PD16", "PD17"; ++ allwinner,function = "emac"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ + uart0_pins_a: uart0@0 { + allwinner,pins = "PA4", "PA5"; + allwinner,function = "uart0"; +@@ -637,14 +639,15 @@ + + emac: ethernet@1c30000 { + compatible = "allwinner,sun8i-h3-emac"; +- reg = <0x01c30000 0x1000>; ++ reg = <0x01c30000 0x104>; + interrupts = ; +- resets = <&ahb_rst 17>; +- reset-names = "ahb"; +- clocks = <&bus_gates 17>, <&ephy>; +- clock-names = "ahb", "tx"; ++ resets = <&ahb_rst 17>, <&ahb_rst 66>; ++ reset-names = "ahb", "ephy"; ++ clocks = <&bus_gates 17>, <&bus_gates 128>; ++ clock-names = "bus_gmac", "bus_ephy"; + #address-cells = <1>; + #size-cells = <0>; ++ status = "disabled"; + }; + + gic: interrupt-controller@01c81000 { +diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers/net/ethernet/allwinner/sun8i-emac.c +index 18c58e6..1c40109 100644 +--- a/drivers/net/ethernet/allwinner/sun8i-emac.c ++++ b/drivers/net/ethernet/allwinner/sun8i-emac.c +@@ -120,6 +120,7 @@ struct sun8i_emac_priv { + struct clk *ahb_clk; + struct clk *tx_clk; + u32 mdc; ++ struct regulator *regulator; + + struct reset_control *rst_phy; + struct reset_control *rst; +@@ -159,6 +160,8 @@ int rb_tx_numfreedesc(struct net_device *ndev) + return (nbdesc - priv->tx_slot) + priv->tx_dirty; + } + ++static int sun8i_ephy_hack(struct net_device *ndev); ++ + /* allocate a skb in a DMA descriptor + * + * @i index of slot to fill +@@ -203,6 +206,10 @@ static void sun8i_emac_set_macaddr(struct sun8i_emac_priv *priv, + { + u32 v; + ++ if (!is_valid_ether_addr(addr)) { ++ random_ether_addr(priv->ndev->dev_addr); ++ addr = priv->ndev->dev_addr; ++ } + dev_info(priv->dev, "device MAC address slot %d %02x:%02x:%02x:%02x:%02x:%02x\n", + index, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + +@@ -597,6 +604,8 @@ static void sun8i_emac_adjust_link(struct net_device *ndev) + unsigned long flags; + int new_state = 0; + ++ dev_dbg(priv->dev, "%s link=%x duplex=%x speed=%x\n", __func__, ++ phydev->link, phydev->duplex, phydev->speed); + if (!phydev) + return; + +@@ -621,6 +630,8 @@ static void sun8i_emac_adjust_link(struct net_device *ndev) + priv->link = phydev->link; + } + ++ dev_dbg(priv->dev, "%s new=%d link=%d pause=%d\n", ++ __func__, new_state, priv->link, phydev->pause); + if (new_state) + sun8i_emac_set_link_mode(priv); + } else if (priv->link != phydev->link) { +@@ -636,9 +647,6 @@ static void sun8i_emac_adjust_link(struct net_device *ndev) + spin_unlock_irqrestore(&priv->lock, flags); + } + +-#define SUN7I_GMAC_GMII_RGMII_RATE 125000000 +-#define SUN7I_GMAC_MII_RATE 25000000 +- + static int sun8i_emac_init(struct net_device *ndev) + { + struct sun8i_emac_priv *priv = netdev_priv(ndev); +@@ -673,6 +681,20 @@ static int sun8i_emac_init(struct net_device *ndev) + return ret; + } + ++ priv->regulator = devm_regulator_get_optional(priv->dev, "phy"); ++ if (IS_ERR(priv->regulator)) { ++ if (PTR_ERR(priv->regulator) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_info(priv->dev, "no regulator found\n"); ++ priv->regulator = NULL; ++ } ++ ++ if (priv->regulator) { ++ ret = regulator_enable(priv->regulator); ++ if (ret) ++ return ret; ++ } ++ + if (priv->rst) { + ret = reset_control_deassert(priv->rst); + if (ret) { +@@ -693,26 +715,14 @@ static int sun8i_emac_init(struct net_device *ndev) + dev_dbg(priv->dev, "MDC auto : %x\n", reg); + writel(reg, priv->base + SUN8I_EMAC_MDIO_CMD); + +- /* The GMAC TX clock lines are configured by setting the clock +- * rate, which then uses the auto-reparenting feature of the +- * clock driver, and enabling/disabling the clock. +- */ +- if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII) { +- clk_set_rate(priv->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE); +- clk_prepare_enable(priv->tx_clk); +- } else { +- clk_set_rate(priv->tx_clk, SUN7I_GMAC_MII_RATE); +- } ++ sun8i_ephy_hack(ndev); + + ret = sun8i_emac_mdio_register(ndev); + if (ret) +- goto err_disable_tx_clk; ++ goto err_assert_reset; + + return 0; +- +-err_disable_tx_clk: +- if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII) +- clk_disable_unprepare(priv->tx_clk); ++err_assert_reset: + if (priv->rst) + reset_control_assert(priv->rst); + err_disable_ahb_clk: +@@ -725,14 +735,157 @@ static void sun8i_emac_uninit(struct net_device *ndev) + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + mdiobus_unregister(priv->mdio); +- +- if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII) +- clk_disable_unprepare(priv->tx_clk); +- + if (priv->rst) + reset_control_assert(priv->rst); + + clk_disable_unprepare(priv->ahb_clk); ++ ++ if (priv->regulator) ++ regulator_disable(priv->regulator); ++} ++ ++/* this function do lots of things that will be splited away (clk/phy) */ ++static int sun8i_ephy_hack(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int err; ++ void __iomem *sc; ++ u32 v; ++ int do_ephy_clk = 1; ++ ++ dev_info(priv->dev, "%s\n", __func__); ++ ++ /* find type of PHY */ ++ priv->phy_interface = of_get_phy_mode(priv->dev->of_node); ++ dev_info(priv->dev, "%s phy_interface=%x\n", __func__, ++ priv->phy_interface); ++ ++ /* fallback to integrate MII */ ++ switch (priv->phy_interface) { ++ case PHY_INTERFACE_MODE_MII: ++ dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_MII\n", ++ __func__); ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_RGMII\n", ++ __func__); ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_RMII\n", ++ __func__); ++ break; ++ case PHY_INTERFACE_MODE_GMII: ++ dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_GMII\n", ++ __func__); ++ break; ++ default: ++ dev_info(priv->dev, "Fallback to MII\n"); ++ priv->phy_interface = PHY_INTERFACE_MODE_MII; ++ } ++ ++ /* systemcontrol */ ++ /* TODO put that in phy clock */ ++ sc = ioremap(0x01C00030, 0x20); ++ if (sc) { ++ v = readl(sc); ++ dev_info(priv->dev, "SystemControl %x\n", v); ++ /* crappy switch to be moved */ ++ switch (v) { ++ case 0: /* A83T */ ++ do_ephy_clk = 0; ++ switch (priv->phy_interface) { ++ case PHY_INTERFACE_MODE_MII: ++ v &= ~BIT(2); ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ v |= BIT(1); ++ v |= BIT(2); ++ v |= BIT(15); ++ break; ++ case PHY_INTERFACE_MODE_GMII: ++ v &= ~BIT(2); ++ break; ++ default: ++ dev_err(priv->dev, "Unknown PHY type %d\n", ++ priv->phy_interface); ++ } ++ break; ++ case 0x58000: /* H3 */ ++ switch (priv->phy_interface) { ++ case PHY_INTERFACE_MODE_MII: ++ /* PHY_SELECT: Internal PHY */ ++ v |= BIT(15); ++ /* SHUTDOWN: Power up */ ++ v &= ~BIT(16); ++ /* 24 Mhz */ ++ /*v &= ~BIT(18);*/ ++ /* LED POL */ ++ v |= BIT(17); ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ v |= BIT(1); ++ v |= BIT(2); ++ /* External PHY */ ++ v &= ~BIT(15); ++ /* SHUTDOWN: Shutdown */ ++ v |= BIT(16); ++ break; ++ /* TODO RMII */ ++ default: ++ dev_err(priv->dev, "Unknown PHY type %d\n", ++ priv->phy_interface); ++ } ++ break; ++ default: ++ dev_err(priv->dev, "Unknown platform %x\n", v); ++ } ++ dev_info(priv->dev, "SystemControl %x\n", v); ++ writel(v, sc); ++ iounmap(sc); ++ } ++ /* end phy clock */ ++ ++ /* PWM */ ++ sc = ioremap(0x01C21400, 0x20); ++ if (sc) { ++ v = readl(sc); ++ /*dev_info(priv->dev, "PWM %x\n", v);*/ ++ v = readl(sc + 0x04); ++ /*dev_info(priv->dev, "PWM %x\n", v);*/ ++ iounmap(sc); ++ } ++ ++ if (do_ephy_clk == 1) { ++ priv->tx_clk = devm_clk_get(priv->dev, "bus_ephy"); ++ if (IS_ERR(priv->tx_clk)) { ++ err = PTR_ERR(priv->tx_clk); ++ dev_err(priv->dev, "Cannot get MII clock err=%d\n", ++ err); ++ return err; ++ } ++ err = clk_prepare_enable(priv->tx_clk); ++ if (err != 0) { ++ dev_err(priv->dev, "Cannot prepare_enable PHY\n"); ++ return err; ++ } ++ dev_info(priv->dev, "PHY clk is enabled\n"); ++ ++ priv->rst_phy = devm_reset_control_get(priv->dev, "ephy"); ++ if (IS_ERR(priv->rst_phy)) { ++ err = PTR_ERR(priv->rst_phy); ++ dev_info(priv->dev, "no PHY reset control found %d\n", ++ err); ++ priv->rst_phy = NULL; ++ } ++ if (priv->rst_phy) { ++ err = reset_control_deassert(priv->rst_phy); ++ if (err) ++ dev_err(priv->dev, "Cannot deassert PHY\n"); ++ else ++ dev_info(priv->dev, "PHY is de-asserted\n"); ++ } ++ } ++ return 0; + } + + static int sun8i_emac_mdio_probe(struct net_device *ndev) +@@ -1453,20 +1606,13 @@ static int sun8i_emac_probe(struct platform_device *pdev) + goto probe_err; + } + +- priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb"); ++ priv->ahb_clk = devm_clk_get(&pdev->dev, "bus_gmac"); + if (IS_ERR(priv->ahb_clk)) { + ret = PTR_ERR(priv->ahb_clk); + dev_err(&pdev->dev, "Cannot get AHB clock err=%d\n", ret); + goto probe_err; + } + +- priv->tx_clk = devm_clk_get(&pdev->dev, "tx"); +- if (IS_ERR(priv->tx_clk)) { +- ret = PTR_ERR(priv->tx_clk); +- dev_err(&pdev->dev, "Cannot get TX clock err=%d\n", ret); +- goto probe_err; +- } +- + priv->rst = devm_reset_control_get_optional(&pdev->dev, "ahb"); + if (IS_ERR(priv->rst)) { + ret = PTR_ERR(priv->rst); +@@ -1505,6 +1651,8 @@ static int sun8i_emac_probe(struct platform_device *pdev) + goto probe_err; + } + ++ sun8i_emac_set_macaddr(priv, ndev->dev_addr, 0); ++ + return 0; + + probe_err: +@@ -1515,11 +1663,15 @@ probe_err: + static int sun8i_emac_remove(struct platform_device *pdev) + { + struct net_device *ndev = platform_get_drvdata(pdev); ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ clk_disable_unprepare(priv->tx_clk); ++ if (priv->rst_phy) ++ reset_control_assert(priv->rst_phy); + + unregister_netdev(ndev); + platform_set_drvdata(pdev, NULL); + free_netdev(ndev); +- + return 0; + } + \ No newline at end of file