diff options
author | Peter Robinson <pbrobinson@gmail.com> | 2017-03-20 09:46:22 +0000 |
---|---|---|
committer | Peter Robinson <pbrobinson@gmail.com> | 2017-03-20 09:46:22 +0000 |
commit | 58a0e11959b0b409914ee77f28ac61f8ef318728 (patch) | |
tree | 21d0500bdab823bbdce0d3445a12a356f4d4d6ef /AllWinner-net-emac.patch | |
parent | 0b1166dd26a3010024eebd72c12943c5f55b5044 (diff) | |
download | kernel-58a0e11959b0b409914ee77f28ac61f8ef318728.tar.gz kernel-58a0e11959b0b409914ee77f28ac61f8ef318728.tar.xz kernel-58a0e11959b0b409914ee77f28ac61f8ef318728.zip |
Rebase AllWinner sun8i emac driver to latest proposed upstream
Diffstat (limited to 'AllWinner-net-emac.patch')
-rw-r--r-- | AllWinner-net-emac.patch | 3930 |
1 files changed, 1720 insertions, 2210 deletions
diff --git a/AllWinner-net-emac.patch b/AllWinner-net-emac.patch index 42dadabab..ebe9a3c94 100644 --- a/AllWinner-net-emac.patch +++ b/AllWinner-net-emac.patch @@ -1,186 +1,396 @@ -From fb909e29d6c073f4c5777a0db75df72b726e4314 Mon Sep 17 00:00:00 2001 +From patchwork Tue Mar 14 14:18:37 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2, 01/20] net-next: stmmac: export + stmmac_set_mac_addr/stmmac_get_mac_addr From: Corentin LABBE <clabbe.montjoie@gmail.com> -Date: Fri, 7 Oct 2016 10:25:48 +0200 -Subject: [PATCH 1/8] ethernet: add sun8i-emac driver +X-Patchwork-Id: 9623505 +Message-Id: <20170314141856.24560-2-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:37 +0100 -This patch add support for sun8i-emac ethernet MAC hardware. -It could be found in Allwinner H3/A83T/A64 SoCs. +Thoses symbol will be needed for the dwmac-sun8i ethernet driver. +For letting it to be build as module, they need to be exported. -It supports 10/100/1000 Mbit/s speed with half/full duplex. -It can use an internal PHY (MII 10/100) or an external PHY -via RGMII/RMII. +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +index e60bfca..0ab985c8 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +@@ -248,6 +248,7 @@ void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6], + data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + writel(data, ioaddr + low); + } ++EXPORT_SYMBOL_GPL(stmmac_set_mac_addr); + + /* Enable disable MAC RX/TX */ + void stmmac_set_mac(void __iomem *ioaddr, bool enable) +@@ -279,4 +280,4 @@ void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr, + addr[4] = hi_addr & 0xff; + addr[5] = (hi_addr >> 8) & 0xff; + } +- ++EXPORT_SYMBOL_GPL(stmmac_get_mac_addr); +From patchwork Tue Mar 14 14:18:38 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,02/20] net-next: stmmac: add optional setup function +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623509 +Message-Id: <20170314141856.24560-3-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:38 +0100 + +Instead of ading more ifthen logic for adding a new mac_device_info +setup function, it is easier to add a function pointer to the function +needed. Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> --- - drivers/net/ethernet/allwinner/Kconfig | 13 + - drivers/net/ethernet/allwinner/Makefile | 1 + - drivers/net/ethernet/allwinner/sun8i-emac.c | 2266 +++++++++++++++++++++++++++ - 3 files changed, 2280 insertions(+) - create mode 100644 drivers/net/ethernet/allwinner/sun8i-emac.c + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +++- + include/linux/stmmac.h | 3 +++ + 2 files changed, 6 insertions(+), 1 deletion(-) -diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig -index 47da7e7..060569c 100644 ---- a/drivers/net/ethernet/allwinner/Kconfig -+++ b/drivers/net/ethernet/allwinner/Kconfig -@@ -33,4 +33,17 @@ config SUN4I_EMAC - To compile this driver as a module, choose M here. The module - will be called sun4i-emac. +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 4498a38..856ac57 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -3101,7 +3101,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv) + struct mac_device_info *mac; -+config SUN8I_EMAC -+ tristate "Allwinner sun8i EMAC support" -+ depends on ARCH_SUNXI || COMPILE_TEST -+ depends on OF -+ select MII -+ select PHYLIB -+ ---help--- -+ This driver support the sun8i EMAC ethernet driver present on -+ H3/A83T/A64 Allwinner SoCs. -+ -+ To compile this driver as a module, choose M here. The module -+ will be called sun8i-emac. -+ - endif # NET_VENDOR_ALLWINNER -diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile -index 03129f7..8bd1693c 100644 ---- a/drivers/net/ethernet/allwinner/Makefile -+++ b/drivers/net/ethernet/allwinner/Makefile -@@ -3,3 +3,4 @@ - # + /* Identify the MAC HW device */ +- if (priv->plat->has_gmac) { ++ if (priv->plat->setup) { ++ mac = priv->plat->setup(priv); ++ } else if (priv->plat->has_gmac) { + priv->dev->priv_flags |= IFF_UNICAST_FLT; + mac = dwmac1000_setup(priv->ioaddr, + priv->plat->multicast_filter_bins, +diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h +index fc273e9..8f09f18 100644 +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -109,6 +109,8 @@ struct stmmac_axi { + bool axi_rb; + }; - obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o -+obj-$(CONFIG_SUN8I_EMAC) += sun8i-emac.o -diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers/net/ethernet/allwinner/sun8i-emac.c ++struct stmmac_priv; ++ + struct plat_stmmacenet_data { + int bus_id; + int phy_addr; +@@ -136,6 +138,7 @@ struct plat_stmmacenet_data { + void (*fix_mac_speed)(void *priv, unsigned int speed); + int (*init)(struct platform_device *pdev, void *priv); + void (*exit)(struct platform_device *pdev, void *priv); ++ struct mac_device_info *(*setup)(struct stmmac_priv *priv); + void *bsp_priv; + struct clk *stmmac_clk; + struct clk *pclk; +From patchwork Tue Mar 14 14:18:39 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2, + 03/20] ARM: sun8i: dt: Add DT bindings documentation for Allwinner + dwmac-sun8i +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623517 +Message-Id: <20170314141856.24560-4-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:39 +0100 + +This patch adds documentation for Device-Tree bindings for the +Allwinner dwmac-sun8i driver. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + .../devicetree/bindings/net/dwmac-sun8i.txt | 77 ++++++++++++++++++++++ + 1 file changed, 77 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/dwmac-sun8i.txt + +diff --git a/Documentation/devicetree/bindings/net/dwmac-sun8i.txt b/Documentation/devicetree/bindings/net/dwmac-sun8i.txt new file mode 100644 -index 0000000..bc74467 +index 0000000..f01ef17 --- /dev/null -+++ b/drivers/net/ethernet/allwinner/sun8i-emac.c -@@ -0,0 +1,2266 @@ ++++ b/Documentation/devicetree/bindings/net/dwmac-sun8i.txt +@@ -0,0 +1,77 @@ ++* Allwinner sun8i GMAC ethernet controller ++ ++This device is a platform glue layer for stmmac. ++Please see stmmac.txt for the other unchanged properties. ++ ++Required properties: ++- compatible: should be one of the following string: ++ "allwinner,sun8i-a83t-emac" ++ "allwinner,sun8i-h3-emac" ++ "allwinner,sun50i-a64-emac" ++- reg: address and length of the register for the device. ++- interrupts: interrupt for the device ++- interrupt-names: should be "macirq" ++- clocks: A phandle to the reference clock for this device ++- clock-names: should be "stmmaceth" ++- resets: A phandle to the reset control for this device ++- reset-names: should be "stmmaceth" ++- phy-mode: See ethernet.txt ++- phy-handle: See ethernet.txt ++- #address-cells: shall be 1 ++- #size-cells: shall be 0 ++- syscon: A phandle to the syscon of the SoC with one of the following ++ compatible string: ++ - allwinner,sun8i-h3-system-controller ++ - allwinner,sun8i-a64-system-controller ++ - allwinner,sun8i-a83t-system-controller ++ ++Optional properties: ++- allwinner,tx-delay: TX clock delay chain value. Range value is 0-0x07. Default is 0) ++- allwinner,rx-delay: RX clock delay chain value. Range value is 0-0x1F. Default is 0) ++Both delay properties are in 0.1ns step. ++ ++Optional properties for "allwinner,sun8i-h3-emac": ++- allwinner,leds-active-low: EPHY LEDs are active low ++ ++Required child node of emac: ++- mdio bus node: should be named mdio ++ ++Required properties of the mdio node: ++- #address-cells: shall be 1 ++- #size-cells: shall be 0 ++ ++The device node referenced by "phy" or "phy-handle" should be a child node ++of the mdio node. See phy.txt for the generic PHY bindings. ++ ++Required properties of the phy node with "allwinner,sun8i-h3-emac": ++- clocks: a phandle to the reference clock for the EPHY ++- resets: a phandle to the reset control for the EPHY ++ ++Example: ++ ++emac: ethernet@1c0b000 { ++ compatible = "allwinner,sun8i-h3-emac"; ++ syscon = <&syscon>; ++ reg = <0x01c0b000 0x104>; ++ interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "macirq"; ++ resets = <&ccu RST_BUS_EMAC>; ++ reset-names = "stmmaceth"; ++ clocks = <&ccu CLK_BUS_EMAC>; ++ clock-names = "stmmaceth"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ phy = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ mdio: mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ int_mii_phy: ethernet-phy@1 { ++ reg = <1>; ++ clocks = <&ccu CLK_BUS_EPHY>; ++ resets = <&ccu RST_BUS_EPHY>; ++ }; ++ }; ++}; +From patchwork Tue Mar 14 14:18:40 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2, + 04/20] ARM: sun8i: dt: Add DT bindings documentation for Allwinner + syscon +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623533 +Message-Id: <20170314141856.24560-5-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:40 +0100 + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + .../devicetree/bindings/misc/allwinner,syscon.txt | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + create mode 100644 Documentation/devicetree/bindings/misc/allwinner,syscon.txt + +diff --git a/Documentation/devicetree/bindings/misc/allwinner,syscon.txt b/Documentation/devicetree/bindings/misc/allwinner,syscon.txt +new file mode 100644 +index 0000000..9f5f1f5 +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/allwinner,syscon.txt +@@ -0,0 +1,19 @@ ++* Allwinner sun8i system controller ++ ++This file describes the bindings for the system controller present in ++Allwinner SoC H3, A83T and A64. ++The principal function of this syscon is to control EMAC PHY choice and ++config. ++ ++Required properties for the system controller: ++- reg: address and length of the register for the device. ++- compatible: should be "syscon" and one of the following string: ++ "allwinner,sun8i-h3-system-controller" ++ "allwinner,sun8i-a64-system-controller" ++ "allwinner,sun8i-a83t-system-controller" ++ ++Example: ++syscon: syscon@01c00000 { ++ compatible = "syscon", "allwinner,sun8i-h3-system-controller"; ++ reg = <0x01c00000 0x1000>; ++}; +From patchwork Tue Mar 14 14:18:41 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,05/20] net-next: stmmac: Add dwmac-sun8i +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623523 +Message-Id: <20170314141856.24560-6-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:41 +0100 + +The dwmac-sun8i is a heavy hacked version of stmmac hardware by +allwinner. +In fact the only common part is the descriptor management and the first +register function. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + drivers/net/ethernet/stmicro/stmmac/Kconfig | 11 + + drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + + drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 938 +++++++++++++++++++++ + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 27 +- + .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 9 +- + include/linux/stmmac.h | 1 + + 6 files changed, 984 insertions(+), 3 deletions(-) + create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c + +diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig +index cfbe363..85c0e41 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig ++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig +@@ -145,6 +145,17 @@ config DWMAC_SUNXI + This selects Allwinner SoC glue layer support for the + stmmac device driver. This driver is used for A20/A31 + GMAC ethernet controller. ++ ++config DWMAC_SUN8I ++ tristate "Allwinner sun8i GMAC support" ++ default ARCH_SUNXI ++ depends on OF && (ARCH_SUNXI || COMPILE_TEST) ++ ---help--- ++ Support for Allwinner H3 A83T A64 EMAC ethernet controllers. ++ ++ This selects Allwinner SoC glue layer support for the ++ stmmac device driver. This driver is used for H3/A83T/A64 ++ EMAC ethernet controller. + endif + + config STMMAC_PCI +diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile +index 700c603..fd4937a 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/Makefile ++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile +@@ -16,6 +16,7 @@ obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o + obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o + obj-$(CONFIG_DWMAC_STM32) += dwmac-stm32.o + obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o ++obj-$(CONFIG_DWMAC_SUN8I) += dwmac-sun8i.o + obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o + obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o + stmmac-platform-objs:= stmmac_platform.o +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +new file mode 100644 +index 0000000..52ab67c +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +@@ -0,0 +1,938 @@ +/* -+ * sun8i-emac driver ++ * dwmac-sun8i.c - Allwinner sun8i DWMAC specific glue layer + * -+ * Copyright (C) 2015-2016 Corentin LABBE <clabbe.montjoie@gmail.com> ++ * Copyright (C) 2017 Corentin Labbe <clabbe.montjoie@gmail.com> + * -+ * This is the driver for Allwinner Ethernet MAC found in H3/A83T/A64 SoC ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. + * -+ * TODO: -+ * - MAC filtering -+ * - Jumbo frame -+ * - features rx-all (NETIF_F_RXALL_BIT) -+ * - PM runtime ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. + */ -+#include <linux/bitops.h> ++ +#include <linux/clk.h> -+#include <linux/dma-mapping.h> -+#include <linux/etherdevice.h> -+#include <linux/interrupt.h> ++#include <linux/io.h> +#include <linux/iopoll.h> -+#include <linux/mii.h> ++#include <linux/mfd/syscon.h> +#include <linux/module.h> -+#include <linux/netdevice.h> +#include <linux/of_device.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h> +#include <linux/phy.h> -+#include <linux/pinctrl/consumer.h> -+#include <linux/pinctrl/pinctrl.h> +#include <linux/platform_device.h> -+#include <linux/reset.h> -+#include <linux/scatterlist.h> -+#include <linux/skbuff.h> -+#include <linux/mfd/syscon.h> ++#include <linux/regulator/consumer.h> +#include <linux/regmap.h> ++#include <linux/stmmac.h> + -+#define EMAC_BASIC_CTL0 0x00 -+#define EMAC_BASIC_CTL1 0x04 -+#define EMAC_INT_STA 0x08 -+#define EMAC_INT_EN 0x0C -+#define EMAC_TX_CTL0 0x10 -+#define EMAC_TX_CTL1 0x14 -+#define EMAC_TX_FLOW_CTL 0x1C -+#define EMAC_RX_CTL0 0x24 -+#define EMAC_RX_CTL1 0x28 -+#define EMAC_RX_FRM_FLT 0x38 -+#define EMAC_MDIO_CMD 0x48 -+#define EMAC_MDIO_DATA 0x4C -+#define EMAC_TX_DMA_STA 0xB0 -+#define EMAC_TX_CUR_DESC 0xB4 -+#define EMAC_TX_CUR_BUF 0xB8 -+#define EMAC_RX_DMA_STA 0xC0 -+ -+#define MDIO_CMD_MII_BUSY BIT(0) -+#define MDIO_CMD_MII_WRITE BIT(1) -+#define MDIO_CMD_MII_PHY_REG_ADDR_MASK GENMASK(8, 4) -+#define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4 -+#define MDIO_CMD_MII_PHY_ADDR_MASK GENMASK(16, 12) -+#define MDIO_CMD_MII_PHY_ADDR_SHIFT 12 -+ -+#define EMAC_MACADDR_HI 0x50 -+#define EMAC_MACADDR_LO 0x54 -+ -+#define EMAC_RX_DESC_LIST 0x34 -+#define EMAC_TX_DESC_LIST 0x20 -+ -+#define EMAC_RX_DO_CRC BIT(27) -+#define EMAC_RX_STRIP_FCS BIT(28) -+ -+#define LE32_BIT(x) (cpu_to_le32(BIT(x))) ++#include "stmmac.h" ++#include "stmmac_platform.h" + -+#define EMAC_COULD_BE_USED_BY_DMA LE32_BIT(31) -+ -+/* Used in RX_CTL1*/ -+#define EMAC_RX_DMA_EN BIT(30) -+#define EMAC_RX_DMA_START BIT(31) -+/* Used in TX_CTL1*/ -+#define EMAC_TX_DMA_EN BIT(30) -+#define EMAC_TX_DMA_START BIT(31) ++/* General notes on dwmac-sun8i: ++ * Locking: no locking is necessary in this file because all necessary locking ++ * is done in the "stmmac files" ++ */ + -+/* Used in RX_CTL0 */ -+#define EMAC_RX_RECEIVER_EN BIT(31) -+/* Used in TX_CTL0 */ -+#define EMAC_TX_TRANSMITTER_EN BIT(31) -+ -+/* Basic CTL0 */ -+#define EMAC_BCTL0_FD BIT(0) -+#define EMAC_BCTL0_SPEED_10 2 -+#define EMAC_BCTL0_SPEED_100 3 -+#define EMAC_BCTL0_SPEED_MASK GENMASK(3, 2) -+#define EMAC_BCTL0_SPEED_SHIFT 2 -+ -+#define EMAC_FLOW_RX 1 -+#define EMAC_FLOW_TX 2 -+ -+#define EMAC_TX_INT BIT(0) -+#define EMAC_TX_DMA_STOP_INT BIT(1) -+#define EMAC_TX_BUF_UA_INT BIT(2) -+#define EMAC_TX_TIMEOUT_INT BIT(3) -+#define EMAC_TX_UNDERFLOW_INT BIT(4) -+#define EMAC_TX_EARLY_INT BIT(5) -+#define EMAC_RX_INT BIT(8) -+#define EMAC_RX_BUF_UA_INT BIT(9) -+#define EMAC_RX_DMA_STOP_INT BIT(10) -+#define EMAC_RX_TIMEOUT_INT BIT(11) -+#define EMAC_RX_OVERFLOW_INT BIT(12) -+#define EMAC_RX_EARLY_INT BIT(13) -+#define EMAC_RGMII_STA_INT BIT(16) -+ -+/* Bits used in frame RX status */ -+#define EMAC_DSC_RX_FIRST BIT(9) -+#define EMAC_DSC_RX_LAST BIT(8) -+ -+/* Bits used in frame TX ctl */ -+#define EMAC_MAGIC_TX_BIT LE32_BIT(24) -+#define EMAC_TX_DO_CRC (LE32_BIT(27) | LE32_BIT(28)) -+#define EMAC_DSC_TX_FIRST LE32_BIT(29) -+#define EMAC_DSC_TX_LAST LE32_BIT(30) -+#define EMAC_WANT_INT LE32_BIT(31) -+ -+/* struct emac_variant - Describe an emac variant of sun8i-emac -+ * @default_syscon_value: Default value of the syscon EMAC register -+ * The default_syscon_value is also used for powering down the PHY -+ * @internal_phy: which PHY type is internal -+ * @support_mii: Does the SoC support MII -+ * @support_rmii: Does the SoC support RMII -+ * @support_rgmii: Does the SoC support RGMII ++/* struct emac_variant - Descrive dwmac-sun8i hardware variant ++ * @default_syscon_value: The default value of the EMAC register in syscon ++ * This value is used for disabling properly EMAC ++ * and used as a good starting value in case of the ++ * boot process(uboot) leave some stuff. ++ * @internal_phy: Does the MAC embed an internal PHY ++ * @support_mii: Does the MAC handle MII ++ * @support_rmii: Does the MAC handle RMII ++ * @support_rgmii: Does the MAC handle RGMII + */ +struct emac_variant { + u32 default_syscon_value; @@ -190,6 +400,25 @@ index 0000000..bc74467 + bool support_rgmii; +}; + ++/* struct sunxi_priv_data - hold all sunxi private data ++ * @tx_clk: reference to MAC TX clock ++ * @ephy_clk: reference to the optional EPHY clock for the internal PHY ++ * @regulator: reference to the optional regulator ++ * @rst_ephy: reference to the optional EPHY reset for the internal PHY ++ * @variant: reference to the current board variant ++ * @regmap: regmap for using the syscon ++ * @use_internal_phy: Does the current PHY choice imply using the internal PHY ++ */ ++struct sunxi_priv_data { ++ struct clk *tx_clk; ++ struct clk *ephy_clk; ++ struct regulator *regulator; ++ struct reset_control *rst_ephy; ++ const struct emac_variant *variant; ++ struct regmap *regmap; ++ bool use_internal_phy; ++}; ++ +static const struct emac_variant emac_variant_h3 = { + .default_syscon_value = 0x58000, + .internal_phy = PHY_INTERFACE_MODE_MII, @@ -213,862 +442,536 @@ index 0000000..bc74467 + .support_rgmii = true +}; + -+static const char const estats_str[][ETH_GSTRING_LEN] = { -+ /* errors */ -+ "rx_payload_error", -+ "rx_CRC_error", -+ "rx_phy_error", -+ "rx_length_error", -+ "rx_col_error", -+ "rx_header_error", -+ "rx_overflow_error", -+ "rx_saf_error", -+ "rx_daf_error", -+ "rx_buf_error", -+ "rx_invalid_error", -+ "tx_timeout", -+ /* misc infos */ -+ "tx_stop_queue", -+ "rx_dma_ua", -+ "rx_dma_stop", -+ "tx_dma_ua", -+ "tx_dma_stop", -+ "rx_hw_csum", -+ "tx_hw_csum", -+ /* interrupts */ -+ "rx_int", -+ "tx_int", -+ "tx_early_int", -+ "tx_underflow_int", -+ "tx_timeout_int", -+ "rx_early_int", -+ "rx_overflow_int", -+ "rx_timeout_int", -+ "rgmii_state_int", -+ /* debug */ -+ "tx_used_desc", -+ "napi_schedule", -+ "napi_underflow", -+}; -+ -+struct sun8i_emac_stats { -+ u64 rx_payload_error; -+ u64 rx_crc_error; -+ u64 rx_phy_error; -+ u64 rx_length_error; -+ u64 rx_col_error; -+ u64 rx_header_error; -+ u64 rx_overflow_error; -+ u64 rx_saf_fail; -+ u64 rx_daf_fail; -+ u64 rx_buf_error; -+ u64 rx_invalid_error; -+ u64 tx_timeout; -+ -+ u64 tx_stop_queue; -+ u64 rx_dma_ua; -+ u64 rx_dma_stop; -+ u64 tx_dma_ua; -+ u64 tx_dma_stop; -+ u64 rx_hw_csum; -+ u64 tx_hw_csum; -+ -+ u64 rx_int; -+ u64 tx_int; -+ u64 tx_early_int; -+ u64 tx_underflow_int; -+ u64 tx_timeout_int; -+ u64 rx_early_int; -+ u64 rx_overflow_int; -+ u64 rx_timeout_int; -+ u64 rgmii_state_int; -+ -+ u64 tx_used_desc; -+ u64 napi_schedule; -+ u64 napi_underflow; -+}; -+ -+/* The datasheet said that each descriptor can transfers up to 4096bytes -+ * But latter, a register documentation reduce that value to 2048 -+ * Anyway using 2048 cause strange behaviours and even BSP driver use 2047 -+ */ -+#define DESC_BUF_MAX 2044 -+ -+/* MAGIC value for knowing if a descriptor is available or not */ -+#define DCLEAN cpu_to_le32(BIT(16) | BIT(14) | BIT(12) | BIT(10) | BIT(9)) ++#define EMAC_BASIC_CTL0 0x00 ++#define EMAC_BASIC_CTL1 0x04 ++#define EMAC_INT_STA 0x08 ++#define EMAC_INT_EN 0x0C ++#define EMAC_TX_CTL0 0x10 ++#define EMAC_TX_CTL1 0x14 ++#define EMAC_TX_FLOW_CTL 0x1C ++#define EMAC_TX_DESC_LIST 0x20 ++#define EMAC_RX_CTL0 0x24 ++#define EMAC_RX_CTL1 0x28 ++#define EMAC_RX_DESC_LIST 0x34 ++#define EMAC_RX_FRM_FLT 0x38 ++#define EMAC_MDIO_CMD 0x48 ++#define EMAC_MDIO_DATA 0x4C ++#define EMAC_MACADDR_HI(reg) (0x50 + (reg) * 8) ++#define EMAC_MACADDR_LO(reg) (0x54 + (reg) * 8) ++#define EMAC_TX_DMA_STA 0xB0 ++#define EMAC_TX_CUR_DESC 0xB4 ++#define EMAC_TX_CUR_BUF 0xB8 ++#define EMAC_RX_DMA_STA 0xC0 ++#define EMAC_RX_CUR_DESC 0xC4 ++#define EMAC_RX_CUR_BUF 0xC8 ++ ++/* Use in EMAC_BASIC_CTL1 */ ++#define EMAC_BURSTLEN_SHIFT 24 ++ ++/* Used in EMAC_RX_FRM_FLT */ ++#define EMAC_FRM_FLT_RXALL BIT(0) ++#define EMAC_FRM_FLT_CTL BIT(13) ++#define EMAC_FRM_FLT_MULTICAST BIT(16) + -+/* struct dma_desc - Structure of DMA descriptor used by the hardware -+ * @status: Status of the frame written by HW, so RO for the -+ * driver (except for BIT(31) which is R/W) -+ * @ctl: Information on the frame written by the driver (INT, len,...) -+ * @buf_addr: physical address of the frame data -+ * @next: physical address of next dma_desc -+ */ -+struct dma_desc { -+ __le32 status; -+ __le32 ctl; -+ __le32 buf_addr; -+ __le32 next; -+}; ++/* Used in RX_CTL1*/ ++#define EMAC_RX_MD BIT(1) ++#define EMAC_RX_TH_MASK GENMASK(4, 5) ++#define EMAC_RX_TH_32 0 ++#define EMAC_RX_TH_64 (0x1 << 4) ++#define EMAC_RX_TH_96 (0x2 << 4) ++#define EMAC_RX_TH_128 (0x3 << 4) ++#define EMAC_RX_DMA_EN BIT(30) ++#define EMAC_RX_DMA_START BIT(31) + -+/* Describe how data from skb are DMA mapped (used in txinfo map member) */ -+#define MAP_SINGLE 1 -+#define MAP_PAGE 2 ++/* Used in TX_CTL1*/ ++#define EMAC_TX_MD BIT(1) ++#define EMAC_TX_NEXT_FRM BIT(2) ++#define EMAC_TX_TH_MASK GENMASK(8, 10) ++#define EMAC_TX_TH_64 0 ++#define EMAC_TX_TH_128 (0x1 << 8) ++#define EMAC_TX_TH_192 (0x2 << 8) ++#define EMAC_TX_TH_256 (0x3 << 8) ++#define EMAC_TX_DMA_EN BIT(30) ++#define EMAC_TX_DMA_START BIT(31) + -+/* Structure for storing information about data in TX ring buffer */ -+struct txinfo { -+ struct sk_buff *skb; -+ int map; -+}; ++/* Used in RX_CTL0 */ ++#define EMAC_RX_RECEIVER_EN BIT(31) ++#define EMAC_RX_DO_CRC BIT(27) ++#define EMAC_RX_FLOW_CTL_EN BIT(16) + -+struct sun8i_emac_priv { -+ void __iomem *base; -+ struct regmap *regmap; -+ int irq; -+ struct device *dev; -+ struct net_device *ndev; -+ struct mii_bus *mdio; -+ struct napi_struct napi; -+ spinlock_t tx_lock;/* control the access of transmit descriptors */ -+ int duplex; -+ int speed; -+ int link; -+ int phy_interface; -+ const struct emac_variant *variant; -+ struct device_node *phy_node; -+ struct device_node *mdio_node; -+ struct clk *ahb_clk; -+ struct clk *ephy_clk; -+ bool use_internal_phy; ++/* Used in TX_CTL0 */ ++#define EMAC_TX_TRANSMITTER_EN BIT(31) ++ ++/* Used in EMAC_TX_FLOW_CTL */ ++#define EMAC_TX_FLOW_CTL_EN BIT(0) ++ ++/* Used in EMAC_INT_STA */ ++#define EMAC_TX_INT BIT(0) ++#define EMAC_TX_DMA_STOP_INT BIT(1) ++#define EMAC_TX_BUF_UA_INT BIT(2) ++#define EMAC_TX_TIMEOUT_INT BIT(3) ++#define EMAC_TX_UNDERFLOW_INT BIT(4) ++#define EMAC_TX_EARLY_INT BIT(5) ++#define EMAC_RX_INT BIT(8) ++#define EMAC_RX_BUF_UA_INT BIT(9) ++#define EMAC_RX_DMA_STOP_INT BIT(10) ++#define EMAC_RX_TIMEOUT_INT BIT(11) ++#define EMAC_RX_OVERFLOW_INT BIT(12) ++#define EMAC_RX_EARLY_INT BIT(13) ++#define EMAC_RGMII_STA_INT BIT(16) ++ ++#define MAC_ADDR_TYPE_DST BIT(31) + -+ struct reset_control *rst_mac; -+ struct reset_control *rst_ephy; ++/* H3 specific bits for EPHY */ ++#define H3_EPHY_ADDR_SHIFT 20 ++#define H3_EPHY_LED_POL BIT(17) /* 1: active low, 0: active high */ ++#define H3_EPHY_SHUTDOWN BIT(16) /* 1: shutdown, 0: power up */ ++#define H3_EPHY_SELECT BIT(15) /* 1: internal PHY, 0: external PHY */ + -+ struct dma_desc *dd_rx; -+ dma_addr_t dd_rx_phy; -+ struct dma_desc *dd_tx; -+ dma_addr_t dd_tx_phy; -+ struct sk_buff **rx_skb; -+ struct txinfo *txl; -+ -+ int nbdesc_tx; -+ int nbdesc_rx; -+ int tx_slot; -+ int tx_dirty; -+ int rx_dirty; -+ struct sun8i_emac_stats estats; -+ u32 msg_enable; -+ int flow_ctrl; -+ int pause; -+}; ++/* H3/A64 specific bits */ ++#define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */ + -+static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id); ++/* Generic system control EMAC_CLK bits */ ++#define SYSCON_ETXDC_MASK GENMASK(2, 0) ++#define SYSCON_ETXDC_SHIFT 10 ++#define SYSCON_ERXDC_MASK GENMASK(4, 0) ++#define SYSCON_ERXDC_SHIFT 5 ++/* EMAC PHY Interface Type */ ++#define SYSCON_EPIT BIT(2) /* 1: RGMII, 0: MII */ ++#define SYSCON_ETCS_MASK GENMASK(1, 0) ++#define SYSCON_ETCS_MII 0x0 ++#define SYSCON_ETCS_EXT_GMII 0x1 ++#define SYSCON_ETCS_INT_GMII 0x2 ++#define SYSCON_EMAC_REG 0x30 + -+static void rb_inc(int *p, const int max) ++/* sun8i_dwmac_dma_reset() - reset the EMAC ++ * Called from stmmac via stmmac_dma_ops->reset ++ */ ++static int sun8i_dwmac_dma_reset(void __iomem *ioaddr) +{ -+ (*p)++; -+ (*p) %= max; ++ writel(0, ioaddr + EMAC_RX_CTL1); ++ writel(0, ioaddr + EMAC_TX_CTL1); ++ writel(0, ioaddr + EMAC_RX_FRM_FLT); ++ writel(0, ioaddr + EMAC_RX_DESC_LIST); ++ writel(0, ioaddr + EMAC_TX_DESC_LIST); ++ writel(0, ioaddr + EMAC_INT_EN); ++ writel(0x1FFFFFF, ioaddr + EMAC_INT_STA); ++ return 0; +} + -+/* Locking strategy: -+ * RX queue does not need any lock since only sun8i_emac_poll() access it. -+ * (All other RX modifiers (ringparam/ndo_stop) disable NAPI and so -+ * sun8i_emac_poll()) -+ * TX queue is handled by sun8i_emac_xmit(), sun8i_emac_complete_xmit() and -+ * sun8i_emac_tx_timeout() -+ * (All other RX modifiers (ringparam/ndo_stop) disable NAPI and stop queue) -+ * -+ * sun8i_emac_xmit() could fire only once (netif_tx_lock) -+ * sun8i_emac_complete_xmit() could fire only once (called from NAPI) -+ * sun8i_emac_tx_timeout() could fire only once (netif_tx_lock) and could not -+ * race with sun8i_emac_xmit (due to netif_tx_lock) and with -+ * sun8i_emac_complete_xmit which disable NAPI. -+ * -+ * So only sun8i_emac_xmit and sun8i_emac_complete_xmit could fire at the same -+ * time. -+ * But they never could modify the same descriptors: -+ * - sun8i_emac_complete_xmit() will modify only descriptors with empty status -+ * - sun8i_emac_xmit() will modify only descriptors set to DCLEAN -+ * Proper memory barriers ensure that descriptor set to DCLEAN could not be -+ * modified latter by sun8i_emac_complete_xmit(). -+ */ -+ -+/* Return the number of contiguous free descriptors -+ * starting from tx_slot ++/* sun8i_dwmac_dma_init() - initialize the EMAC ++ * Called from stmmac via stmmac_dma_ops->init + */ -+static int rb_tx_numfreedesc(struct net_device *ndev) ++static void sun8i_dwmac_dma_init(void __iomem *ioaddr, ++ struct stmmac_dma_cfg *dma_cfg, ++ u32 dma_tx, u32 dma_rx, int atds) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ if (priv->tx_slot < priv->tx_dirty) -+ return priv->tx_dirty - priv->tx_slot; ++ /* Write TX and RX descriptors address */ ++ writel(dma_rx, ioaddr + EMAC_RX_DESC_LIST); ++ writel(dma_tx, ioaddr + EMAC_TX_DESC_LIST); + -+ return (priv->nbdesc_tx - priv->tx_slot) + priv->tx_dirty; ++ writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN); ++ writel(0x1FFFFFF, ioaddr + EMAC_INT_STA); +} + -+/* sun8i_emac_rx_skb - Allocate a skb in a DMA descriptor -+ * -+ * @ndev: The net_device for this interface -+ * @i: index of slot to fill -+ * -+ * Refill a DMA descriptor with a fresh skb and map it for DMA. -+*/ -+static int sun8i_emac_rx_skb(struct net_device *ndev, int i) ++/* sun8i_dwmac_dump_regs() - Dump EMAC address space ++ * Called from stmmac_dma_ops->dump_regs ++ * Used for ethtool ++ */ ++static void sun8i_dwmac_dump_regs(void __iomem *ioaddr, u32 *reg_space) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct dma_desc *ddesc; -+ struct sk_buff *skb; -+ -+ ddesc = priv->dd_rx + i; -+ -+ ddesc->ctl = 0; -+ -+ skb = netdev_alloc_skb_ip_align(ndev, DESC_BUF_MAX); -+ if (!skb) -+ return -ENOMEM; -+ -+ /* should not happen */ -+ if (unlikely(priv->rx_skb[i])) -+ dev_warn(priv->dev, "BUG: Leaking a skbuff\n"); -+ -+ priv->rx_skb[i] = skb; ++ int i; + -+ ddesc->buf_addr = dma_map_single(priv->dev, skb->data, -+ DESC_BUF_MAX, DMA_FROM_DEVICE); -+ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { -+ dev_err(priv->dev, "ERROR: Cannot map RX buffer for DMA\n"); -+ dev_kfree_skb(skb); -+ return -EFAULT; ++ for (i = 0; i < 0xC8; i += 4) { ++ if (i == 0x32 || i == 0x3C) ++ continue; ++ reg_space[i / 4] = readl(ioaddr + i); + } -+ /* We cannot direcly use cpu_to_le32() after dma_map_single -+ * since dma_mapping_error use it -+ */ -+ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); -+ ddesc->ctl |= cpu_to_le32(DESC_BUF_MAX); -+ /* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ -+ wmb(); -+ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; -+ -+ return 0; +} + -+static void sun8i_emac_stop_tx(struct net_device *ndev) ++/* sun8i_dwmac_dump_mac_regs() - Dump EMAC address space ++ * Called from stmmac_ops->dump_regs ++ * Used for ethtool ++ */ ++static void sun8i_dwmac_dump_mac_regs(struct mac_device_info *hw, ++ u32 *reg_space) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ u32 v; -+ -+ netif_stop_queue(ndev); -+ -+ v = readl(priv->base + EMAC_TX_CTL0); -+ /* Disable transmitter after current reception */ -+ v &= ~EMAC_TX_TRANSMITTER_EN; -+ writel(v, priv->base + EMAC_TX_CTL0); -+ -+ v = readl(priv->base + EMAC_TX_CTL1); -+ /* Stop TX DMA */ -+ v &= ~EMAC_TX_DMA_EN; -+ writel(v, priv->base + EMAC_TX_CTL1); ++ int i; ++ void __iomem *ioaddr = hw->pcsr; + -+ /* We must be sure that all is stopped before leaving this function */ -+ wmb(); ++ for (i = 0; i < 0xC8; i += 4) { ++ if (i == 0x32 || i == 0x3C) ++ continue; ++ reg_space[i / 4] = readl(ioaddr + i); ++ } +} + -+static void sun8i_emac_stop_rx(struct net_device *ndev) ++static void sun8i_dwmac_enable_dma_irq(void __iomem *ioaddr) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ u32 v; -+ -+ v = readl(priv->base + EMAC_RX_CTL0); -+ /* Disable receiver after current reception */ -+ v &= ~EMAC_RX_RECEIVER_EN; -+ writel(v, priv->base + EMAC_RX_CTL0); -+ -+ v = readl(priv->base + EMAC_RX_CTL1); -+ /* Stop RX DMA */ -+ v &= ~EMAC_RX_DMA_EN; -+ writel(v, priv->base + EMAC_RX_CTL1); ++ writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN); ++} + -+ /* We must be sure that all is stopped before leaving this function */ -+ wmb(); ++static void sun8i_dwmac_disable_dma_irq(void __iomem *ioaddr) ++{ ++ writel(0, ioaddr + EMAC_INT_EN); +} + -+static void sun8i_emac_start_rx(struct net_device *ndev) ++static void sun8i_dwmac_dma_start_tx(void __iomem *ioaddr) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v; + -+ v = readl(priv->base + EMAC_RX_CTL0); -+ /* Enable receiver */ -+ v |= EMAC_RX_RECEIVER_EN; -+ writel(v, priv->base + EMAC_RX_CTL0); -+ -+ v = readl(priv->base + EMAC_RX_CTL1); -+ v |= EMAC_RX_DMA_START; -+ v |= EMAC_RX_DMA_EN; -+ writel(v, priv->base + EMAC_RX_CTL1); ++ v = readl(ioaddr + EMAC_TX_CTL0); ++ v |= EMAC_TX_TRANSMITTER_EN; ++ writel(v, ioaddr + EMAC_TX_CTL0); +} + -+static void sun8i_emac_start_tx(struct net_device *ndev) ++static void sun8i_dwmac_enable_dma_transmission(void __iomem *ioaddr) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v; + -+ v = readl(priv->base + EMAC_TX_CTL0); -+ v |= EMAC_TX_TRANSMITTER_EN; -+ writel(v, priv->base + EMAC_TX_CTL0); -+ -+ v = readl(priv->base + EMAC_TX_CTL1); ++ v = readl(ioaddr + EMAC_TX_CTL1); + v |= EMAC_TX_DMA_START; + v |= EMAC_TX_DMA_EN; -+ writel(v, priv->base + EMAC_TX_CTL1); ++ writel_relaxed(v, ioaddr + EMAC_TX_CTL1); +} + -+/* sun8i_emac_set_macaddr - Set MAC address for slot index -+ * -+ * @addr: the MAC address to set -+ * @index: The index of slot where to set address. -+ * -+ * The slot 0 is the main MAC address -+ */ -+static void sun8i_emac_set_macaddr(struct sun8i_emac_priv *priv, -+ const u8 *addr, int index) ++static void sun8i_dwmac_dma_stop_tx(void __iomem *ioaddr) +{ + u32 v; + -+ dev_info(priv->dev, "device MAC address slot %d %pM", index, addr); -+ -+ v = (addr[5] << 8) | addr[4]; -+ writel(v, priv->base + EMAC_MACADDR_HI + index * 8); -+ -+ v = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; -+ writel(v, priv->base + EMAC_MACADDR_LO + index * 8); ++ v = readl(ioaddr + EMAC_TX_CTL0); ++ v &= ~EMAC_TX_TRANSMITTER_EN; ++ writel(v, ioaddr + EMAC_TX_CTL0); +} + -+static void sun8i_emac_set_link_mode(struct sun8i_emac_priv *priv) ++static void sun8i_dwmac_dma_start_rx(void __iomem *ioaddr) +{ + u32 v; + -+ v = readl(priv->base + EMAC_BASIC_CTL0); -+ -+ if (priv->duplex) -+ v |= EMAC_BCTL0_FD; -+ else -+ v &= ~EMAC_BCTL0_FD; -+ -+ v &= ~EMAC_BCTL0_SPEED_MASK; -+ -+ switch (priv->speed) { -+ case 1000: -+ break; -+ case 100: -+ v |= EMAC_BCTL0_SPEED_100 << EMAC_BCTL0_SPEED_SHIFT; -+ break; -+ case 10: -+ v |= EMAC_BCTL0_SPEED_10 << EMAC_BCTL0_SPEED_SHIFT; -+ break; -+ default: -+ dev_err(priv->dev, "Unsupported speed %d\n", priv->speed); -+ return; -+ } ++ v = readl(ioaddr + EMAC_RX_CTL0); ++ v |= EMAC_RX_RECEIVER_EN; ++ writel(v, ioaddr + EMAC_RX_CTL0); + -+ writel(v, priv->base + EMAC_BASIC_CTL0); ++ v = readl(ioaddr + EMAC_RX_CTL1); ++ v |= EMAC_RX_DMA_START; ++ v |= EMAC_RX_DMA_EN; ++ writel(v, ioaddr + EMAC_RX_CTL1); +} + -+static void sun8i_emac_flow_ctrl(struct sun8i_emac_priv *priv, int duplex, -+ int fc) ++static void sun8i_dwmac_dma_stop_rx(void __iomem *ioaddr) +{ -+ u32 flow = 0; ++ u32 v; + -+ flow = readl(priv->base + EMAC_RX_CTL0); -+ if (fc & EMAC_FLOW_RX) -+ flow |= BIT(16); -+ else -+ flow &= ~BIT(16); -+ writel(flow, priv->base + EMAC_RX_CTL0); ++ v = readl(ioaddr + EMAC_RX_CTL0); ++ v &= ~EMAC_RX_RECEIVER_EN; ++ writel(v, ioaddr + EMAC_RX_CTL0); + -+ flow = readl(priv->base + EMAC_TX_FLOW_CTL); -+ if (fc & EMAC_FLOW_TX) -+ flow |= BIT(0); -+ else -+ flow &= ~BIT(0); -+ writel(flow, priv->base + EMAC_TX_FLOW_CTL); ++ v = readl(ioaddr + EMAC_RX_CTL1); ++ v &= ~EMAC_RX_DMA_EN; ++ writel(v, ioaddr + EMAC_RX_CTL1); +} + -+/* Grab a frame into a skb from descriptor number i */ -+static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i) ++static int sun8i_dwmac_dma_interrupt(void __iomem *ioaddr, ++ struct stmmac_extra_stats *x) +{ -+ struct sk_buff *skb; -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct dma_desc *ddesc = priv->dd_rx + i; -+ int frame_len; -+ int rxcsum_done = 0; -+ u32 dstatus = le32_to_cpu(ddesc->status); -+ -+ if (ndev->features & NETIF_F_RXCSUM) -+ rxcsum_done = 1; -+ -+ /* bit0/bit7 work only on IPv4/IPv6 TCP traffic, -+ * (not on ARP for example) so we do not raise rx_errors/discard frame -+ */ -+ /* the checksum or length of received frame's payload is wrong*/ -+ if (dstatus & BIT(0)) { -+ priv->estats.rx_payload_error++; -+ rxcsum_done = 0; -+ } -+ -+ /* RX_CRC_ERR */ -+ if (dstatus & BIT(1)) { -+ priv->ndev->stats.rx_errors++; -+ priv->ndev->stats.rx_crc_errors++; -+ priv->estats.rx_crc_error++; -+ goto discard_frame; -+ } ++ u32 v; ++ int ret = 0; + -+ /* RX_PHY_ERR */ -+ if ((dstatus & BIT(3))) { -+ priv->ndev->stats.rx_errors++; -+ priv->estats.rx_phy_error++; -+ goto discard_frame; -+ } ++ v = readl(ioaddr + EMAC_INT_STA); + -+ /* RX_LENGTH_ERR */ -+ if ((dstatus & BIT(4))) { -+ priv->ndev->stats.rx_errors++; -+ priv->ndev->stats.rx_length_errors++; -+ priv->estats.rx_length_error++; -+ goto discard_frame; ++ if (v & EMAC_TX_INT) { ++ ret |= handle_tx; ++ x->tx_normal_irq_n++; + } + -+ /* RX_COL_ERR */ -+ if ((dstatus & BIT(6))) { -+ priv->ndev->stats.rx_errors++; -+ priv->estats.rx_col_error++; -+ goto discard_frame; -+ } ++ if (v & EMAC_TX_DMA_STOP_INT) ++ x->tx_process_stopped_irq++; + -+ /* RX_HEADER_ERR */ -+ if ((dstatus & BIT(7))) { -+ priv->estats.rx_header_error++; -+ rxcsum_done = 0; -+ } ++ if (v & EMAC_TX_BUF_UA_INT) ++ x->tx_process_stopped_irq++; + -+ /* RX_OVERFLOW_ERR */ -+ if ((dstatus & BIT(11))) { -+ priv->ndev->stats.rx_over_errors++; -+ priv->estats.rx_overflow_error++; -+ goto discard_frame; -+ } ++ if (v & EMAC_TX_TIMEOUT_INT) ++ ret |= tx_hard_error; + -+ /* RX_NO_ENOUGTH_BUF_ERR */ -+ if ((dstatus & BIT(14))) { -+ priv->ndev->stats.rx_errors++; -+ priv->estats.rx_buf_error++; -+ goto discard_frame; ++ if (v & EMAC_TX_UNDERFLOW_INT) { ++ ret |= tx_hard_error; ++ x->tx_undeflow_irq++; + } + -+ /* BIT(9) is for the first frame, not having it is bad since we do not -+ * handle Jumbo frame -+ */ -+ if ((dstatus & EMAC_DSC_RX_FIRST) == 0) { -+ priv->ndev->stats.rx_errors++; -+ priv->estats.rx_invalid_error++; -+ goto discard_frame; -+ } ++ if (v & EMAC_TX_EARLY_INT) ++ x->tx_early_irq++; + -+ /* this frame is not the last */ -+ if ((dstatus & EMAC_DSC_RX_LAST) == 0) { -+ priv->ndev->stats.rx_errors++; -+ priv->estats.rx_invalid_error++; -+ goto discard_frame; ++ if (v & EMAC_RX_INT) { ++ ret |= handle_rx; ++ x->rx_normal_irq_n++; + } + -+ frame_len = (dstatus >> 16) & 0x3FFF; -+ if (!(ndev->features & NETIF_F_RXFCS)) -+ frame_len -= ETH_FCS_LEN; ++ if (v & EMAC_RX_BUF_UA_INT) ++ x->rx_buf_unav_irq++; + -+ skb = priv->rx_skb[i]; -+ -+ netif_dbg(priv, rx_status, priv->ndev, -+ "%s from %02d %pad len=%d status=%x st=%x\n", -+ __func__, i, &ddesc, frame_len, dstatus, -+ cpu_to_le32(ddesc->ctl)); ++ if (v & EMAC_RX_DMA_STOP_INT) ++ x->rx_process_stopped_irq++; + -+ skb_put(skb, frame_len); ++ if (v & EMAC_RX_TIMEOUT_INT) ++ ret |= tx_hard_error; + -+ dma_unmap_single(priv->dev, le32_to_cpu(ddesc->buf_addr), DESC_BUF_MAX, -+ DMA_FROM_DEVICE); -+ skb->protocol = eth_type_trans(skb, priv->ndev); -+ if (rxcsum_done) { -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ priv->estats.rx_hw_csum++; -+ } else { -+ skb->ip_summed = CHECKSUM_PARTIAL; ++ if (v & EMAC_RX_OVERFLOW_INT) { ++ ret |= tx_hard_error; ++ x->rx_overflow_irq++; + } + -+ priv->ndev->stats.rx_packets++; -+ priv->ndev->stats.rx_bytes += frame_len; -+ priv->rx_skb[i] = NULL; ++ if (v & EMAC_RX_EARLY_INT) ++ x->rx_early_irq++; + -+ sun8i_emac_rx_skb(ndev, i); -+ napi_gro_receive(&priv->napi, skb); ++ if (v & EMAC_RGMII_STA_INT) ++ x->irq_rgmii_n++; + -+ return 0; -+ /* If the frame need to be dropped, we simply reuse the buffer */ -+discard_frame: -+ ddesc->ctl = cpu_to_le32(DESC_BUF_MAX); -+ /* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ -+ wmb(); -+ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; -+ return 0; ++ writel(v, ioaddr + EMAC_INT_STA); ++ ++ return ret; +} + -+/* Iterate over dma_desc for finding completed xmit. -+ * -+ * The problem is: how to know that a descriptor is sent and not just in -+ * preparation. -+ * Need to have status=0 and st set but this is the state of first frame just -+ * before setting the own-by-DMA bit. -+ * The solution is to used the artificial value DCLEAN. -+ */ -+static int sun8i_emac_complete_xmit(struct net_device *ndev, int budget) ++static void sun8i_dwmac_dma_operation_mode(void __iomem *ioaddr, int txmode, ++ int rxmode, int rxfifosz) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct dma_desc *ddesc; -+ int frame_len; -+ int work = 0; -+ unsigned int bytes_compl = 0, pkts_compl = 0; -+ u32 dstatus; -+ -+ do { -+ ddesc = priv->dd_tx + priv->tx_dirty; ++ u32 v; + -+ if (ddesc->status & EMAC_COULD_BE_USED_BY_DMA) -+ goto xmit_end; ++ v = readl(ioaddr + EMAC_TX_CTL1); ++ if (txmode == SF_DMA_MODE) { ++ v |= EMAC_TX_MD; ++ /* Undocumented bit (called TX_NEXT_FRM in BSP), the original ++ * comment is ++ * "Operating on second frame increase the performance ++ * especially when transmit store-and-forward is used." ++ */ ++ v |= EMAC_TX_NEXT_FRM; ++ } else { ++ v &= ~EMAC_TX_MD; ++ v &= ~EMAC_TX_TH_MASK; ++ if (txmode < 64) ++ v |= EMAC_TX_TH_64; ++ else if (txmode < 128) ++ v |= EMAC_TX_TH_128; ++ else if (txmode < 192) ++ v |= EMAC_TX_TH_192; ++ else if (txmode < 256) ++ v |= EMAC_TX_TH_256; ++ } ++ writel(v, ioaddr + EMAC_TX_CTL1); ++ ++ v = readl(ioaddr + EMAC_RX_CTL1); ++ if (rxmode == SF_DMA_MODE) { ++ v |= EMAC_RX_MD; ++ } else { ++ v &= ~EMAC_RX_MD; ++ v &= ~EMAC_RX_TH_MASK; ++ if (rxmode < 32) ++ v |= EMAC_RX_TH_32; ++ else if (rxmode < 64) ++ v |= EMAC_RX_TH_64; ++ else if (rxmode < 96) ++ v |= EMAC_RX_TH_96; ++ else if (rxmode < 128) ++ v |= EMAC_RX_TH_128; ++ } ++ writel(v, ioaddr + EMAC_RX_CTL1); ++} + -+ if (ddesc->status == DCLEAN) -+ goto xmit_end; ++static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = { ++ .reset = sun8i_dwmac_dma_reset, ++ .init = sun8i_dwmac_dma_init, ++ .dump_regs = sun8i_dwmac_dump_regs, ++ .dma_mode = sun8i_dwmac_dma_operation_mode, ++ .enable_dma_transmission = sun8i_dwmac_enable_dma_transmission, ++ .enable_dma_irq = sun8i_dwmac_enable_dma_irq, ++ .disable_dma_irq = sun8i_dwmac_disable_dma_irq, ++ .start_tx = sun8i_dwmac_dma_start_tx, ++ .stop_tx = sun8i_dwmac_dma_stop_tx, ++ .start_rx = sun8i_dwmac_dma_start_rx, ++ .stop_rx = sun8i_dwmac_dma_stop_rx, ++ .dma_interrupt = sun8i_dwmac_dma_interrupt, ++}; + -+ dstatus = cpu_to_le32(ddesc->status); ++static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) ++{ ++ struct sunxi_priv_data *gmac = priv; ++ int ret; + -+ if (ddesc->status == 0 && !ddesc->ctl) { -+ dev_err(priv->dev, "BUG: reached the void %d %d\n", -+ priv->tx_dirty, priv->tx_slot); -+ goto xmit_end; ++ if (gmac->regulator) { ++ ret = regulator_enable(gmac->regulator); ++ if (ret) { ++ dev_err(&pdev->dev, "Fail to enable regulator\n"); ++ return ret; + } ++ } + -+ /* TX_UNDERFLOW_ERR */ -+ if (dstatus & BIT(1)) -+ priv->ndev->stats.tx_errors++; -+ /* TX_DEFER_ERR */ -+ if (dstatus & BIT(2)) -+ priv->ndev->stats.tx_errors++; -+ /* BIT 6:3 numbers of collisions */ -+ if (dstatus & 0x78) -+ priv->ndev->stats.collisions += -+ (dstatus & 0x78) >> 3; -+ /* TX_COL_ERR_1 */ -+ if (dstatus & BIT(8)) -+ priv->ndev->stats.tx_errors++; -+ /* TX_COL_ERR_0 */ -+ if (dstatus & BIT(9)) -+ priv->ndev->stats.tx_errors++; -+ /* TX_CRS_ERR */ -+ if (dstatus & BIT(10)) -+ priv->ndev->stats.tx_carrier_errors++; -+ /* TX_PAYLOAD_ERR */ -+ if (dstatus & BIT(12)) -+ priv->ndev->stats.tx_errors++; -+ /* TX_LENGTH_ERR */ -+ if (dstatus & BIT(14)) -+ priv->ndev->stats.tx_errors++; -+ /* TX_HEADER_ERR */ -+ if (dstatus & BIT(16)) -+ priv->ndev->stats.tx_errors++; -+ -+ frame_len = le32_to_cpu(ddesc->ctl) & 0x3FFF; -+ bytes_compl += frame_len; -+ -+ if (priv->txl[priv->tx_dirty].map == MAP_SINGLE) -+ dma_unmap_single(priv->dev, -+ le32_to_cpu(ddesc->buf_addr), -+ frame_len, DMA_TO_DEVICE); -+ else -+ dma_unmap_page(priv->dev, -+ le32_to_cpu(ddesc->buf_addr), -+ frame_len, DMA_TO_DEVICE); -+ /* we can free skb only on last frame */ -+ if (priv->txl[priv->tx_dirty].skb && -+ (ddesc->ctl & EMAC_DSC_TX_LAST)) { -+ dev_kfree_skb_irq(priv->txl[priv->tx_dirty].skb); -+ pkts_compl++; -+ } ++ ret = clk_prepare_enable(gmac->tx_clk); ++ if (ret) { ++ if (gmac->regulator) ++ regulator_disable(gmac->regulator); ++ dev_err(&pdev->dev, "Could not enable AHB clock\n"); ++ return ret; ++ } + -+ priv->txl[priv->tx_dirty].skb = NULL; -+ priv->txl[priv->tx_dirty].map = 0; -+ ddesc->ctl = 0; -+ /* setting status to DCLEAN is the last value to be set */ -+ wmb(); -+ ddesc->status = DCLEAN; -+ work++; -+ -+ rb_inc(&priv->tx_dirty, priv->nbdesc_tx); -+ ddesc = priv->dd_tx + priv->tx_dirty; -+ } while (ddesc->ctl && -+ !(ddesc->status & EMAC_COULD_BE_USED_BY_DMA) && -+ work < budget); -+ -+xmit_end: -+ netdev_completed_queue(ndev, pkts_compl, bytes_compl); -+ -+ /* if we don't have handled all packets */ -+ if (work < budget) -+ work = 0; -+ -+ if (netif_queue_stopped(ndev) && -+ rb_tx_numfreedesc(ndev) > MAX_SKB_FRAGS + 1) -+ netif_wake_queue(ndev); -+ return work; ++ return 0; +} + -+static int sun8i_emac_poll(struct napi_struct *napi, int budget) ++static void sun8i_dwmac_core_init(struct mac_device_info *hw, int mtu) +{ -+ struct sun8i_emac_priv *priv = -+ container_of(napi, struct sun8i_emac_priv, napi); -+ struct net_device *ndev = priv->ndev; -+ int worked; -+ struct dma_desc *ddesc; -+ -+ priv->estats.napi_schedule++; -+ worked = sun8i_emac_complete_xmit(ndev, budget); -+ -+ ddesc = priv->dd_rx + priv->rx_dirty; -+ while (!(ddesc->status & EMAC_COULD_BE_USED_BY_DMA) && -+ worked < budget) { -+ sun8i_emac_rx_from_ddesc(ndev, priv->rx_dirty); -+ worked++; -+ rb_inc(&priv->rx_dirty, priv->nbdesc_rx); -+ ddesc = priv->dd_rx + priv->rx_dirty; -+ }; -+ if (worked < budget) { -+ priv->estats.napi_underflow++; -+ napi_complete(&priv->napi); -+ writel(EMAC_RX_INT | EMAC_TX_INT, priv->base + EMAC_INT_EN); -+ } -+ return worked; ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; ++ ++ v = (8 << EMAC_BURSTLEN_SHIFT); /* burst len */ ++ writel(v, ioaddr + EMAC_BASIC_CTL1); +} + -+static int sun8i_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) ++static void sun8i_dwmac_set_umac_addr(struct mac_device_info *hw, ++ unsigned char *addr, ++ unsigned int reg_n) +{ -+ struct net_device *ndev = bus->priv; -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ int err; -+ u32 reg; -+ -+ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, -+ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); -+ if (err) { -+ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); -+ return err; -+ } -+ -+ reg &= ~MDIO_CMD_MII_WRITE; -+ reg &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; -+ reg |= (phy_reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & -+ MDIO_CMD_MII_PHY_REG_ADDR_MASK; -+ -+ reg &= ~MDIO_CMD_MII_PHY_ADDR_MASK; -+ -+ reg |= (phy_addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & -+ MDIO_CMD_MII_PHY_ADDR_MASK; -+ -+ reg |= MDIO_CMD_MII_BUSY; -+ -+ writel(reg, priv->base + EMAC_MDIO_CMD); -+ -+ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, -+ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; + -+ if (err) { -+ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); -+ return err; ++ stmmac_set_mac_addr(ioaddr, addr, EMAC_MACADDR_HI(reg_n), ++ EMAC_MACADDR_LO(reg_n)); ++ if (reg_n > 0) { ++ v = readl(ioaddr + EMAC_MACADDR_HI(reg_n)); ++ v |= MAC_ADDR_TYPE_DST; ++ writel(v, ioaddr + EMAC_MACADDR_HI(reg_n)); + } -+ -+ return readl(priv->base + EMAC_MDIO_DATA); +} + -+static int sun8i_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, -+ u16 data) ++static void sun8i_dwmac_get_umac_addr(struct mac_device_info *hw, ++ unsigned char *addr, ++ unsigned int reg_n) +{ -+ struct net_device *ndev = bus->priv; -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ u32 reg; -+ int err; ++ void __iomem *ioaddr = hw->pcsr; + -+ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, -+ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); -+ if (err) { -+ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); -+ return err; -+ } -+ -+ reg &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; -+ reg |= (phy_reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & -+ MDIO_CMD_MII_PHY_REG_ADDR_MASK; -+ -+ reg &= ~MDIO_CMD_MII_PHY_ADDR_MASK; -+ reg |= (phy_addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & -+ MDIO_CMD_MII_PHY_ADDR_MASK; ++ stmmac_get_mac_addr(ioaddr, addr, EMAC_MACADDR_HI(reg_n), ++ EMAC_MACADDR_LO(reg_n)); ++} + -+ reg |= MDIO_CMD_MII_WRITE; -+ reg |= MDIO_CMD_MII_BUSY; ++/* caution this function must return non 0 to work */ ++static int sun8i_dwmac_rx_ipc_enable(struct mac_device_info *hw) ++{ ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; + -+ writel(reg, priv->base + EMAC_MDIO_CMD); -+ writel(data, priv->base + EMAC_MDIO_DATA); ++ v = readl(ioaddr + EMAC_RX_CTL0); ++ v |= EMAC_RX_DO_CRC; ++ writel(v, ioaddr + EMAC_RX_CTL0); + -+ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, -+ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); -+ if (err) { -+ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); -+ return err; -+ } -+ -+ return 0; ++ return 1; +} + -+static int sun8i_emac_mdio_register(struct net_device *ndev) ++static void sun8i_dwmac_set_filter(struct mac_device_info *hw, ++ struct net_device *dev) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct mii_bus *bus; -+ int ret; -+ -+ bus = mdiobus_alloc(); -+ if (!bus) { -+ netdev_err(ndev, "Failed to allocate a new mdio bus\n"); -+ return -ENOMEM; -+ } ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; ++ int i = 0; ++ struct netdev_hw_addr *ha; + -+ bus->name = dev_name(priv->dev); -+ bus->read = &sun8i_mdio_read; -+ bus->write = &sun8i_mdio_write; -+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%x", bus->name, priv->dev->id); ++ v = readl(ioaddr + EMAC_RX_FRM_FLT); + -+ bus->parent = priv->dev; -+ bus->priv = ndev; ++ v |= EMAC_FRM_FLT_CTL; + -+ ret = of_mdiobus_register(bus, priv->mdio_node); -+ if (ret) { -+ netdev_err(ndev, "Could not register a MDIO bus: %d\n", ret); -+ mdiobus_free(bus); -+ return ret; ++ if (dev->flags & IFF_PROMISC) { ++ v = EMAC_FRM_FLT_RXALL; ++ } else if (dev->flags & IFF_ALLMULTI) { ++ v = EMAC_FRM_FLT_MULTICAST; ++ } else if (!netdev_mc_empty(dev)) { ++ netdev_for_each_mc_addr(ha, dev) { ++ i++; ++ sun8i_dwmac_set_umac_addr(hw, ha->addr, i); ++ } + } + -+ priv->mdio = bus; -+ -+ return 0; ++ if (netdev_uc_count(dev) + i > hw->unicast_filter_entries) { ++ netdev_info(dev, "Too many address, switching to promiscuous\n"); ++ v = EMAC_FRM_FLT_RXALL; ++ } else { ++ netdev_for_each_uc_addr(ha, dev) { ++ i++; ++ sun8i_dwmac_set_umac_addr(hw, ha->addr, i); ++ } ++ } ++ writel(v, ioaddr + EMAC_RX_FRM_FLT); +} + -+static void sun8i_emac_mdio_unregister(struct net_device *ndev) ++static void sun8i_dwmac_flow_ctrl(struct mac_device_info *hw, ++ unsigned int duplex, ++ unsigned int fc, unsigned int pause_time) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ void __iomem *ioaddr = hw->pcsr; ++ u32 v; ++ ++ v = readl(ioaddr + EMAC_RX_CTL0); ++ if (fc == FLOW_AUTO) ++ v |= EMAC_RX_FLOW_CTL_EN; ++ else ++ v &= ~EMAC_RX_FLOW_CTL_EN; ++ writel(v, ioaddr + EMAC_RX_CTL0); + -+ mdiobus_unregister(priv->mdio); -+ mdiobus_free(priv->mdio); ++ v = readl(ioaddr + EMAC_TX_FLOW_CTL); ++ if (fc == FLOW_AUTO) ++ v |= EMAC_TX_FLOW_CTL_EN; ++ else ++ v &= ~EMAC_TX_FLOW_CTL_EN; ++ writel(v, ioaddr + EMAC_TX_FLOW_CTL); +} + -+/* Run within phydev->lock */ -+static void sun8i_emac_adjust_link(struct net_device *ndev) ++static int sun8i_dwmac_reset(struct stmmac_priv *priv) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct phy_device *phydev = ndev->phydev; -+ int new_state = 0; -+ -+ netif_dbg(priv, link, priv->ndev, -+ "%s link=%x duplex=%x speed=%x\n", __func__, -+ phydev->link, phydev->duplex, phydev->speed); -+ if (!phydev) -+ return; -+ -+ if (phydev->link) { -+ if (phydev->duplex != priv->duplex) { -+ new_state = 1; -+ priv->duplex = phydev->duplex; -+ } -+ if (phydev->pause) -+ sun8i_emac_flow_ctrl(priv, phydev->duplex, -+ priv->flow_ctrl); ++ u32 v; ++ int err; + -+ if (phydev->speed != priv->speed) { -+ new_state = 1; -+ priv->speed = phydev->speed; -+ } ++ v = readl(priv->ioaddr + EMAC_BASIC_CTL1); ++ writel(v | 0x01, priv->ioaddr + EMAC_BASIC_CTL1); + -+ if (priv->link == 0) { -+ new_state = 1; -+ priv->link = phydev->link; -+ } ++ err = readl_poll_timeout(priv->ioaddr + EMAC_BASIC_CTL1, v, ++ !(v & 0x01), 100, 10000); + -+ netif_dbg(priv, link, priv->ndev, -+ "%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) { -+ new_state = 1; -+ priv->link = 0; -+ priv->speed = 0; -+ priv->duplex = -1; ++ if (err) { ++ dev_err(priv->device, "EMAC reset timeout\n"); ++ return -EFAULT; + } -+ -+ if (new_state) -+ phy_print_status(phydev); ++ return 0; +} + -+/* H3 specific bits for EPHY */ -+#define H3_EPHY_ADDR_SHIFT 20 -+#define H3_EPHY_LED_POL BIT(17) /* 1: active low, 0: active high */ -+#define H3_EPHY_SHUTDOWN BIT(16) /* 1: shutdown, 0: power up */ -+#define H3_EPHY_SELECT BIT(15) /* 1: internal PHY, 0: external PHY */ -+ -+/* H3/A64 specific bits */ -+#define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */ -+ -+/* Generic system control EMAC_CLK bits */ -+#define SYSCON_ETXDC_MASK GENMASK(2, 0) -+#define SYSCON_ETXDC_SHIFT 10 -+#define SYSCON_ERXDC_MASK GENMASK(4, 0) -+#define SYSCON_ERXDC_SHIFT 5 -+/* EMAC PHY Interface Type */ -+#define SYSCON_EPIT BIT(2) /* 1: RGMII, 0: MII */ -+#define SYSCON_ETCS_MASK GENMASK(1, 0) -+#define SYSCON_ETCS_MII 0x0 -+#define SYSCON_ETCS_EXT_GMII 0x1 -+#define SYSCON_ETCS_INT_GMII 0x2 -+#define SYSCON_EMAC_REG 0x30 -+ -+static int sun8i_emac_set_syscon(struct net_device *ndev) ++static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct device_node *node = priv->dev->of_node; ++ struct sunxi_priv_data *gmac = priv->plat->bsp_priv; ++ struct device_node *node = priv->device->of_node; + int ret; + u32 reg, val; + -+ reg = priv->variant->default_syscon_value; ++ regmap_read(gmac->regmap, SYSCON_EMAC_REG, &val); ++ reg = gmac->variant->default_syscon_value; ++ if (reg != val) ++ dev_warn(priv->device, ++ "Current syscon value is not the default %x (expect %x)\n", ++ val, reg); + -+ if (priv->variant->internal_phy) { -+ if (!priv->use_internal_phy) { ++ if (gmac->variant->internal_phy) { ++ if (!gmac->use_internal_phy) { + /* switch to external PHY interface */ + reg &= ~H3_EPHY_SELECT; + } else { + reg |= H3_EPHY_SELECT; + reg &= ~H3_EPHY_SHUTDOWN; ++ dev_dbg(priv->device, "Select internal_phy %x\n", reg); + -+ if (of_property_read_bool(priv->phy_node, ++ if (of_property_read_bool(priv->plat->phy_node, + "allwinner,leds-active-low")) + reg |= H3_EPHY_LED_POL; ++ else ++ reg &= ~H3_EPHY_LED_POL; + -+ ret = of_mdio_parse_addr(priv->dev, priv->phy_node); ++ ret = of_mdio_parse_addr(priv->device, ++ priv->plat->phy_node); + if (ret < 0) { -+ netdev_err(ndev, "Could not parse MDIO addr\n"); ++ dev_err(priv->device, "Could not parse MDIO addr\n"); + return ret; + } + /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY @@ -1079,29 +982,35 @@ index 0000000..bc74467 + } + + if (!of_property_read_u32(node, "allwinner,tx-delay", &val)) { ++ dev_dbg(priv->device, "set tx-delay to %x\n", val); + if (val <= SYSCON_ETXDC_MASK) { + reg &= ~(SYSCON_ETXDC_MASK << SYSCON_ETXDC_SHIFT); + reg |= (val << SYSCON_ETXDC_SHIFT); + } else { -+ netdev_warn(ndev, "Invalid TX clock delay: %d\n", val); ++ dev_err(priv->device, "Invalid TX clock delay: %d\n", ++ val); ++ return -EINVAL; + } + } + + if (!of_property_read_u32(node, "allwinner,rx-delay", &val)) { ++ dev_dbg(priv->device, "set rx-delay to %x\n", val); + if (val <= SYSCON_ERXDC_MASK) { + reg &= ~(SYSCON_ERXDC_MASK << SYSCON_ERXDC_SHIFT); + reg |= (val << SYSCON_ERXDC_SHIFT); + } else { -+ netdev_warn(ndev, "Invalid RX clock delay: %d\n", val); ++ dev_err(priv->device, "Invalid RX clock delay: %d\n", ++ val); ++ return -EINVAL; + } + } + + /* Clear interface mode bits */ + reg &= ~(SYSCON_ETCS_MASK | SYSCON_EPIT); -+ if (priv->variant->support_rmii) ++ if (gmac->variant->support_rmii) + reg &= ~SYSCON_RMII_EN; + -+ switch (priv->phy_interface) { ++ switch (priv->plat->interface) { + case PHY_INTERFACE_MODE_MII: + /* default */ + break; @@ -1112,1402 +1021,483 @@ index 0000000..bc74467 + reg |= SYSCON_RMII_EN | SYSCON_ETCS_EXT_GMII; + break; + default: -+ netdev_err(ndev, "Unsupported interface mode: %s", -+ phy_modes(priv->phy_interface)); ++ dev_err(priv->device, "Unsupported interface mode: %s", ++ phy_modes(priv->plat->interface)); + return -EINVAL; + } + -+ regmap_write(priv->regmap, SYSCON_EMAC_REG, reg); ++ regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg); + + return 0; +} + -+static void sun8i_emac_unset_syscon(struct net_device *ndev) ++static void sun8i_dwmac_unset_syscon(struct sunxi_priv_data *gmac) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ u32 reg = priv->variant->default_syscon_value; ++ u32 reg = gmac->variant->default_syscon_value; + -+ regmap_write(priv->regmap, SYSCON_EMAC_REG, reg); -+} -+ -+/* Set Management Data Clock, must be call after device reset */ -+static void sun8i_emac_set_mdc(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ unsigned long rate; -+ u32 reg; -+ -+ rate = clk_get_rate(priv->ahb_clk); -+ if (rate > 160000000) -+ reg = 0x3 << 20; /* AHB / 128 */ -+ else if (rate > 80000000) -+ reg = 0x2 << 20; /* AHB / 64 */ -+ else if (rate > 40000000) -+ reg = 0x1 << 20; /* AHB / 32 */ -+ else -+ reg = 0x0 << 20; /* AHB / 16 */ -+ netif_dbg(priv, link, ndev, "MDC auto : %x\n", reg); -+ writel(reg, priv->base + EMAC_MDIO_CMD); ++ regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg); +} + -+/* "power" the device, by enabling clk/reset/regulators */ -+static int sun8i_emac_power(struct net_device *ndev) ++static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct sunxi_priv_data *gmac = priv->plat->bsp_priv; + int ret; + -+ ret = clk_prepare_enable(priv->ahb_clk); -+ if (ret) { -+ netdev_err(ndev, "Could not enable AHB clock\n"); -+ return ret; -+ } -+ -+ if (priv->rst_mac) { -+ ret = reset_control_deassert(priv->rst_mac); -+ if (ret) { -+ netdev_err(ndev, "Could not deassert reset\n"); -+ goto err_reset; -+ } -+ } -+ -+ if (priv->ephy_clk) { -+ ret = clk_prepare_enable(priv->ephy_clk); ++ if (gmac->ephy_clk) { ++ ret = clk_prepare_enable(gmac->ephy_clk); + if (ret) { -+ netdev_err(ndev, "Could not enable EPHY clock\n"); -+ goto err_ephy_clk; ++ dev_err(priv->device, "Cannot enable ephy\n"); ++ return ret; + } + } + -+ if (priv->rst_ephy) { -+ ret = reset_control_deassert(priv->rst_ephy); ++ if (gmac->rst_ephy) { ++ ret = reset_control_deassert(gmac->rst_ephy); + if (ret) { -+ netdev_err(ndev, "Could not deassert EPHY reset\n"); -+ goto err_ephy_reset; ++ dev_err(priv->device, "Cannot deassert ephy\n"); ++ clk_disable_unprepare(gmac->ephy_clk); ++ return ret; + } + } + + return 0; -+ -+err_ephy_reset: -+ if (priv->ephy_clk) -+ clk_disable_unprepare(priv->ephy_clk); -+err_ephy_clk: -+ if (priv->rst_mac) -+ reset_control_assert(priv->rst_mac); -+err_reset: -+ clk_disable_unprepare(priv->ahb_clk); -+ return ret; +} + -+/* "Unpower" the device, disabling clocks and regulators, asserting reset */ -+static void sun8i_emac_unpower(struct net_device *ndev) ++static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ if (priv->rst_ephy) -+ reset_control_assert(priv->rst_ephy); -+ -+ if (priv->ephy_clk) -+ clk_disable_unprepare(priv->ephy_clk); -+ -+ if (priv->rst_mac) -+ reset_control_assert(priv->rst_mac); -+ -+ clk_disable_unprepare(priv->ahb_clk); -+} -+ -+static int sun8i_emac_init(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct device_node *node = priv->dev->of_node; -+ const u8 *addr; -+ -+ /* Try to get MAC address from DT, or assign a random one */ -+ addr = of_get_mac_address(node); -+ if (addr) -+ ether_addr_copy(ndev->dev_addr, addr); -+ else -+ eth_hw_addr_random(ndev); -+ -+ return 0; -+} -+ -+static int sun8i_emac_mdio_probe(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct phy_device *phydev = NULL; -+ -+ phydev = of_phy_connect(ndev, priv->phy_node, &sun8i_emac_adjust_link, -+ 0, priv->phy_interface); -+ -+ if (!phydev) { -+ netdev_err(ndev, "Could not attach to PHY\n"); -+ return -ENODEV; -+ } -+ -+ phy_attached_info(phydev); -+ -+ /* mask with MAC supported features */ -+ phydev->supported &= PHY_GBIT_FEATURES; -+ phydev->advertising = phydev->supported; -+ -+ priv->link = 0; -+ priv->speed = 0; -+ priv->duplex = -1; -+ -+ return 0; -+} -+ -+/* Allocate both RX and TX ring buffer and init them -+ * This function also write the startbase of thoses ring in the device. -+ * All structures that help managing thoses rings are also handled -+ * by this functions (rx_skb/txl) -+ */ -+static int sun8i_emac_alloc_rings(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct dma_desc *ddesc; -+ int err, i; -+ -+ priv->rx_skb = kcalloc(priv->nbdesc_rx, sizeof(struct sk_buff *), -+ GFP_KERNEL); -+ if (!priv->rx_skb) { -+ err = -ENOMEM; -+ goto rx_skb_error; -+ } -+ priv->txl = kcalloc(priv->nbdesc_tx, sizeof(struct txinfo), GFP_KERNEL); -+ if (!priv->txl) { -+ err = -ENOMEM; -+ goto tx_error; -+ } -+ -+ /* allocate/init RX ring */ -+ priv->dd_rx = dma_zalloc_coherent(priv->dev, -+ priv->nbdesc_rx * sizeof(struct dma_desc), -+ &priv->dd_rx_phy, GFP_KERNEL); -+ if (!priv->dd_rx) { -+ dev_err(priv->dev, "ERROR: cannot allocate DMA RX buffer"); -+ err = -ENOMEM; -+ goto dma_rx_error; -+ } -+ ddesc = priv->dd_rx; -+ for (i = 0; i < priv->nbdesc_rx; i++) { -+ sun8i_emac_rx_skb(ndev, i); -+ ddesc->next = cpu_to_le32(priv->dd_rx_phy + (i + 1) -+ * sizeof(struct dma_desc)); -+ ddesc++; -+ } -+ /* last descriptor point back to first one */ -+ ddesc--; -+ ddesc->next = cpu_to_le32(priv->dd_rx_phy); -+ -+ /* allocate/init TX ring */ -+ priv->dd_tx = dma_zalloc_coherent(priv->dev, -+ priv->nbdesc_tx * sizeof(struct dma_desc), -+ &priv->dd_tx_phy, GFP_KERNEL); -+ if (!priv->dd_tx) { -+ dev_err(priv->dev, "ERROR: cannot allocate DMA TX buffer"); -+ err = -ENOMEM; -+ goto dma_tx_error; -+ } -+ ddesc = priv->dd_tx; -+ for (i = 0; i < priv->nbdesc_tx; i++) { -+ ddesc->status = DCLEAN; -+ ddesc->ctl = 0; -+ ddesc->next = cpu_to_le32(priv->dd_tx_phy + (i + 1) -+ * sizeof(struct dma_desc)); -+ ddesc++; -+ } -+ /* last descriptor point back to first one */ -+ ddesc--; -+ ddesc->next = cpu_to_le32(priv->dd_tx_phy); -+ i--; -+ -+ priv->tx_slot = 0; -+ priv->tx_dirty = 0; -+ priv->rx_dirty = 0; -+ -+ /* write start of RX ring descriptor */ -+ writel(priv->dd_rx_phy, priv->base + EMAC_RX_DESC_LIST); -+ /* write start of TX ring descriptor */ -+ writel(priv->dd_tx_phy, priv->base + EMAC_TX_DESC_LIST); -+ -+ return 0; -+dma_tx_error: -+ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), -+ priv->dd_rx, priv->dd_rx_phy); -+dma_rx_error: -+ kfree(priv->txl); -+tx_error: -+ kfree(priv->rx_skb); -+rx_skb_error: -+ return err; -+} -+ -+static int sun8i_emac_open(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ int err; -+ u32 v; -+ -+ err = sun8i_emac_power(ndev); -+ if (err) -+ return err; -+ -+ err = request_irq(priv->irq, sun8i_emac_dma_interrupt, 0, -+ dev_name(priv->dev), ndev); -+ if (err) { -+ dev_err(priv->dev, "Cannot request IRQ: %d\n", err); -+ goto err_power; -+ } -+ -+ /* Set interface mode (and configure internal PHY on H3) */ -+ err = sun8i_emac_set_syscon(ndev); -+ if (err) -+ goto err_irq; -+ -+ /* Do SOFT RST */ -+ v = readl(priv->base + EMAC_BASIC_CTL1); -+ writel(v | 0x01, priv->base + EMAC_BASIC_CTL1); -+ -+ err = readl_poll_timeout(priv->base + EMAC_BASIC_CTL1, v, -+ !(v & 0x01), 100, 10000); -+ if (err) { -+ dev_err(priv->dev, "EMAC reset timeout\n"); -+ err = -EFAULT; -+ goto err_syscon; -+ } -+ -+ sun8i_emac_set_mdc(ndev); -+ -+ err = sun8i_emac_mdio_register(ndev); -+ if (err) -+ goto err_syscon; -+ -+ err = sun8i_emac_mdio_probe(ndev); -+ if (err) -+ goto err_syscon; -+ -+ /* DMA */ -+ v = (8 << 24);/* burst len */ -+ writel(v, priv->base + EMAC_BASIC_CTL1); -+ -+ writel(EMAC_RX_INT | EMAC_TX_INT, priv->base + EMAC_INT_EN); -+ -+ v = readl(priv->base + EMAC_RX_CTL0); -+ /* CHECK_CRC */ -+ if (ndev->features & NETIF_F_RXCSUM) -+ v |= EMAC_RX_DO_CRC; -+ else -+ v &= ~EMAC_RX_DO_CRC; -+ /* STRIP_FCS */ -+ if (ndev->features & NETIF_F_RXFCS) -+ v &= ~EMAC_RX_STRIP_FCS; -+ else -+ v |= EMAC_RX_STRIP_FCS; -+ writel(v, priv->base + EMAC_RX_CTL0); -+ -+ v = readl(priv->base + EMAC_TX_CTL1); -+ /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ -+ v |= BIT(1); -+ /* Undocumented bit (called TX_NEXT_FRM in BSP), the original comment is -+ * "Operating on second frame increase the performance -+ * especially when transmit store-and-forward is used." -+ */ -+ v |= BIT(2); -+ writel(v, priv->base + EMAC_TX_CTL1); -+ -+ v = readl(priv->base + EMAC_RX_CTL1); -+ /* RX_MD RX DMA reads data from RX DMA FIFO to host memory after a -+ * complete frame has been written to RX DMA FIFO -+ */ -+ v |= BIT(1); -+ writel(v, priv->base + EMAC_RX_CTL1); -+ -+ sun8i_emac_set_macaddr(priv, ndev->dev_addr, 0); -+ -+ err = sun8i_emac_alloc_rings(ndev); -+ if (err) { -+ netdev_err(ndev, "Fail to allocate rings\n"); -+ goto err_mdio; -+ } -+ -+ phy_start(ndev->phydev); -+ -+ sun8i_emac_start_rx(ndev); -+ sun8i_emac_start_tx(ndev); -+ -+ netif_napi_add(ndev, &priv->napi, sun8i_emac_poll, 64); -+ napi_enable(&priv->napi); -+ netif_start_queue(ndev); -+ ++ if (gmac->ephy_clk) ++ clk_disable_unprepare(gmac->ephy_clk); ++ if (gmac->rst_ephy) ++ reset_control_assert(gmac->rst_ephy); + return 0; -+err_mdio: -+ phy_disconnect(ndev->phydev); -+err_syscon: -+ sun8i_emac_unset_syscon(ndev); -+err_irq: -+ free_irq(priv->irq, ndev); -+err_power: -+ sun8i_emac_unpower(ndev); -+ return err; -+} -+ -+/* Clean the TX ring of any accepted skb for xmit */ -+static void sun8i_emac_tx_clean(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ int i; -+ struct dma_desc *ddesc; -+ int frame_len; -+ -+ for (i = 0; i < priv->nbdesc_tx; i++) { -+ if (priv->txl[i].skb) { -+ ddesc = priv->dd_tx + i; -+ frame_len = le32_to_cpu(ddesc->ctl) & 0x3FFF; -+ switch (priv->txl[i].map) { -+ case MAP_SINGLE: -+ dma_unmap_single(priv->dev, -+ le32_to_cpu(ddesc->buf_addr), -+ frame_len, DMA_TO_DEVICE); -+ break; -+ case MAP_PAGE: -+ dma_unmap_page(priv->dev, -+ le32_to_cpu(ddesc->buf_addr), -+ frame_len, DMA_TO_DEVICE); -+ break; -+ default: -+ dev_err(priv->dev, "Trying to free an empty slot\n"); -+ continue; -+ } -+ dev_kfree_skb_any(priv->txl[i].skb); -+ priv->txl[i].skb = NULL; -+ ddesc->ctl = 0; -+ ddesc->status = DCLEAN; -+ } -+ } -+ priv->tx_slot = 0; -+ priv->tx_dirty = 0; -+} -+ -+/* Clean the RX ring */ -+static void sun8i_emac_rx_clean(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ int i; -+ struct dma_desc *ddesc; -+ -+ /* clean RX ring */ -+ for (i = 0; i < priv->nbdesc_rx; i++) -+ if (priv->rx_skb[i]) { -+ ddesc = priv->dd_rx + i; -+ dma_unmap_single(priv->dev, -+ le32_to_cpu(ddesc->buf_addr), -+ DESC_BUF_MAX, DMA_FROM_DEVICE); -+ dev_kfree_skb_any(priv->rx_skb[i]); -+ priv->rx_skb[i] = NULL; -+ } -+} -+ -+static int sun8i_emac_stop(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ napi_disable(&priv->napi); -+ -+ sun8i_emac_stop_tx(ndev); -+ sun8i_emac_stop_rx(ndev); -+ -+ phy_stop(ndev->phydev); -+ phy_disconnect(ndev->phydev); -+ -+ sun8i_emac_mdio_unregister(ndev); -+ -+ sun8i_emac_unset_syscon(ndev); -+ -+ free_irq(priv->irq, ndev); -+ -+ sun8i_emac_rx_clean(ndev); -+ sun8i_emac_tx_clean(ndev); -+ -+ kfree(priv->rx_skb); -+ kfree(priv->txl); -+ -+ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), -+ priv->dd_rx, priv->dd_rx_phy); -+ dma_free_coherent(priv->dev, priv->nbdesc_tx * sizeof(struct dma_desc), -+ priv->dd_tx, priv->dd_tx_phy); -+ -+ sun8i_emac_unpower(ndev); -+ -+ return 0; -+} -+ -+static netdev_tx_t sun8i_emac_xmit(struct sk_buff *skb, struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct dma_desc *ddesc; -+ struct dma_desc *first; -+ int i = 0, rbd_first; -+ unsigned int len, fraglen, tlen; -+ u32 v; -+ int n; -+ int nf; -+ const skb_frag_t *frag; -+ int do_csum = 0; -+ -+ if (skb_put_padto(skb, ETH_ZLEN)) -+ return NETDEV_TX_OK; -+ len = skb_headlen(skb); -+ -+ n = skb_shinfo(skb)->nr_frags; -+ -+ if (skb->ip_summed == CHECKSUM_PARTIAL) { -+ do_csum = 1; -+ priv->estats.tx_hw_csum++; -+ } -+ netif_dbg(priv, tx_queued, ndev, "%s len=%u skblen=%u %x\n", __func__, -+ len, skb->len, -+ (skb->ip_summed == CHECKSUM_PARTIAL)); -+ -+ /* check for contigous space -+ * We need at least 1(skb->data) + n(numfrags) + 1(one clean slot) -+ */ -+ if (rb_tx_numfreedesc(ndev) < n + 2) { -+ dev_err_ratelimited(priv->dev, "BUG!: TX is full %d %d\n", -+ priv->tx_dirty, priv->tx_slot); -+ netif_stop_queue(ndev); -+ return NETDEV_TX_BUSY; -+ } -+ i = priv->tx_slot; -+ -+ ddesc = priv->dd_tx + i; -+ first = priv->dd_tx + i; -+ rbd_first = i; -+ -+ ddesc->buf_addr = dma_map_single(priv->dev, skb->data, len, -+ DMA_TO_DEVICE); -+ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { -+ dev_err(priv->dev, "ERROR: Cannot map buffer for DMA\n"); -+ goto xmit_error; -+ } -+ /* We cannot direcly use cpu_to_le32() after dma_map_single -+ * since dma_mapping_error use it -+ */ -+ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); -+ priv->txl[i].map = MAP_SINGLE; -+ priv->txl[i].skb = skb; -+ -+ tlen = len; -+ ddesc->ctl = le32_to_cpu(len); -+ /* Undocumented bit that make it works -+ * Without it, packets never be sent on H3 SoC -+ */ -+ ddesc->ctl |= EMAC_MAGIC_TX_BIT; -+ if (do_csum) -+ ddesc->ctl |= EMAC_TX_DO_CRC; -+ -+ /* handle fragmented skb, one descriptor per fragment */ -+ for (nf = 0; nf < n; nf++) { -+ frag = &skb_shinfo(skb)->frags[nf]; -+ rb_inc(&i, priv->nbdesc_tx); -+ priv->txl[i].skb = skb; -+ ddesc = priv->dd_tx + i; -+ fraglen = skb_frag_size(frag); -+ ddesc->ctl = le32_to_cpu(fraglen); -+ tlen += fraglen, -+ ddesc->ctl |= EMAC_MAGIC_TX_BIT; -+ if (do_csum) -+ ddesc->ctl |= EMAC_TX_DO_CRC; -+ -+ ddesc->buf_addr = skb_frag_dma_map(priv->dev, frag, 0, -+ fraglen, DMA_TO_DEVICE); -+ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { -+ dev_err(priv->dev, "Cannot map buffer for DMA\n"); -+ goto xmit_error; -+ } -+ /* Cannot directly use cpu_to_le32() after skb_frag_dma_map -+ * since dma_mapping_error use it -+ */ -+ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); -+ priv->txl[i].map = MAP_PAGE; -+ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; -+ } -+ -+ /* frame end */ -+ ddesc->ctl |= EMAC_DSC_TX_LAST; -+ /* We want an interrupt after transmission */ -+ ddesc->ctl |= EMAC_WANT_INT; -+ -+ rb_inc(&i, priv->nbdesc_tx); -+ -+ /* This line was previously after DMA start, but with that we hit a -+ * small race with complete_xmit() where we complete more data than -+ * sent. -+ * The packet is sent just after EMAC_COULD_BE_USED_BY_DMA flag set and -+ * complete_xmit fire just after before netdev_sent_queue(). -+ * This race could be observed only when overflowing a gigabit line. -+ */ -+ netdev_sent_queue(ndev, skb->len); -+ -+ /* frame begin */ -+ first->ctl |= EMAC_DSC_TX_FIRST; -+ wmb();/* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ -+ first->status = EMAC_COULD_BE_USED_BY_DMA; -+ priv->tx_slot = i; -+ -+ /* Trying to optimize this (recording DMA start/stop) seems -+ * to lead to errors. So we always start DMA. -+ */ -+ v = readl(priv->base + EMAC_TX_CTL1); -+ v |= EMAC_TX_DMA_START; -+ v |= EMAC_TX_DMA_EN; -+ writel_relaxed(v, priv->base + EMAC_TX_CTL1); -+ -+ if (rb_tx_numfreedesc(ndev) < MAX_SKB_FRAGS + 1) { -+ netif_stop_queue(ndev); -+ priv->estats.tx_stop_queue++; -+ } -+ priv->estats.tx_used_desc = rb_tx_numfreedesc(ndev); -+ priv->ndev->stats.tx_packets++; -+ priv->ndev->stats.tx_bytes += tlen; -+ -+ return NETDEV_TX_OK; -+ -+xmit_error: -+ /* destroy skb and return TX OK Documentation/DMA-API-HOWTO.txt */ -+ /* clean descritors from rbd_first to i */ -+ ddesc->ctl = 0; -+ /* setting status to DCLEAN is the last value to be set */ -+ wmb(); -+ ddesc->status = DCLEAN; -+ do { -+ ddesc = priv->dd_tx + rbd_first; -+ ddesc->ctl = 0; -+ /* setting status to DCLEAN is the last value to be set */ -+ wmb(); -+ ddesc->status = DCLEAN; -+ rb_inc(&rbd_first, priv->nbdesc_tx); -+ } while (rbd_first != i); -+ dev_kfree_skb_any(skb); -+ return NETDEV_TX_OK; +} + -+static int sun8i_emac_change_mtu(struct net_device *ndev, int new_mtu) ++static int sun8i_power_phy(struct stmmac_priv *priv) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ int max_mtu; -+ -+ dev_info(priv->dev, "%s set MTU to %d\n", __func__, new_mtu); -+ -+ if (netif_running(ndev)) { -+ dev_err(priv->dev, "%s: must be stopped to change its MTU\n", -+ ndev->name); -+ return -EBUSY; -+ } -+ -+ max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN); -+ -+ if ((new_mtu < 68) || (new_mtu > max_mtu)) { -+ dev_err(priv->dev, "%s: invalid MTU, max MTU is: %d\n", -+ ndev->name, max_mtu); -+ return -EINVAL; -+ } -+ -+ ndev->mtu = new_mtu; -+ netdev_update_features(ndev); -+ return 0; -+} -+ -+static int sun8i_emac_set_features(struct net_device *ndev, -+ netdev_features_t features) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ u32 v; -+ -+ v = readl(priv->base + EMAC_BASIC_CTL0); -+ if (features & NETIF_F_LOOPBACK && netif_running(ndev)) { -+ netif_info(priv, hw, ndev, "Set loopback features"); -+ v |= BIT(1); -+ } else { -+ netif_info(priv, hw, ndev, "Unset loopback features"); -+ v &= ~BIT(1); -+ } -+ writel(v, priv->base + EMAC_BASIC_CTL0); -+ -+ v = readl(priv->base + EMAC_RX_CTL0); -+ if (features & NETIF_F_RXCSUM) { -+ v |= EMAC_RX_DO_CRC; -+ netif_info(priv, hw, ndev, "Doing RX CRC check by hardware"); -+ } else { -+ v &= ~EMAC_RX_DO_CRC; -+ netif_info(priv, hw, ndev, "No RX CRC check by hardware"); -+ } -+ if (features & NETIF_F_RXFCS) { -+ v &= ~EMAC_RX_STRIP_FCS; -+ netif_info(priv, hw, ndev, "Keep FCS"); -+ } else { -+ v |= EMAC_RX_STRIP_FCS; -+ netif_info(priv, hw, ndev, "Strip FCS"); -+ } -+ writel(v, priv->base + EMAC_RX_CTL0); -+ -+ netif_dbg(priv, drv, ndev, "%s %llx %x\n", __func__, features, v); -+ -+ return 0; -+} -+ -+static void sun8i_emac_set_rx_mode(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ u32 v = 0; -+ int i = 0; -+ struct netdev_hw_addr *ha; -+ -+ /* Receive all multicast frames */ -+ v |= BIT(16); -+ /* Receive all control frames */ -+ v |= BIT(13); -+ if (ndev->flags & IFF_PROMISC) -+ v |= BIT(1); -+ if (netdev_uc_count(ndev) > 7) { -+ v |= BIT(1); -+ } else { -+ netdev_for_each_uc_addr(ha, ndev) { -+ i++; -+ sun8i_emac_set_macaddr(priv, ha->addr, i); -+ } -+ } -+ writel(v, priv->base + EMAC_RX_FRM_FLT); -+} -+ -+static void sun8i_emac_tx_timeout(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ netdev_err(ndev, "%s\n", __func__); -+ -+ sun8i_emac_stop_tx(ndev); -+ -+ sun8i_emac_tx_clean(ndev); -+ -+ /* write start of the new TX ring descriptor */ -+ writel(priv->dd_tx_phy, priv->base + EMAC_TX_DESC_LIST); -+ -+ sun8i_emac_start_tx(ndev); -+ -+ netdev_reset_queue(ndev); -+ -+ priv->estats.tx_timeout++; -+ ndev->stats.tx_errors++; -+ netif_wake_queue(ndev); -+} -+ -+static int sun8i_emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) -+{ -+ struct phy_device *phydev = ndev->phydev; ++ struct sunxi_priv_data *gmac = priv->plat->bsp_priv; ++ int ret; + -+ if (!netif_running(ndev)) -+ return -EINVAL; ++ ret = sun8i_dwmac_power_internal_phy(priv); ++ if (ret) ++ return ret; + -+ if (!phydev) -+ return -ENODEV; ++ ret = sun8i_dwmac_set_syscon(priv); ++ if (ret) ++ goto error_phy; + -+ return phy_mii_ioctl(phydev, rq, cmd); -+} -+ -+static int sun8i_emac_check_if_running(struct net_device *ndev) -+{ -+ if (!netif_running(ndev)) -+ return -EINVAL; ++ ret = sun8i_dwmac_reset(priv); ++ if (ret) ++ goto error_phy; + return 0; -+} -+ -+static int sun8i_emac_get_sset_count(struct net_device *ndev, int sset) -+{ -+ switch (sset) { -+ case ETH_SS_STATS: -+ return ARRAY_SIZE(estats_str); -+ } -+ return -EOPNOTSUPP; -+} + -+static int sun8i_emac_ethtool_get_settings(struct net_device *ndev, -+ struct ethtool_cmd *cmd) -+{ -+ struct phy_device *phy = ndev->phydev; -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ if (!phy) { -+ netdev_err(ndev, "%s: %s: PHY is not registered\n", -+ __func__, ndev->name); -+ return -ENODEV; -+ } -+ -+ if (!netif_running(ndev)) { -+ dev_err(priv->dev, "interface disabled: we cannot track link speed / duplex setting\n"); -+ return -EBUSY; -+ } -+ -+ return phy_ethtool_gset(phy, cmd); -+} -+ -+static int sun8i_emac_ethtool_set_settings(struct net_device *ndev, -+ struct ethtool_cmd *cmd) -+{ -+ struct phy_device *phy = ndev->phydev; -+ -+ return phy_ethtool_sset(phy, cmd); -+} -+ -+static void sun8i_emac_ethtool_getdrvinfo(struct net_device *ndev, -+ struct ethtool_drvinfo *info) -+{ -+ strlcpy(info->driver, "sun8i_emac", sizeof(info->driver)); -+ strcpy(info->version, "00"); -+ info->fw_version[0] = '\0'; -+} -+ -+static void sun8i_emac_ethtool_stats(struct net_device *ndev, -+ struct ethtool_stats *dummy, u64 *data) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ memcpy(data, &priv->estats, -+ sun8i_emac_get_sset_count(ndev, ETH_SS_STATS) * sizeof(u64)); -+} -+ -+static void sun8i_emac_ethtool_strings(struct net_device *dev, u32 stringset, -+ u8 *buffer) -+{ -+ switch (stringset) { -+ case ETH_SS_STATS: -+ memcpy(buffer, &estats_str, sizeof(estats_str)); -+ break; -+ } -+} -+ -+static u32 sun8i_emac_ethtool_getmsglevel(struct net_device *ndev) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ return priv->msg_enable; -+} -+ -+static void sun8i_emac_ethtool_setmsglevel(struct net_device *ndev, u32 level) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ priv->msg_enable = level; -+} -+ -+static void sun8i_emac_get_pauseparam(struct net_device *ndev, -+ struct ethtool_pauseparam *pause) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ pause->rx_pause = 0; -+ pause->tx_pause = 0; -+ pause->autoneg = ndev->phydev->autoneg; -+ -+ if (priv->flow_ctrl & EMAC_FLOW_RX) -+ pause->rx_pause = 1; -+ if (priv->flow_ctrl & EMAC_FLOW_TX) -+ pause->tx_pause = 1; -+} -+ -+static int sun8i_emac_set_pauseparam(struct net_device *ndev, -+ struct ethtool_pauseparam *pause) -+{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ struct phy_device *phy = ndev->phydev; -+ int new_pause = 0; -+ int ret = 0; -+ -+ if (pause->rx_pause) -+ new_pause |= EMAC_FLOW_RX; -+ if (pause->tx_pause) -+ new_pause |= EMAC_FLOW_TX; -+ -+ priv->flow_ctrl = new_pause; -+ phy->autoneg = pause->autoneg; -+ -+ if (phy->autoneg) { -+ if (netif_running(ndev)) -+ ret = phy_start_aneg(phy); -+ } else { -+ sun8i_emac_flow_ctrl(priv, phy->duplex, priv->flow_ctrl); -+ } ++error_phy: ++ sun8i_dwmac_unset_syscon(gmac); ++ sun8i_dwmac_unpower_internal_phy(gmac); + return ret; +} + -+static void sun8i_emac_ethtool_get_ringparam(struct net_device *ndev, -+ struct ethtool_ringparam *ring) ++static void sun8i_unpower_phy(struct sunxi_priv_data *gmac) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ -+ ring->rx_pending = priv->nbdesc_rx; -+ ring->tx_pending = priv->nbdesc_tx; ++ sun8i_dwmac_unset_syscon(gmac); ++ sun8i_dwmac_unpower_internal_phy(gmac); +} + -+static int sun8i_emac_ethtool_set_ringparam(struct net_device *ndev, -+ struct ethtool_ringparam *ring) ++static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) +{ -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ int err; ++ struct sunxi_priv_data *gmac = priv; + -+ if (ring->rx_max_pending || ring->rx_mini_max_pending || -+ ring->rx_jumbo_max_pending || ring->rx_mini_pending || -+ ring->rx_jumbo_pending || ring->tx_max_pending) -+ return -EINVAL; -+ -+ if (ring->tx_pending < MAX_SKB_FRAGS + 1) { -+ netdev_err(ndev, "The number of TX descriptors is too low"); -+ return -EINVAL; -+ } ++ sun8i_unpower_phy(gmac); + -+ sun8i_emac_stop_tx(ndev); -+ sun8i_emac_stop_rx(ndev); ++ clk_disable_unprepare(gmac->tx_clk); + -+ sun8i_emac_rx_clean(ndev); -+ sun8i_emac_tx_clean(ndev); -+ -+ kfree(priv->rx_skb); -+ kfree(priv->txl); -+ -+ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), -+ priv->dd_rx, priv->dd_rx_phy); -+ dma_free_coherent(priv->dev, priv->nbdesc_tx * sizeof(struct dma_desc), -+ priv->dd_tx, priv->dd_tx_phy); -+ -+ priv->nbdesc_rx = ring->rx_pending; -+ priv->nbdesc_tx = ring->tx_pending; -+ err = sun8i_emac_alloc_rings(ndev); -+ if (err) { -+ /* Fatal error, we cannot re start */ -+ netdev_err(ndev, "Fail to allocate rings\n"); -+ return -EFAULT; -+ } -+ -+ sun8i_emac_start_rx(ndev); -+ sun8i_emac_start_tx(ndev); -+ -+ netif_start_queue(ndev); -+ -+ netdev_info(ndev, "Ring Param settings: rx: %d, tx %d\n", -+ ring->rx_pending, ring->tx_pending); -+ return 0; ++ if (gmac->regulator) ++ regulator_disable(gmac->regulator); +} + -+static const struct ethtool_ops sun8i_emac_ethtool_ops = { -+ .begin = sun8i_emac_check_if_running, -+ .get_settings = sun8i_emac_ethtool_get_settings, -+ .set_settings = sun8i_emac_ethtool_set_settings, -+ .get_link = ethtool_op_get_link, -+ .get_pauseparam = sun8i_emac_get_pauseparam, -+ .set_pauseparam = sun8i_emac_set_pauseparam, -+ .get_ethtool_stats = sun8i_emac_ethtool_stats, -+ .get_strings = sun8i_emac_ethtool_strings, -+ .get_sset_count = sun8i_emac_get_sset_count, -+ .get_drvinfo = sun8i_emac_ethtool_getdrvinfo, -+ .get_msglevel = sun8i_emac_ethtool_getmsglevel, -+ .set_msglevel = sun8i_emac_ethtool_setmsglevel, -+ .get_ringparam = sun8i_emac_ethtool_get_ringparam, -+ .set_ringparam = sun8i_emac_ethtool_set_ringparam, -+}; -+ -+static const struct net_device_ops sun8i_emac_netdev_ops = { -+ .ndo_init = sun8i_emac_init, -+ .ndo_open = sun8i_emac_open, -+ .ndo_start_xmit = sun8i_emac_xmit, -+ .ndo_stop = sun8i_emac_stop, -+ .ndo_change_mtu = sun8i_emac_change_mtu, -+ .ndo_set_features = sun8i_emac_set_features, -+ .ndo_set_rx_mode = sun8i_emac_set_rx_mode, -+ .ndo_tx_timeout = sun8i_emac_tx_timeout, -+ .ndo_do_ioctl = sun8i_emac_ioctl, -+ .ndo_set_mac_address = eth_mac_addr, ++static const struct stmmac_ops sun8i_dwmac_ops = { ++ .core_init = sun8i_dwmac_core_init, ++ .dump_regs = sun8i_dwmac_dump_mac_regs, ++ .rx_ipc = sun8i_dwmac_rx_ipc_enable, ++ .set_filter = sun8i_dwmac_set_filter, ++ .flow_ctrl = sun8i_dwmac_flow_ctrl, ++ .set_umac_addr = sun8i_dwmac_set_umac_addr, ++ .get_umac_addr = sun8i_dwmac_get_umac_addr, +}; + -+/* No locking in this function since it is guaranteed to be run once and -+ * do actions only done here: -+ * - Scheduling NAPI -+ * - Stopping interrupts -+ * - Updating stats -+ * -+ * Even when not enabled via EMAC_INT_EN, some interrupt could fire, so we need -+ * to handle all of them. -+ * Interrupts know to fire when not enabled are: -+ * - EMAC_TX_DMA_STOP_INT -+ * - EMAC_TX_BUF_UA_INT -+ * - EMAC_TX_EARLY_INT -+ * - EMAC_RX_BUF_UA_INT -+ * - EMAC_RX_EARLY_INT -+ */ -+static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id) ++static struct mac_device_info *sun8i_dwmac_setup(struct stmmac_priv *priv) +{ -+ struct net_device *ndev = dev_id; -+ struct sun8i_emac_priv *priv = netdev_priv(ndev); -+ u32 v, u; -+ -+ v = readl(priv->base + EMAC_INT_STA); -+ -+ /* When this bit is asserted, a frame transmission is completed. */ -+ if (v & EMAC_TX_INT) { -+ priv->estats.tx_int++; -+ writel(0, priv->base + EMAC_INT_EN); -+ napi_schedule(&priv->napi); -+ } -+ -+ /* When this bit is asserted, the TX DMA FSM is stopped. -+ * For the moment only a call to tx_timeout cause this interrupt -+ * to fire. -+ */ -+ if (v & EMAC_TX_DMA_STOP_INT) -+ priv->estats.tx_dma_stop++; -+ -+ /* When this asserted, the TX DMA can not acquire next TX descriptor -+ * and TX DMA FSM is suspended. -+ */ -+ if (v & EMAC_TX_BUF_UA_INT) { -+ priv->estats.tx_dma_ua++; -+ writel(0, priv->base + EMAC_INT_EN); -+ napi_schedule(&priv->napi); -+ } -+ -+ if (v & EMAC_TX_TIMEOUT_INT) -+ priv->estats.tx_timeout_int++; -+ -+ if (v & EMAC_TX_UNDERFLOW_INT) -+ priv->estats.tx_underflow_int++; -+ -+ /* When this bit asserted , the frame is transmitted to FIFO totally. */ -+ if (v & EMAC_TX_EARLY_INT) -+ priv->estats.tx_early_int++; -+ -+ /* When this bit is asserted, a frame reception is completed */ -+ if (v & EMAC_RX_INT) { -+ priv->estats.rx_int++; -+ writel(0, priv->base + EMAC_INT_EN); -+ napi_schedule(&priv->napi); -+ } -+ -+ /* When this asserted, the RX DMA can not acquire next RX descriptor -+ * and RX DMA FSM is suspended. -+ */ -+ if (v & EMAC_RX_BUF_UA_INT) { -+ u = readl(priv->base + EMAC_RX_CTL1); -+ writel(u | EMAC_RX_DMA_START, priv->base + EMAC_RX_CTL1); -+ priv->estats.rx_dma_ua++; -+ } -+ -+ /* Same as TX DMA STOP, but never hit it */ -+ if (v & EMAC_RX_DMA_STOP_INT) -+ priv->estats.rx_dma_stop++; -+ -+ if (v & EMAC_RX_TIMEOUT_INT) -+ priv->estats.rx_timeout_int++; -+ -+ if (v & EMAC_RX_OVERFLOW_INT) -+ priv->estats.rx_overflow_int++; -+ -+ if (v & EMAC_RX_EARLY_INT) -+ priv->estats.rx_early_int++; -+ -+ if (v & EMAC_RGMII_STA_INT) -+ priv->estats.rgmii_state_int++; -+ -+ /* the datasheet state those register as read-only -+ * but nothing work(freeze) without writing to it -+ */ -+ writel(v, priv->base + EMAC_INT_STA); ++ struct mac_device_info *mac; ++ int ret; + -+ return IRQ_HANDLED; ++ mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL); ++ if (!mac) ++ return NULL; ++ ++ ret = sun8i_power_phy(priv); ++ if (ret) ++ return NULL; ++ ++ mac->pcsr = priv->ioaddr; ++ mac->mac = &sun8i_dwmac_ops; ++ mac->dma = &sun8i_dwmac_dma_ops; ++ ++ mac->link.port = 0; ++ mac->link.duplex = BIT(0); ++ mac->link.speed = 1; ++ mac->mii.addr = EMAC_MDIO_CMD; ++ mac->mii.data = EMAC_MDIO_DATA; ++ mac->mii.reg_shift = 4; ++ mac->mii.reg_mask = GENMASK(8, 4); ++ mac->mii.addr_shift = 12; ++ mac->mii.addr_mask = GENMASK(16, 12); ++ mac->mii.clk_csr_shift = 20; ++ mac->mii.clk_csr_mask = GENMASK(22, 20); ++ mac->unicast_filter_entries = 8; ++ ++ /* Synopsys Id is not available */ ++ priv->synopsys_id = 0; ++ ++ return mac; +} + -+static int sun8i_emac_probe(struct platform_device *pdev) ++static int sun8i_dwmac_probe(struct platform_device *pdev) +{ -+ struct device_node *node = pdev->dev.of_node; -+ struct sun8i_emac_priv *priv; -+ struct net_device *ndev; -+ struct resource *res; ++ struct plat_stmmacenet_data *plat_dat; ++ struct stmmac_resources stmmac_res; ++ struct sunxi_priv_data *gmac; ++ struct device *dev = &pdev->dev; + int ret; + -+ ndev = alloc_etherdev(sizeof(*priv)); -+ if (!ndev) -+ return -ENOMEM; ++ ret = stmmac_get_platform_resources(pdev, &stmmac_res); ++ if (ret) ++ return ret; + -+ SET_NETDEV_DEV(ndev, &pdev->dev); -+ priv = netdev_priv(ndev); -+ platform_set_drvdata(pdev, ndev); ++ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); ++ if (IS_ERR(plat_dat)) ++ return PTR_ERR(plat_dat); + -+ priv->variant = of_device_get_match_data(&pdev->dev); -+ if (!priv->variant) { ++ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); ++ if (!gmac) ++ return -ENOMEM; ++ ++ gmac->variant = of_device_get_match_data(&pdev->dev); ++ if (!gmac->variant) { + dev_err(&pdev->dev, "Missing sun8i-emac variant\n"); + return -EINVAL; + } + -+ priv->mdio_node = of_get_child_by_name(node, "mdio"); -+ if (!priv->mdio_node) { -+ netdev_err(ndev, "Could not find a MDIO node\n"); -+ return -EINVAL; ++ gmac->tx_clk = devm_clk_get(dev, "stmmaceth"); ++ if (IS_ERR(gmac->tx_clk)) { ++ dev_err(dev, "could not get tx clock\n"); ++ return PTR_ERR(gmac->tx_clk); + } + -+ priv->phy_node = of_parse_phandle(node, "phy-handle", 0); -+ if (!priv->phy_node) { -+ netdev_err(ndev, "No associated PHY\n"); -+ return -EINVAL; ++ /* Optional regulator for PHY */ ++ gmac->regulator = devm_regulator_get_optional(dev, "phy"); ++ if (IS_ERR(gmac->regulator)) { ++ if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_info(dev, "no regulator found\n"); ++ gmac->regulator = NULL; + } + -+ priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ gmac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "syscon"); -+ if (IS_ERR(priv->regmap)) { -+ ret = PTR_ERR(priv->regmap); ++ if (IS_ERR(gmac->regmap)) { ++ ret = PTR_ERR(gmac->regmap); + dev_err(&pdev->dev, "unable to map SYSCON:%d\n", ret); + return ret; + } + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ priv->base = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(priv->base)) { -+ ret = PTR_ERR(priv->base); -+ dev_err(&pdev->dev, "Cannot request MMIO: %d\n", ret); -+ return ret; -+ } -+ -+ priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb"); -+ 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->rst_mac = devm_reset_control_get_optional(&pdev->dev, "ahb"); -+ if (IS_ERR(priv->rst_mac)) { -+ ret = PTR_ERR(priv->rst_mac); -+ if (ret == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ dev_info(&pdev->dev, "No MAC reset control found %d\n", ret); -+ priv->rst_mac = NULL; -+ } -+ -+ priv->phy_interface = of_get_phy_mode(node); -+ if (priv->phy_interface < 0) { -+ netdev_err(ndev, "PHY interface mode node unspecified\n"); -+ return priv->phy_interface; -+ } -+ -+ switch (priv->phy_interface) { -+ case PHY_INTERFACE_MODE_MII: -+ if (!priv->variant->support_mii) -+ return -EINVAL; -+ break; -+ case PHY_INTERFACE_MODE_RMII: -+ if (!priv->variant->support_rmii) -+ return -EINVAL; -+ break; -+ case PHY_INTERFACE_MODE_RGMII: -+ if (!priv->variant->support_rgmii) -+ return -EINVAL; -+ break; -+ default: -+ netdev_err(ndev, "Unsupported interface mode: %s", -+ phy_modes(priv->phy_interface)); -+ return -EINVAL; -+ } -+ -+ if (priv->phy_interface == priv->variant->internal_phy) -+ priv->use_internal_phy = true; -+ -+ if (priv->use_internal_phy) { -+ priv->ephy_clk = of_clk_get(priv->phy_node, 0); -+ if (IS_ERR(priv->ephy_clk)) { -+ ret = PTR_ERR(priv->ephy_clk); ++ plat_dat->interface = of_get_phy_mode(dev->of_node); ++ if (plat_dat->interface == gmac->variant->internal_phy) { ++ dev_info(&pdev->dev, "Will use internal PHY\n"); ++ gmac->use_internal_phy = true; ++ gmac->ephy_clk = of_clk_get(plat_dat->phy_node, 0); ++ if (IS_ERR(gmac->ephy_clk)) { ++ ret = PTR_ERR(gmac->ephy_clk); + dev_err(&pdev->dev, "Cannot get EPHY clock err=%d\n", + ret); -+ goto probe_err; ++ return -EINVAL; + } + -+ priv->rst_ephy = of_reset_control_get(priv->phy_node, 0); -+ if (IS_ERR(priv->rst_ephy)) { -+ ret = PTR_ERR(priv->rst_ephy); ++ gmac->rst_ephy = of_reset_control_get(plat_dat->phy_node, NULL); ++ if (IS_ERR(gmac->rst_ephy)) { ++ ret = PTR_ERR(gmac->rst_ephy); + if (ret == -EPROBE_DEFER) -+ goto probe_err; -+ dev_info(&pdev->dev, -+ "No EPHY reset control found %d\n", ret); -+ priv->rst_ephy = NULL; ++ return ret; ++ dev_err(&pdev->dev, "No EPHY reset control found %d\n", ++ ret); ++ return -EINVAL; + } ++ } else { ++ dev_info(&pdev->dev, "Will use external PHY\n"); ++ gmac->use_internal_phy = false; + } + -+ priv->irq = platform_get_irq(pdev, 0); -+ if (priv->irq < 0) { -+ ret = priv->irq; -+ dev_err(&pdev->dev, "Cannot claim IRQ: %d\n", ret); -+ goto probe_err; -+ } -+ -+ spin_lock_init(&priv->tx_lock); -+ -+ ndev->netdev_ops = &sun8i_emac_netdev_ops; -+ ndev->ethtool_ops = &sun8i_emac_ethtool_ops; -+ -+ priv->ndev = ndev; -+ priv->dev = &pdev->dev; -+ -+ ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA; -+ ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | -+ NETIF_F_RXCSUM; -+ ndev->features |= ndev->hw_features; -+ ndev->hw_features |= NETIF_F_RXFCS; -+ ndev->hw_features |= NETIF_F_RXALL; -+ ndev->hw_features |= NETIF_F_LOOPBACK; -+ ndev->priv_flags |= IFF_UNICAST_FLT; -+ -+ ndev->watchdog_timeo = msecs_to_jiffies(5000); -+ netif_carrier_off(ndev); -+ -+ /* Benched on OPIPC with 100M, setting more than 256 does not give any -+ * perf boost ++ /* platform data specifying hardware features and callbacks. ++ * hardware features were copied from Allwinner drivers. + */ -+ priv->nbdesc_rx = 128; -+ priv->nbdesc_tx = 256; -+ -+ ret = register_netdev(ndev); -+ if (ret) { -+ dev_err(&pdev->dev, "ERROR: Register %s failed\n", ndev->name); -+ goto probe_err; -+ } ++ plat_dat->rx_coe = STMMAC_RX_COE_TYPE2; ++ plat_dat->tx_coe = 1; ++ plat_dat->has_sun8i = true; ++ plat_dat->bsp_priv = gmac; ++ plat_dat->init = sun8i_dwmac_init; ++ plat_dat->exit = sun8i_dwmac_exit; ++ plat_dat->setup = sun8i_dwmac_setup; ++ ++ ret = sun8i_dwmac_init(pdev, plat_dat->bsp_priv); ++ if (ret) ++ return ret; + -+ return 0; ++ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); ++ if (ret) ++ sun8i_dwmac_exit(pdev, plat_dat->bsp_priv); + -+probe_err: -+ free_netdev(ndev); + return ret; +} + -+static int sun8i_emac_remove(struct platform_device *pdev) -+{ -+ struct net_device *ndev = platform_get_drvdata(pdev); -+ -+ unregister_netdev(ndev); -+ platform_set_drvdata(pdev, NULL); -+ free_netdev(ndev); -+ -+ return 0; -+} -+ -+static const struct of_device_id sun8i_emac_of_match_table[] = { -+ { .compatible = "allwinner,sun8i-a83t-emac", -+ .data = &emac_variant_a83t }, ++static const struct of_device_id sun8i_dwmac_match[] = { + { .compatible = "allwinner,sun8i-h3-emac", -+ .data = &emac_variant_h3 }, ++ .data = &emac_variant_h3 }, ++ { .compatible = "allwinner,sun8i-a83t-emac", ++ .data = &emac_variant_a83t }, + { .compatible = "allwinner,sun50i-a64-emac", -+ .data = &emac_variant_a64 }, -+ {} ++ .data = &emac_variant_a64 }, ++ { } +}; -+MODULE_DEVICE_TABLE(of, sun8i_emac_of_match_table); -+ -+static struct platform_driver sun8i_emac_driver = { -+ .probe = sun8i_emac_probe, -+ .remove = sun8i_emac_remove, -+ .driver = { -+ .name = "sun8i-emac", -+ .of_match_table = sun8i_emac_of_match_table, ++MODULE_DEVICE_TABLE(of, sun8i_dwmac_match); ++ ++static struct platform_driver sun8i_dwmac_driver = { ++ .probe = sun8i_dwmac_probe, ++ .remove = stmmac_pltfr_remove, ++ .driver = { ++ .name = "sun8i-dwmac", ++ .pm = &stmmac_pltfr_pm_ops, ++ .of_match_table = sun8i_dwmac_match, + }, +}; ++module_platform_driver(sun8i_dwmac_driver); + -+module_platform_driver(sun8i_emac_driver); -+ -+MODULE_DESCRIPTION("sun8i Ethernet driver"); ++MODULE_AUTHOR("Corentin Labbe <clabbe.montjoie@gmail.com>"); ++MODULE_DESCRIPTION("Allwinner sun8i DWMAC specific glue layer"); +MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Corentin Labbe <clabbe.montjoie@gmail.com"); --- -2.9.3 - -From 1d9ea1f4df27f3a2e2ed7094852a23a4dd45e2be Mon Sep 17 00:00:00 2001 -From: Corentin LABBE <clabbe.montjoie@gmail.com> -Date: Fri, 7 Oct 2016 10:25:49 +0200 -Subject: [PATCH 2/8] MAINTAINERS: Add myself as maintainer of sun8i-emac - -This patch add myself as maintainer of the sun8i-emac driver. - -Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> ---- - MAINTAINERS | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/MAINTAINERS b/MAINTAINERS -index 411e3b8..e99a087 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -590,6 +590,12 @@ S: Maintained - F: Documentation/i2c/busses/i2c-ali1563 - F: drivers/i2c/busses/i2c-ali1563.c +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 856ac57..05e8018 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -177,6 +177,17 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv) + else if ((clk_rate >= CSR_F_250M) && (clk_rate < CSR_F_300M)) + priv->clk_csr = STMMAC_CSR_250_300M; + } ++ ++ if (priv->plat->has_sun8i) { ++ if (clk_rate > 160000000) ++ priv->clk_csr = 0x03; ++ else if (clk_rate > 80000000) ++ priv->clk_csr = 0x02; ++ else if (clk_rate > 40000000) ++ priv->clk_csr = 0x01; ++ else ++ priv->clk_csr = 0; ++ } + } -+ALLWINNER SUN8I-EMAC ETHERNET DRIVER -+M: Corentin Labbe <clabbe.montjoie@gmail.com> -+L: netdev@vger.kernel.org -+S: Maintained -+F: drivers/net/ethernet/allwinner/sun8i-emac.c -+ - ALLWINNER SECURITY SYSTEM - M: Corentin Labbe <clabbe.montjoie@gmail.com> - L: linux-crypto@vger.kernel.org --- -2.9.3 - -From 6b3093e31b877f39475454b5af3b820dbb3a4f73 Mon Sep 17 00:00:00 2001 -From: Corentin LABBE <clabbe.montjoie@gmail.com> -Date: Fri, 7 Oct 2016 10:25:50 +0200 -Subject: [PATCH 3/8] ARM: sun8i: dt: Add DT bindings documentation for - Allwinner sun8i-emac - -This patch adds documentation for Device-Tree bindings for the -Allwinner sun8i-emac driver. - -Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> ---- - .../bindings/net/allwinner,sun8i-emac.txt | 70 ++++++++++++++++++++++ - 1 file changed, 70 insertions(+) - create mode 100644 Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt - -diff --git a/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt b/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt -new file mode 100644 -index 0000000..92e4ef3b ---- /dev/null -+++ b/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt -@@ -0,0 +1,70 @@ -+* Allwinner sun8i EMAC ethernet controller -+ -+Required properties: -+- compatible: should be one of the following string: -+ "allwinner,sun8i-a83t-emac" -+ "allwinner,sun8i-h3-emac" -+ "allwinner,sun50i-a64-emac" -+- reg: address and length of the register for the device. -+- syscon: A phandle to the syscon of the SoC -+- interrupts: interrupt for the device -+- clocks: A phandle to the reference clock for this device -+- clock-names: should be "ahb" -+- resets: A phandle to the reset control for this device -+- reset-names: should be "ahb" -+- phy-mode: See ethernet.txt -+- phy-handle: See ethernet.txt -+- #address-cells: shall be 1 -+- #size-cells: shall be 0 -+ -+Optional properties: -+- allwinner,tx-delay: TX clock delay chain value. Range value is 0-0x07. Default is 0) -+- allwinner,rx-delay: RX clock delay chain value. Range value is 0-0x1F. Default is 0) -+Both delay properties does not have units, there are arbitrary value. -+The TX/RX clock delay chain settings are board specific and could be found -+in vendor FEX files. -+ -+Optional properties for "allwinner,sun8i-h3-emac": -+- allwinner,leds-active-low: EPHY LEDs are active low -+ -+Required child node of emac: -+- mdio bus node: should be named mdio -+ -+Required properties of the mdio node: -+- #address-cells: shall be 1 -+- #size-cells: shall be 0 -+ -+The device node referenced by "phy" or "phy-handle" should be a child node -+of the mdio node. See phy.txt for the generic PHY bindings. -+ -+Required properties of the phy node with "allwinner,sun8i-h3-emac": -+- clocks: an extra phandle to the reference clock for the EPHY -+- resets: an extra phandle to the reset control for the EPHY -+ -+Example: -+ -+emac: ethernet@01c0b000 { -+ compatible = "allwinner,sun8i-h3-emac"; -+ syscon = <&syscon>; -+ reg = <0x01c0b000 0x104>; -+ interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>; -+ resets = <&ccu RST_BUS_EMAC>; -+ reset-names = "ahb"; -+ clocks = <&ccu CLK_BUS_EMAC>; -+ clock-names = "ahb"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ phy = <&int_mii_phy>; -+ phy-mode = "mii"; -+ allwinner,leds-active-low; -+ mdio: mdio { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ int_mii_phy: ethernet-phy@1 { -+ reg = <1>; -+ clocks = <&ccu CLK_BUS_EPHY>; -+ resets = <&ccu RST_BUS_EPHY>; -+ }; + static void print_pkt(unsigned char *buf, int len) +@@ -697,6 +708,10 @@ static void stmmac_adjust_link(struct net_device *dev) + if (phydev->link) { + u32 ctrl = readl(priv->ioaddr + MAC_CTRL_REG); + ++ /* disable loopback */ ++ if (priv->plat->has_sun8i) ++ ctrl &= ~BIT(1); ++ + /* Now we make sure that we can be in full duplex mode. + * If not, we operate in half-duplex mode. */ + if (phydev->duplex != priv->oldduplex) { +@@ -714,6 +729,8 @@ static void stmmac_adjust_link(struct net_device *dev) + + if (phydev->speed != priv->speed) { + new_state = 1; ++ if (priv->plat->has_sun8i) ++ ctrl &= ~GENMASK(3, 2); + switch (phydev->speed) { + case 1000: + if (priv->plat->has_gmac || +@@ -725,6 +742,8 @@ static void stmmac_adjust_link(struct net_device *dev) + priv->plat->has_gmac4) { + ctrl |= priv->hw->link.port; + ctrl |= priv->hw->link.speed; ++ } else if (priv->plat->has_sun8i) { ++ ctrl |= 3 << 2; + } else { + ctrl &= ~priv->hw->link.port; + } +@@ -734,6 +753,8 @@ static void stmmac_adjust_link(struct net_device *dev) + priv->plat->has_gmac4) { + ctrl |= priv->hw->link.port; + ctrl &= ~(priv->hw->link.speed); ++ } else if (priv->plat->has_sun8i) { ++ ctrl |= 2 << 2; + } else { + ctrl &= ~priv->hw->link.port; + } +@@ -1702,7 +1723,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) + /* Enable the MAC Rx/Tx */ + if (priv->synopsys_id >= DWMAC_CORE_4_00) + stmmac_dwmac4_set_mac(priv->ioaddr, true); +- else ++ else if (!priv->plat->has_sun8i) + stmmac_set_mac(priv->ioaddr, true); + + /* Set the HW DMA mode and the COE */ +@@ -3123,6 +3144,10 @@ static int stmmac_hw_init(struct stmmac_priv *priv) + + priv->hw = mac; + ++ /* dwmac-sun8i only work in chain mode */ ++ if (priv->plat->has_sun8i) ++ chain_mode = 1; ++ + /* To use the chained or ring mode */ + if (priv->synopsys_id >= DWMAC_CORE_4_00) { + priv->hw->mode = &dwmac4_ring_mode_ops; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +index 0ba1caf..3c21862 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -160,6 +160,12 @@ static int stmmac_dt_phy(struct plat_stmmacenet_data *plat, + struct device_node *np, struct device *dev) + { + bool mdio = true; ++ static const struct of_device_id need_mdio_ids[] = { ++ { .compatible = "snps,dwc-qos-ethernet-4.10" }, ++ { .compatible = "allwinner,sun8i-a83t-emac" }, ++ { .compatible = "allwinner,sun8i-h3-emac" }, ++ { .compatible = "allwinner,sun50i-a64-emac" }, + }; -+}; --- -2.9.3 - -From aa634aa1b0454befe54a14d03b9a3ba6f750dcfd Mon Sep 17 00:00:00 2001 -From: Corentin LABBE <clabbe.montjoie@gmail.com> -Date: Fri, 7 Oct 2016 10:25:51 +0200 -Subject: [PATCH 4/8] ARM: dts: sun8i-h3: Add dt node for the syscon control + + /* If phy-handle property is passed from DT, use it as the PHY */ + plat->phy_node = of_parse_phandle(np, "phy-handle", 0); +@@ -176,8 +182,7 @@ static int stmmac_dt_phy(struct plat_stmmacenet_data *plat, + mdio = false; + } + +- /* exception for dwmac-dwc-qos-eth glue logic */ +- if (of_device_is_compatible(np, "snps,dwc-qos-ethernet-4.10")) { ++ if (of_match_node(need_mdio_ids, np)) { + plat->mdio_node = of_get_child_by_name(np, "mdio"); + } else { + /** +diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h +index 8f09f18..100386c 100644 +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -147,6 +147,7 @@ struct plat_stmmacenet_data { + struct reset_control *stmmac_rst; + struct stmmac_axi *axi; + int has_gmac4; ++ bool has_sun8i; + bool tso_en; + int mac_port_sel_speed; + bool en_tx_lpi_clockgating; +From patchwork Tue Mar 14 14:18:42 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2, 06/20] ARM: dts: sunxi-h3-h5: Add dt node for the syscon control module +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623549 +Message-Id: <20170314141856.24560-7-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:42 +0100 This patch add the dt node for the syscon register present on the -Allwinner H3. +Allwinner H3/H5 Only two register are present in this syscon and the only one useful is -the one dedicated to EMAC clock. +the one dedicated to EMAC clock.. Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> --- - arch/arm/boot/dts/sun8i-h3.dtsi | 5 +++++ - 1 file changed, 5 insertions(+) + arch/arm/boot/dts/sunxi-h3-h5.dtsi | 6 ++++++ + 1 file changed, 6 insertions(+) -diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi -index 75a8654..eac7d00 100644 ---- a/arch/arm/boot/dts/sun8i-h3.dtsi -+++ b/arch/arm/boot/dts/sun8i-h3.dtsi -@@ -140,6 +140,11 @@ +diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi +index 2494ea0..07e4f36 100644 +--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi ++++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi +@@ -102,6 +102,12 @@ #size-cells = <1>; ranges; + syscon: syscon@01c00000 { -+ compatible = "syscon"; ++ compatible = "syscon", ++ "allwinner,sun8i-h3-system-controller"; + reg = <0x01c00000 0x1000>; + }; + dma: dma-controller@01c02000 { compatible = "allwinner,sun8i-h3-dma"; reg = <0x01c02000 0x1000>; --- -2.9.3 - -From 976cca2d4eb5bf85c26dfaad03ea70bff88981fd Mon Sep 17 00:00:00 2001 -From: Peter Robinson <pbrobinson@gmail.com> -Date: Tue, 8 Nov 2016 10:23:08 +0000 -Subject: [PATCH 5/8] ARM: dts: sun8i-h3: add sun8i-emac ethernet driver +From patchwork Tue Mar 14 14:18:43 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,07/20] ARM: dts: sunxi-h3-h5: add dwmac-sun8i ethernet driver +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623561 +Message-Id: <20170314141856.24560-8-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:43 +0100 -The sun8i-emac is an ethernet MAC hardware that support 10/100/1000 +The dwmac-sun8i is an ethernet MAC hardware that support 10/100/1000 speed. -This patch enable the sun8i-emac on the Allwinner H3 SoC Device-tree. -The SoC H3 have an internal PHY, so optionals syscon and ephy are set. +This patch enable the dwmac-sun8i on Allwinner H3/H5 SoC Device-tree. +SoC H3/H5 have an internal PHY, so optionals syscon and ephy are set. Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> --- - arch/arm/boot/dts/sun8i-h3.dtsi | 24 ++++++++++++++++++++++++ - 1 file changed, 24 insertions(+) + arch/arm/boot/dts/sunxi-h3-h5.dtsi | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) -diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi -index eac7d00..86a8665 100644 ---- a/arch/arm/boot/dts/sun8i-h3.dtsi -+++ b/arch/arm/boot/dts/sun8i-h3.dtsi -@@ -535,6 +535,30 @@ - #size-cells = <0>; +diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi +index 07e4f36..c35af5e 100644 +--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi ++++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi +@@ -272,6 +272,14 @@ + interrupt-controller; + #interrupt-cells = <3>; + ++ emac_rgmii_pins: emac0@0 { ++ pins = "PD0", "PD1", "PD2", "PD3", "PD4", ++ "PD5", "PD7", "PD8", "PD9", "PD10", ++ "PD12", "PD13", "PD15", "PD16", "PD17"; ++ function = "emac"; ++ drive-strength = <40>; ++ }; ++ + i2c0_pins: i2c0 { + pins = "PA11", "PA12"; + function = "i2c0"; +@@ -368,6 +376,31 @@ + clocks = <&osc24M>; }; + emac: ethernet@1c30000 { @@ -2515,10 +1505,11 @@ index eac7d00..86a8665 100644 + syscon = <&syscon>; + reg = <0x01c30000 0x104>; + interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "macirq"; + resets = <&ccu RST_BUS_EMAC>; -+ reset-names = "ahb"; ++ reset-names = "stmmaceth"; + clocks = <&ccu CLK_BUS_EMAC>; -+ clock-names = "ahb"; ++ clock-names = "stmmaceth"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; @@ -2527,25 +1518,119 @@ index eac7d00..86a8665 100644 + #address-cells = <1>; + #size-cells = <0>; + int_mii_phy: ethernet-phy@1 { -+ reg = <1>; -+ clocks = <&ccu CLK_BUS_EPHY>; -+ resets = <&ccu RST_BUS_EPHY>; ++ reg = <1>; ++ clocks = <&ccu CLK_BUS_EPHY>; ++ resets = <&ccu RST_BUS_EPHY>; + }; + }; + }; + - gic: interrupt-controller@01c81000 { - compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic"; - reg = <0x01c81000 0x1000>, --- -2.9.3 + spi0: spi@01c68000 { + compatible = "allwinner,sun8i-h3-spi"; + reg = <0x01c68000 0x1000>; +From patchwork Tue Mar 14 14:18:44 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,08/20] ARM: dts: sun8i: Enable dwmac-sun8i on the Banana Pi M2+ +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623539 +Message-Id: <20170314141856.24560-9-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, LABBE Corentin <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:44 +0100 + +From: LABBE Corentin <clabbe.montjoie@gmail.com> + +The dwmac-sun8i hardware is present on the Banana Pi M2+ +It uses an external PHY rtl8211e via RGMII. + +This patch create the needed regulator, emac and phy nodes. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts | 37 +++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) -From 6e483713dda9e0d5e8e5f37d8b0367ff216c53db Mon Sep 17 00:00:00 2001 +diff --git a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts +index 52acbe1..30b0a41 100644 +--- a/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts ++++ b/arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts +@@ -90,6 +90,18 @@ + pinctrl-0 = <&wifi_en_bpi_m2p>; + reset-gpios = <&r_pio 0 7 GPIO_ACTIVE_LOW>; /* PL7 */ + }; ++ ++ 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>; ++ }; + }; + + &ehci1 { +@@ -186,3 +198,28 @@ + /* USB VBUS is on as long as VCC-IO is on */ + status = "okay"; + }; ++ ++&pio { ++ gmac_power_pin_orangepi: gmac_power_pin@0 { ++ pins = "PD6"; ++ function = "gpio_out"; ++ drive-strength = <10>; ++ }; ++}; ++ ++&mdio { ++ ext_rgmii_phy: ethernet-phy@1 { ++ reg = <0>; ++ }; ++}; ++ ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emac_rgmii_pins>; ++ phy-supply = <®_gmac_3v3>; ++ phy-handle = <&ext_rgmii_phy>; ++ phy-mode = "rgmii"; ++ ++ allwinner,leds-active-low; ++ status = "okay"; ++}; +From patchwork Tue Mar 14 14:18:45 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,09/20] ARM: dts: sun8i: Enable dwmac-sun8i on the Orange PI PC From: Corentin LABBE <clabbe.montjoie@gmail.com> -Date: Fri, 7 Oct 2016 10:25:53 +0200 -Subject: [PATCH 6/8] ARM: dts: sun8i: Enable sun8i-emac on the Orange PI PC +X-Patchwork-Id: 9623555 +Message-Id: <20170314141856.24560-10-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, LABBE Corentin <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:45 +0100 + +From: LABBE Corentin <clabbe.montjoie@gmail.com> -The sun8i-emac hardware is present on the Orange PI PC. +The dwmac-sun8i hardware is present on the Orange PI PC. It uses the internal PHY. This patch create the needed emac node. @@ -2556,10 +1641,10 @@ Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts -index 3ec9712..1b8dbc0 100644 +index f148111..746c25a 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts -@@ -54,6 +54,7 @@ +@@ -53,6 +53,7 @@ aliases { serial0 = &uart0; @@ -2567,7 +1652,7 @@ index 3ec9712..1b8dbc0 100644 }; chosen { -@@ -183,3 +184,10 @@ +@@ -184,3 +185,10 @@ /* USB VBUS is always on */ status = "okay"; }; @@ -2578,30 +1663,90 @@ index 3ec9712..1b8dbc0 100644 + allwinner,leds-active-low; + status = "okay"; +}; --- -2.9.3 +From patchwork Tue Mar 14 14:18:46 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,10/20] ARM: dts: sun8i: Enable dwmac-sun8i on the Orange Pi 2 +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623557 +Message-Id: <20170314141856.24560-11-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:46 +0100 + +The dwmac-sun8i hardware is present on the Orange PI 2. +It uses the internal PHY. + +This patch create the needed emac node. -From 05cf4e2c77424ad5c9dc8495a65d6695d2831b0a Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -Date: Fri, 7 Oct 2016 10:25:54 +0200 -Subject: [PATCH 7/8] ARM: dts: sun8i: Enable sun8i-emac on the Orange PI One +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +index 5b6d145..3f54b12 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +@@ -55,6 +55,7 @@ + serial0 = &uart0; + /* ethernet0 is the H3 emac, defined in sun8i-h3.dtsi */ + ethernet1 = &rtl8189; ++ ethernet0 = &emac; + }; + + chosen { +@@ -203,3 +204,10 @@ + usb1_vbus-supply = <®_usb1_vbus>; + status = "okay"; + }; ++ ++&emac { ++ phy-handle = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ status = "okay"; ++}; +From patchwork Tue Mar 14 14:18:47 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,11/20] ARM: dts: sun8i: Enable dwmac-sun8i on the Orange PI One +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623541 +Message-Id: <20170314141856.24560-12-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:47 +0100 -The sun8i-emac hardware is present on the Orange PI One. +The dwmac-sun8i hardware is present on the Orange PI One. It uses the internal PHY. This patch create the needed emac node. -Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> --- arch/arm/boot/dts/sun8i-h3-orangepi-one.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts -index 5c9b5bf..fa398cc 100644 +index ea8fd13..1f98ddc 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts -@@ -54,6 +54,7 @@ +@@ -53,6 +53,7 @@ aliases { serial0 = &uart0; @@ -2609,7 +1754,7 @@ index 5c9b5bf..fa398cc 100644 }; chosen { -@@ -94,6 +95,13 @@ +@@ -93,6 +94,13 @@ status = "okay"; }; @@ -2623,47 +1768,412 @@ index 5c9b5bf..fa398cc 100644 &mmc0 { pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; --- -2.9.3 - -From 52c300b614fcb29e81067aa7ed8aafa489b590b7 Mon Sep 17 00:00:00 2001 -From: Peter Robinson <pbrobinson@gmail.com> -Date: Tue, 8 Nov 2016 10:24:42 +0000 -Subject: [PATCH 8/8] ARM: dts: sun8i: Enable sun8i-emac on the Orange Pi 2 +From patchwork Tue Mar 14 14:18:48 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,12/20] ARM: dts: sun8i: Enable dwmac-sun8i on the Orange Pi plus +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623569 +Message-Id: <20170314141856.24560-13-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:48 +0100 -The sun8i-emac hardware is present on the Orange PI 2. -It uses the internal PHY. +The dwmac-sun8i hardware is present on the Orange PI plus. +It uses an external PHY rtl8211e via RGMII. -This patch create the needed emac node. +This patch create the needed regulator, emac and phy nodes. Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> --- - arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 8 ++++++++ - 1 file changed, 8 insertions(+) + arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts | 35 ++++++++++++++++++++++++++++ + 1 file changed, 35 insertions(+) -diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts -index e5bcaba..ad66b61 100644 ---- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts -+++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts -@@ -54,6 +54,7 @@ +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts +index 8c40ab7..4e075a2 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-plus.dts +@@ -58,6 +58,18 @@ + enable-active-high; + gpio = <&pio 6 11 GPIO_ACTIVE_HIGH>; + }; ++ ++ 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>; ++ }; + }; - aliases { - serial0 = &uart0; -+ ethernet0 = &emac; - /* ethernet0 is the H3 emac, defined in sun8i-h3.dtsi */ - ethernet1 = &rtl8189; + &ehci3 { +@@ -86,8 +98,31 @@ + pins = "PG11"; + function = "gpio_out"; }; -@@ -212,3 +213,10 @@ - usb1_vbus-supply = <®_usb1_vbus>; - status = "okay"; ++ ++ gmac_power_pin_orangepi: gmac_power_pin@0 { ++ pins = "PD6"; ++ function = "gpio_out"; ++ drive-strength = <10>; ++ }; + }; + + &usbphy { + usb3_vbus-supply = <®_usb3_vbus>; }; + ++&mdio { ++ ext_rgmii_phy: ethernet-phy@1 { ++ reg = <0>; ++ }; ++}; ++ +&emac { -+ phy-handle = <&int_mii_phy>; -+ phy-mode = "mii"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emac_rgmii_pins>; ++ phy-supply = <®_gmac_3v3>; ++ phy-handle = <&ext_rgmii_phy>; ++ phy-mode = "rgmii"; ++ + allwinner,leds-active-low; + status = "okay"; +}; --- -2.9.3 +From patchwork Tue Mar 14 14:18:49 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2, + 13/20] ARM: dts: sun8i: orangepi-pc-plus: Set EMAC activity LEDs to + active high +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623593 +Message-Id: <20170314141856.24560-14-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:49 +0100 + +On the Orange Pi PC Plus, the polarity of the LEDs on the RJ45 Ethernet +port were changed from active low to active high. + +Signed-off-by: Chen-Yu Tsai <wens@csie.org> +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts +index 8b93f5c..0380769 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc-plus.dts +@@ -86,3 +86,8 @@ + /* eMMC is missing pull-ups */ + bias-pull-up; + }; ++ ++&emac { ++ /* LEDs changed to active high on the plus */ ++ /delete-property/ allwinner,leds-active-low; ++}; +From patchwork Tue Mar 14 14:18:50 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2, 14/20] ARM64: dts: sun50i-a64: Add dt node for the syscon control + module +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623591 +Message-Id: <20170314141856.24560-15-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:50 +0100 + +This patch add the dt node for the syscon register present on the +Allwinner A64. + +Only two register are present in this syscon and the only one useful is +the one dedicated to EMAC clock. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index 1c64ea2..3b09af2 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -121,6 +121,12 @@ + #size-cells = <1>; + ranges; + ++ syscon: syscon@01c00000 { ++ compatible = "syscon", ++ "allwinner,sun8i-h3-system-controller"; ++ reg = <0x01c00000 0x1000>; ++ }; ++ + mmc0: mmc@1c0f000 { + compatible = "allwinner,sun50i-a64-mmc"; + reg = <0x01c0f000 0x1000>; +From patchwork Tue Mar 14 14:18:51 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,15/20] ARM64: dts: sun50i-a64: add dwmac-sun8i Ethernet driver +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623621 +Message-Id: <20170314141856.24560-16-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:51 +0100 + +The dwmac-sun8i is an Ethernet MAC that supports 10/100/1000 Mbit +connections. It is very similar to the device found in the Allwinner +H3, but lacks the internal 100 Mbit PHY and its associated control +bits. +This adds the necessary bits to the Allwinner A64 SoC .dtsi, but keeps +it disabled at this level. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 37 +++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index 3b09af2..57d69e5 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -277,6 +277,23 @@ + bias-pull-up; + }; + ++ rmii_pins: rmii_pins { ++ pins = "PD10", "PD11", "PD13", "PD14", ++ "PD17", "PD18", "PD19", "PD20", ++ "PD22", "PD23"; ++ function = "emac"; ++ drive-strength = <40>; ++ }; ++ ++ rgmii_pins: rgmii_pins { ++ pins = "PD8", "PD9", "PD10", "PD11", ++ "PD12", "PD13", "PD15", ++ "PD16", "PD17", "PD18", "PD19", ++ "PD20", "PD21", "PD22", "PD23"; ++ function = "emac"; ++ drive-strength = <40>; ++ }; ++ + uart0_pins_a: uart0@0 { + pins = "PB8", "PB9"; + function = "uart0"; +@@ -381,6 +398,26 @@ + #size-cells = <0>; + }; + ++ emac: ethernet@1c30000 { ++ compatible = "allwinner,sun50i-a64-emac"; ++ syscon = <&syscon>; ++ reg = <0x01c30000 0x100>; ++ interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "macirq"; ++ resets = <&ccu RST_BUS_EMAC>; ++ reset-names = "stmmaceth"; ++ clocks = <&ccu CLK_BUS_EMAC>; ++ clock-names = "stmmaceth"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ mdio: mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ + gic: interrupt-controller@1c81000 { + compatible = "arm,gic-400"; + reg = <0x01c81000 0x1000>, +From patchwork Tue Mar 14 14:18:52 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,16/20] ARM: dts: sun50i-a64: enable dwmac-sun8i on pine64 +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623607 +Message-Id: <20170314141856.24560-17-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:52 +0100 + +The dwmac-sun8i hardware is present on the pine64 +It uses an external PHY via RMII. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +index c680ed3..b53994d 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +@@ -109,3 +109,18 @@ + &usbphy { + status = "okay"; + }; ++ ++&mdio { ++ ext_rmii_phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rmii_pins>; ++ phy-mode = "rmii"; ++ phy-handle = <&ext_rmii_phy1>; ++ status = "okay"; ++ ++}; +From patchwork Tue Mar 14 14:18:53 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,17/20] ARM: dts: sun50i-a64: enable dwmac-sun8i on pine64 plus +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623597 +Message-Id: <20170314141856.24560-18-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:53 +0100 + +The dwmac-sun8i hardware is present on the pine64 plus. +It uses an external PHY rtl8211e via RGMII. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts +index 790d14d..8e06aed 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts +@@ -46,5 +46,19 @@ + model = "Pine64+"; + compatible = "pine64,pine64-plus", "allwinner,sun50i-a64"; + +- /* TODO: Camera, Ethernet PHY, touchscreen, etc. */ ++ /* TODO: Camera, touchscreen, etc. */ ++}; ++ ++&mdio { ++ ext_rgmii_phy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rgmii_pins>; ++ phy-mode = "rgmii"; ++ phy-handle = <&ext_rgmii_phy>; ++ status = "okay"; + }; +From patchwork Tue Mar 14 14:18:54 2017 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2, + 18/20] ARM: dts: sun50i-a64: enable dwmac-sun8i on the BananaPi M64 +From: Corentin LABBE <clabbe.montjoie@gmail.com> +X-Patchwork-Id: 9623595 +Message-Id: <20170314141856.24560-19-clabbe.montjoie@gmail.com> +To: robh+dt@kernel.org, mark.rutland@arm.com, + maxime.ripard@free-electrons.com, + wens@csie.org, linux@armlinux.org.uk, catalin.marinas@arm.com, + will.deacon@arm.com, peppe.cavallaro@st.com, alexandre.torgue@st.com, + davem@davemloft.net +Cc: devicetree@vger.kernel.org, f.fainelli@gmail.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Corentin Labbe <clabbe.montjoie@gmail.com>, + linux-arm-kernel@lists.infradead.org +Date: Tue, 14 Mar 2017 15:18:54 +0100 +The dwmac-sun8i hardware is present on the BananaPi M64. +It uses an external PHY rtl8211e via RGMII. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts +index 6872135..347c262 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts +@@ -77,6 +77,20 @@ + bias-pull-up; + }; + ++&mdio { ++ ext_rgmii_phy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&emac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rgmii_pins>; ++ phy-mode = "rgmii"; ++ phy-handle = <&ext_rgmii_phy>; ++ status = "okay"; ++}; ++ + &mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins>; |