common: Drop asm/global_data.h from common header
[platform/kernel/u-boot.git] / drivers / net / sun8i_emac.c
index c0a4408..a6cdda8 100644 (file)
  *
 */
 
+#include <cpu_func.h>
+#include <log.h>
+#include <asm/cache.h>
+#include <asm/global_data.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/gpio.h>
 #include <clk.h>
 #include <dm.h>
 #include <fdt_support.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <malloc.h>
 #include <miiphy.h>
 #include <net.h>
 #include <reset.h>
 #include <dt-bindings/pinctrl/sun4i-a10.h>
-#ifdef CONFIG_DM_GPIO
+#include <wait_bit.h>
+#if CONFIG_IS_ENABLED(DM_GPIO)
 #include <asm-generic/gpio.h>
 #endif
 
 #define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT        4
 #define MDIO_CMD_MII_PHY_ADDR_MASK     0x0001f000
 #define MDIO_CMD_MII_PHY_ADDR_SHIFT    12
+#define MDIO_CMD_MII_CLK_CSR_DIV_16    0x0
+#define MDIO_CMD_MII_CLK_CSR_DIV_32    0x1
+#define MDIO_CMD_MII_CLK_CSR_DIV_64    0x2
+#define MDIO_CMD_MII_CLK_CSR_DIV_128   0x3
+#define MDIO_CMD_MII_CLK_CSR_SHIFT     20
 
 #define CONFIG_TX_DESCR_NUM    32
 #define CONFIG_RX_DESCR_NUM    32
 
 /* IO mux settings */
 #define SUN8I_IOMUX_H3         2
-#define SUN8I_IOMUX_R40        5
+#define SUN8I_IOMUX_R40                5
+#define SUN8I_IOMUX_H6         5
+#define SUN8I_IOMUX_H616       2
 #define SUN8I_IOMUX            4
 
 /* H3/A64 EMAC Register's offset */
 #define EMAC_CTL0              0x00
+#define EMAC_CTL0_FULL_DUPLEX          BIT(0)
+#define EMAC_CTL0_SPEED_MASK           GENMASK(3, 2)
+#define EMAC_CTL0_SPEED_10             (0x2 << 2)
+#define EMAC_CTL0_SPEED_100            (0x3 << 2)
+#define EMAC_CTL0_SPEED_1000           (0x0 << 2)
 #define EMAC_CTL1              0x04
+#define EMAC_CTL1_SOFT_RST             BIT(0)
+#define EMAC_CTL1_BURST_LEN_SHIFT      24
 #define EMAC_INT_STA           0x08
 #define EMAC_INT_EN            0x0c
 #define EMAC_TX_CTL0           0x10
+#define        EMAC_TX_CTL0_TX_EN              BIT(31)
 #define EMAC_TX_CTL1           0x14
+#define        EMAC_TX_CTL1_TX_MD              BIT(1)
+#define        EMAC_TX_CTL1_TX_DMA_EN          BIT(30)
+#define        EMAC_TX_CTL1_TX_DMA_START       BIT(31)
 #define EMAC_TX_FLOW_CTL       0x1c
 #define EMAC_TX_DMA_DESC       0x20
 #define EMAC_RX_CTL0           0x24
+#define        EMAC_RX_CTL0_RX_EN              BIT(31)
 #define EMAC_RX_CTL1           0x28
+#define        EMAC_RX_CTL1_RX_MD              BIT(1)
+#define        EMAC_RX_CTL1_RX_RUNT_FRM        BIT(2)
+#define        EMAC_RX_CTL1_RX_ERR_FRM         BIT(3)
+#define        EMAC_RX_CTL1_RX_DMA_EN          BIT(30)
+#define        EMAC_RX_CTL1_RX_DMA_START       BIT(31)
 #define EMAC_RX_DMA_DESC       0x34
 #define EMAC_MII_CMD           0x48
 #define EMAC_MII_DATA          0x4c
 #define EMAC_RX_DMA_STA                0xc0
 #define EMAC_RX_CUR_DESC       0xc4
 
+#define EMAC_DESC_OWN_DMA      BIT(31)
+#define EMAC_DESC_LAST_DESC    BIT(30)
+#define EMAC_DESC_FIRST_DESC   BIT(29)
+#define EMAC_DESC_CHAIN_SECOND BIT(24)
+
+#define EMAC_DESC_RX_ERROR_MASK        0x400068db
+
 DECLARE_GLOBAL_DATA_PTR;
 
 enum emac_variant {
@@ -105,11 +144,12 @@ enum emac_variant {
        H3_EMAC,
        A64_EMAC,
        R40_GMAC,
+       H6_EMAC,
 };
 
 struct emac_dma_desc {
        u32 status;
-       u32 st;
+       u32 ctl_size;
        u32 buf_addr;
        u32 next;
 } __aligned(ARCH_DMA_MINALIGN);
@@ -141,7 +181,7 @@ struct emac_eth_dev {
        struct clk ephy_clk;
        struct reset_ctl tx_rst;
        struct reset_ctl ephy_rst;
-#ifdef CONFIG_DM_GPIO
+#if CONFIG_IS_ENABLED(DM_GPIO)
        struct gpio_desc reset_gpio;
 #endif
 };
@@ -159,32 +199,31 @@ static int sun8i_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
 {
        struct udevice *dev = bus->priv;
        struct emac_eth_dev *priv = dev_get_priv(dev);
-       ulong start;
-       u32 miiaddr = 0;
-       int timeout = CONFIG_MDIO_TIMEOUT;
+       u32 mii_cmd;
+       int ret;
 
-       miiaddr &= ~MDIO_CMD_MII_WRITE;
-       miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK;
-       miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) &
+       mii_cmd = (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) &
                MDIO_CMD_MII_PHY_REG_ADDR_MASK;
-
-       miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK;
-
-       miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) &
+       mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) &
                MDIO_CMD_MII_PHY_ADDR_MASK;
 
-       miiaddr |= MDIO_CMD_MII_BUSY;
+       /*
+        * The EMAC clock is either 200 or 300 MHz, so we need a divider
+        * of 128 to get the MDIO frequency below the required 2.5 MHz.
+        */
+       mii_cmd |= MDIO_CMD_MII_CLK_CSR_DIV_128 << MDIO_CMD_MII_CLK_CSR_SHIFT;
+
+       mii_cmd |= MDIO_CMD_MII_BUSY;
 
-       writel(miiaddr, priv->mac_reg + EMAC_MII_CMD);
+       writel(mii_cmd, priv->mac_reg + EMAC_MII_CMD);
 
-       start = get_timer(0);
-       while (get_timer(start) < timeout) {
-               if (!(readl(priv->mac_reg + EMAC_MII_CMD) & MDIO_CMD_MII_BUSY))
-                       return readl(priv->mac_reg + EMAC_MII_DATA);
-               udelay(10);
-       };
+       ret = wait_for_bit_le32(priv->mac_reg + EMAC_MII_CMD,
+                               MDIO_CMD_MII_BUSY, false,
+                               CONFIG_MDIO_TIMEOUT, true);
+       if (ret < 0)
+               return ret;
 
-       return -1;
+       return readl(priv->mac_reg + EMAC_MII_DATA);
 }
 
 static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
@@ -192,39 +231,35 @@ static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
 {
        struct udevice *dev = bus->priv;
        struct emac_eth_dev *priv = dev_get_priv(dev);
-       ulong start;
-       u32 miiaddr = 0;
-       int ret = -1, timeout = CONFIG_MDIO_TIMEOUT;
+       u32 mii_cmd;
 
-       miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK;
-       miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) &
+       mii_cmd = (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) &
                MDIO_CMD_MII_PHY_REG_ADDR_MASK;
-
-       miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK;
-       miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) &
+       mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) &
                MDIO_CMD_MII_PHY_ADDR_MASK;
 
-       miiaddr |= MDIO_CMD_MII_WRITE;
-       miiaddr |= MDIO_CMD_MII_BUSY;
+       /*
+        * The EMAC clock is either 200 or 300 MHz, so we need a divider
+        * of 128 to get the MDIO frequency below the required 2.5 MHz.
+        */
+       mii_cmd |= MDIO_CMD_MII_CLK_CSR_DIV_128 << MDIO_CMD_MII_CLK_CSR_SHIFT;
 
-       writel(val, priv->mac_reg + EMAC_MII_DATA);
-       writel(miiaddr, priv->mac_reg + EMAC_MII_CMD);
+       mii_cmd |= MDIO_CMD_MII_WRITE;
+       mii_cmd |= MDIO_CMD_MII_BUSY;
 
-       start = get_timer(0);
-       while (get_timer(start) < timeout) {
-               if (!(readl(priv->mac_reg + EMAC_MII_CMD) &
-                                       MDIO_CMD_MII_BUSY)) {
-                       ret = 0;
-                       break;
-               }
-               udelay(10);
-       };
+       writel(val, priv->mac_reg + EMAC_MII_DATA);
+       writel(mii_cmd, priv->mac_reg + EMAC_MII_CMD);
 
-       return ret;
+       return wait_for_bit_le32(priv->mac_reg + EMAC_MII_CMD,
+                                MDIO_CMD_MII_BUSY, false,
+                                CONFIG_MDIO_TIMEOUT, true);
 }
 
-static int _sun8i_write_hwaddr(struct emac_eth_dev *priv, u8 *mac_id)
+static int sun8i_eth_write_hwaddr(struct udevice *dev)
 {
+       struct emac_eth_dev *priv = dev_get_priv(dev);
+       struct eth_pdata *pdata = dev_get_plat(dev);
+       uchar *mac_id = pdata->enetaddr;
        u32 macid_lo, macid_hi;
 
        macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) +
@@ -245,58 +280,57 @@ static void sun8i_adjust_link(struct emac_eth_dev *priv,
        v = readl(priv->mac_reg + EMAC_CTL0);
 
        if (phydev->duplex)
-               v |= BIT(0);
+               v |= EMAC_CTL0_FULL_DUPLEX;
        else
-               v &= ~BIT(0);
+               v &= ~EMAC_CTL0_FULL_DUPLEX;
 
-       v &= ~0x0C;
+       v &= ~EMAC_CTL0_SPEED_MASK;
 
        switch (phydev->speed) {
        case 1000:
+               v |= EMAC_CTL0_SPEED_1000;
                break;
        case 100:
-               v |= BIT(2);
-               v |= BIT(3);
+               v |= EMAC_CTL0_SPEED_100;
                break;
        case 10:
-               v |= BIT(3);
+               v |= EMAC_CTL0_SPEED_10;
                break;
        }
        writel(v, priv->mac_reg + EMAC_CTL0);
 }
 
-static int sun8i_emac_set_syscon_ephy(struct emac_eth_dev *priv, u32 *reg)
+static u32 sun8i_emac_set_syscon_ephy(struct emac_eth_dev *priv, u32 reg)
 {
        if (priv->use_internal_phy) {
                /* H3 based SoC's that has an Internal 100MBit PHY
                 * needs to be configured and powered up before use
                */
-               *reg &= ~H3_EPHY_DEFAULT_MASK;
-               *reg |=  H3_EPHY_DEFAULT_VALUE;
-               *reg |= priv->phyaddr << H3_EPHY_ADDR_SHIFT;
-               *reg &= ~H3_EPHY_SHUTDOWN;
-               *reg |= H3_EPHY_SELECT;
-       } else
-               /* This is to select External Gigabit PHY on
-                * the boards with H3 SoC.
-               */
-               *reg &= ~H3_EPHY_SELECT;
+               reg &= ~H3_EPHY_DEFAULT_MASK;
+               reg |=  H3_EPHY_DEFAULT_VALUE;
+               reg |= priv->phyaddr << H3_EPHY_ADDR_SHIFT;
+               reg &= ~H3_EPHY_SHUTDOWN;
+               return reg | H3_EPHY_SELECT;
+       }
 
-       return 0;
+       /* This is to select External Gigabit PHY on those boards with
+        * an internal PHY. Does not hurt on other SoCs. Linux does
+        * it as well.
+        */
+       return reg & ~H3_EPHY_SELECT;
 }
 
 static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
                                 struct emac_eth_dev *priv)
 {
-       int ret;
        u32 reg;
 
        if (priv->variant == R40_GMAC) {
                /* Select RGMII for R40 */
                reg = readl(priv->sysctl_reg + 0x164);
-               reg |= CCM_GMAC_CTRL_TX_CLK_SRC_INT_RGMII |
-                      CCM_GMAC_CTRL_GPIT_RGMII |
-                      CCM_GMAC_CTRL_TX_CLK_DELAY(CONFIG_GMAC_TX_DELAY);
+               reg |= SC_ETCS_INT_GMII |
+                      SC_EPIT |
+                      (CONFIG_GMAC_TX_DELAY << SC_ETXDC_OFFSET);
 
                writel(reg, priv->sysctl_reg + 0x164);
                return 0;
@@ -304,14 +338,12 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
 
        reg = readl(priv->sysctl_reg + 0x30);
 
-       if (priv->variant == H3_EMAC) {
-               ret = sun8i_emac_set_syscon_ephy(priv, &reg);
-               if (ret)
-                       return ret;
-       }
+       reg = sun8i_emac_set_syscon_ephy(priv, reg);
 
        reg &= ~(SC_ETCS_MASK | SC_EPIT);
-       if (priv->variant == H3_EMAC || priv->variant == A64_EMAC)
+       if (priv->variant == H3_EMAC ||
+           priv->variant == A64_EMAC ||
+           priv->variant == H6_EMAC)
                reg &= ~SC_RMII_EN;
 
        switch (priv->interface) {
@@ -319,11 +351,15 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
                /* default */
                break;
        case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
                reg |= SC_EPIT | SC_ETCS_INT_GMII;
                break;
        case PHY_INTERFACE_MODE_RMII:
                if (priv->variant == H3_EMAC ||
-                   priv->variant == A64_EMAC) {
+                   priv->variant == A64_EMAC ||
+                   priv->variant == H6_EMAC) {
                        reg |= SC_RMII_EN | SC_ETCS_EXT_GMII;
                break;
                }
@@ -362,24 +398,36 @@ static int sun8i_phy_init(struct emac_eth_dev *priv, void *dev)
        return 0;
 }
 
+#define cache_clean_descriptor(desc)                                   \
+       flush_dcache_range((uintptr_t)(desc),                           \
+                          (uintptr_t)(desc) + sizeof(struct emac_dma_desc))
+
+#define cache_inv_descriptor(desc)                                     \
+       invalidate_dcache_range((uintptr_t)(desc),                      \
+                              (uintptr_t)(desc) + sizeof(struct emac_dma_desc))
+
 static void rx_descs_init(struct emac_eth_dev *priv)
 {
        struct emac_dma_desc *desc_table_p = &priv->rx_chain[0];
        char *rxbuffs = &priv->rxbuffer[0];
        struct emac_dma_desc *desc_p;
-       u32 idx;
-
-       /* flush Rx buffers */
-       flush_dcache_range((uintptr_t)rxbuffs, (ulong)rxbuffs +
-                       RX_TOTAL_BUFSIZE);
-
-       for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) {
-               desc_p = &desc_table_p[idx];
-               desc_p->buf_addr = (uintptr_t)&rxbuffs[idx * CONFIG_ETH_BUFSIZE]
-                       ;
-               desc_p->next = (uintptr_t)&desc_table_p[idx + 1];
-               desc_p->st |= CONFIG_ETH_RXSIZE;
-               desc_p->status = BIT(31);
+       int i;
+
+       /*
+        * Make sure we don't have dirty cache lines around, which could
+        * be cleaned to DRAM *after* the MAC has already written data to it.
+        */
+       invalidate_dcache_range((uintptr_t)desc_table_p,
+                             (uintptr_t)desc_table_p + sizeof(priv->rx_chain));
+       invalidate_dcache_range((uintptr_t)rxbuffs,
+                               (uintptr_t)rxbuffs + sizeof(priv->rxbuffer));
+
+       for (i = 0; i < CONFIG_RX_DESCR_NUM; i++) {
+               desc_p = &desc_table_p[i];
+               desc_p->buf_addr = (uintptr_t)&rxbuffs[i * CONFIG_ETH_BUFSIZE];
+               desc_p->next = (uintptr_t)&desc_table_p[i + 1];
+               desc_p->ctl_size = CONFIG_ETH_RXSIZE;
+               desc_p->status = EMAC_DESC_OWN_DMA;
        }
 
        /* Correcting the last pointer of the chain */
@@ -398,97 +446,84 @@ static void tx_descs_init(struct emac_eth_dev *priv)
        struct emac_dma_desc *desc_table_p = &priv->tx_chain[0];
        char *txbuffs = &priv->txbuffer[0];
        struct emac_dma_desc *desc_p;
-       u32 idx;
-
-       for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) {
-               desc_p = &desc_table_p[idx];
-               desc_p->buf_addr = (uintptr_t)&txbuffs[idx * CONFIG_ETH_BUFSIZE]
-                       ;
-               desc_p->next = (uintptr_t)&desc_table_p[idx + 1];
-               desc_p->status = (1 << 31);
-               desc_p->st = 0;
+       int i;
+
+       for (i = 0; i < CONFIG_TX_DESCR_NUM; i++) {
+               desc_p = &desc_table_p[i];
+               desc_p->buf_addr = (uintptr_t)&txbuffs[i * CONFIG_ETH_BUFSIZE];
+               desc_p->next = (uintptr_t)&desc_table_p[i + 1];
+               desc_p->ctl_size = 0;
+               desc_p->status = 0;
        }
 
        /* Correcting the last pointer of the chain */
        desc_p->next =  (uintptr_t)&desc_table_p[0];
 
-       /* Flush all Tx buffer descriptors */
-       flush_dcache_range((uintptr_t)priv->tx_chain,
-                          (uintptr_t)priv->tx_chain +
-                       sizeof(priv->tx_chain));
+       /* Flush the first TX buffer descriptor we will tell the MAC about. */
+       cache_clean_descriptor(desc_table_p);
 
        writel((uintptr_t)&desc_table_p[0], priv->mac_reg + EMAC_TX_DMA_DESC);
        priv->tx_currdescnum = 0;
 }
 
-static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr)
+static int sun8i_emac_eth_start(struct udevice *dev)
 {
-       u32 reg, v;
-       int timeout = 100;
-
-       reg = readl((priv->mac_reg + EMAC_CTL1));
-
-       if (!(reg & 0x1)) {
-               /* Soft reset MAC */
-               setbits_le32((priv->mac_reg + EMAC_CTL1), 0x1);
-               do {
-                       reg = readl(priv->mac_reg + EMAC_CTL1);
-               } while ((reg & 0x01) != 0 &&  (--timeout));
-               if (!timeout) {
-                       printf("%s: Timeout\n", __func__);
-                       return -1;
-               }
+       struct emac_eth_dev *priv = dev_get_priv(dev);
+       int ret;
+
+       /* Soft reset MAC */
+       writel(EMAC_CTL1_SOFT_RST, priv->mac_reg + EMAC_CTL1);
+       ret = wait_for_bit_le32(priv->mac_reg + EMAC_CTL1,
+                               EMAC_CTL1_SOFT_RST, false, 10, true);
+       if (ret) {
+               printf("%s: Timeout\n", __func__);
+               return ret;
        }
 
        /* Rewrite mac address after reset */
-       _sun8i_write_hwaddr(priv, enetaddr);
+       sun8i_eth_write_hwaddr(dev);
 
-       v = readl(priv->mac_reg + EMAC_TX_CTL1);
-       /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/
-       v |= BIT(1);
-       writel(v, priv->mac_reg + EMAC_TX_CTL1);
+       /* transmission starts after the full frame arrived in TX DMA FIFO */
+       setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_MD);
 
-       v = readl(priv->mac_reg + EMAC_RX_CTL1);
-       /* RX_MD RX DMA reads data from RX DMA FIFO to host memory after a
+       /*
+        * 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->mac_reg + EMAC_RX_CTL1);
+       setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_MD);
 
-       /* DMA */
-       writel(8 << 24, priv->mac_reg + EMAC_CTL1);
+       /* DMA burst length */
+       writel(8 << EMAC_CTL1_BURST_LEN_SHIFT, priv->mac_reg + EMAC_CTL1);
 
        /* Initialize rx/tx descriptors */
        rx_descs_init(priv);
        tx_descs_init(priv);
 
        /* PHY Start Up */
-       phy_startup(priv->phydev);
+       ret = phy_startup(priv->phydev);
+       if (ret)
+               return ret;
 
        sun8i_adjust_link(priv, priv->phydev);
 
-       /* Start RX DMA */
-       v = readl(priv->mac_reg + EMAC_RX_CTL1);
-       v |= BIT(30);
-       writel(v, priv->mac_reg + EMAC_RX_CTL1);
-       /* Start TX DMA */
-       v = readl(priv->mac_reg + EMAC_TX_CTL1);
-       v |= BIT(30);
-       writel(v, priv->mac_reg + EMAC_TX_CTL1);
+       /* Start RX/TX DMA */
+       setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN |
+                    EMAC_RX_CTL1_RX_ERR_FRM | EMAC_RX_CTL1_RX_RUNT_FRM);
+       setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_EN);
 
        /* Enable RX/TX */
-       setbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31));
-       setbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31));
+       setbits_le32(priv->mac_reg + EMAC_RX_CTL0, EMAC_RX_CTL0_RX_EN);
+       setbits_le32(priv->mac_reg + EMAC_TX_CTL0, EMAC_TX_CTL0_TX_EN);
 
        return 0;
 }
 
 static int parse_phy_pins(struct udevice *dev)
 {
-       struct emac_eth_dev *priv = dev_get_priv(dev);
        int offset;
        const char *pin_name;
        int drive, pull = SUN4I_PINCTRL_NO_PULL, i;
+       u32 iomux;
 
        offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev),
                                       "pinctrl-0");
@@ -515,6 +550,21 @@ static int parse_phy_pins(struct udevice *dev)
        else if (fdt_get_property(gd->fdt_blob, offset, "bias-pull-down", NULL))
                pull = SUN4I_PINCTRL_PULL_DOWN;
 
+       /*
+        * The GPIO pinmux value is an integration choice, so depends on the
+        * SoC, not the EMAC variant.
+        */
+       if (IS_ENABLED(CONFIG_MACH_SUN8I_H3))
+               iomux = SUN8I_IOMUX_H3;
+       else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40))
+               iomux = SUN8I_IOMUX_R40;
+       else if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
+               iomux = SUN8I_IOMUX_H6;
+       else if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
+               iomux = SUN8I_IOMUX_H616;
+       else
+               iomux = SUN8I_IOMUX;
+
        for (i = 0; ; i++) {
                int pin;
 
@@ -527,12 +577,7 @@ static int parse_phy_pins(struct udevice *dev)
                if (pin < 0)
                        continue;
 
-               if (priv->variant == H3_EMAC)
-                       sunxi_gpio_set_cfgpin(pin, SUN8I_IOMUX_H3);
-               else if (priv->variant == R40_GMAC)
-                       sunxi_gpio_set_cfgpin(pin, SUN8I_IOMUX_R40);
-               else
-                       sunxi_gpio_set_cfgpin(pin, SUN8I_IOMUX);
+               sunxi_gpio_set_cfgpin(pin, iomux);
 
                if (drive != ~0)
                        sunxi_gpio_set_drv(pin, drive);
@@ -548,88 +593,71 @@ static int parse_phy_pins(struct udevice *dev)
        return 0;
 }
 
-static int _sun8i_eth_recv(struct emac_eth_dev *priv, uchar **packetp)
+static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 {
+       struct emac_eth_dev *priv = dev_get_priv(dev);
        u32 status, desc_num = priv->rx_currdescnum;
        struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num];
-       int length = -EAGAIN;
-       int good_packet = 1;
-       uintptr_t desc_start = (uintptr_t)desc_p;
-       uintptr_t desc_end = desc_start +
-               roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN);
-
-       ulong data_start = (uintptr_t)desc_p->buf_addr;
-       ulong data_end;
+       uintptr_t data_start = (uintptr_t)desc_p->buf_addr;
+       int length;
 
        /* Invalidate entire buffer descriptor */
-       invalidate_dcache_range(desc_start, desc_end);
+       cache_inv_descriptor(desc_p);
 
        status = desc_p->status;
 
        /* Check for DMA own bit */
-       if (!(status & BIT(31))) {
-               length = (desc_p->status >> 16) & 0x3FFF;
+       if (status & EMAC_DESC_OWN_DMA)
+               return -EAGAIN;
 
-               if (length < 0x40) {
-                       good_packet = 0;
-                       debug("RX: Bad Packet (runt)\n");
-               }
+       length = (status >> 16) & 0x3fff;
 
-               data_end = data_start + length;
-               /* Invalidate received data */
-               invalidate_dcache_range(rounddown(data_start,
-                                                 ARCH_DMA_MINALIGN),
-                                       roundup(data_end,
-                                               ARCH_DMA_MINALIGN));
-               if (good_packet) {
-                       if (length > CONFIG_ETH_RXSIZE) {
-                               printf("Received packet is too big (len=%d)\n",
-                                      length);
-                               return -EMSGSIZE;
-                       }
-                       *packetp = (uchar *)(ulong)desc_p->buf_addr;
-                       return length;
-               }
+       /* make sure we read from DRAM, not our cache */
+       invalidate_dcache_range(data_start,
+                               data_start + roundup(length, ARCH_DMA_MINALIGN));
+
+       if (status & EMAC_DESC_RX_ERROR_MASK) {
+               debug("RX: packet error: 0x%x\n",
+                     status & EMAC_DESC_RX_ERROR_MASK);
+               return 0;
+       }
+       if (length < 0x40) {
+               debug("RX: Bad Packet (runt)\n");
+               return 0;
        }
 
+       if (length > CONFIG_ETH_RXSIZE) {
+               debug("RX: Too large packet (%d bytes)\n", length);
+               return 0;
+       }
+
+       *packetp = (uchar *)(ulong)desc_p->buf_addr;
+
        return length;
 }
 
-static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet,
-                               int len)
+static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length)
 {
-       u32 v, desc_num = priv->tx_currdescnum;
+       struct emac_eth_dev *priv = dev_get_priv(dev);
+       u32 desc_num = priv->tx_currdescnum;
        struct emac_dma_desc *desc_p = &priv->tx_chain[desc_num];
-       uintptr_t desc_start = (uintptr_t)desc_p;
-       uintptr_t desc_end = desc_start +
-               roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN);
-
        uintptr_t data_start = (uintptr_t)desc_p->buf_addr;
        uintptr_t data_end = data_start +
-               roundup(len, ARCH_DMA_MINALIGN);
+               roundup(length, ARCH_DMA_MINALIGN);
 
-       /* Invalidate entire buffer descriptor */
-       invalidate_dcache_range(desc_start, desc_end);
+       desc_p->ctl_size = length | EMAC_DESC_CHAIN_SECOND;
 
-       desc_p->st = len;
-       /* Mandatory undocumented bit */
-       desc_p->st |= BIT(24);
-
-       memcpy((void *)data_start, packet, len);
+       memcpy((void *)data_start, packet, length);
 
        /* Flush data to be sent */
        flush_dcache_range(data_start, data_end);
 
-       /* frame end */
-       desc_p->st |= BIT(30);
-       desc_p->st |= BIT(31);
-
-       /*frame begin */
-       desc_p->st |= BIT(29);
-       desc_p->status = BIT(31);
+       /* frame begin and end */
+       desc_p->ctl_size |= EMAC_DESC_LAST_DESC | EMAC_DESC_FIRST_DESC;
+       desc_p->status = EMAC_DESC_OWN_DMA;
 
-       /*Descriptors st and status field has changed, so FLUSH it */
-       flush_dcache_range(desc_start, desc_end);
+       /* make sure the MAC reads the actual data from DRAM */
+       cache_clean_descriptor(desc_p);
 
        /* Move to next Descriptor and wrap around */
        if (++desc_num >= CONFIG_TX_DESCR_NUM)
@@ -637,23 +665,18 @@ static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet,
        priv->tx_currdescnum = desc_num;
 
        /* Start the DMA */
-       v = readl(priv->mac_reg + EMAC_TX_CTL1);
-       v |= BIT(31);/* mandatory */
-       v |= BIT(30);/* mandatory */
-       writel(v, priv->mac_reg + EMAC_TX_CTL1);
+       setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_START);
 
-       return 0;
-}
-
-static int sun8i_eth_write_hwaddr(struct udevice *dev)
-{
-       struct eth_pdata *pdata = dev_get_platdata(dev);
-       struct emac_eth_dev *priv = dev_get_priv(dev);
+       /*
+        * Since we copied the data above, we return here without waiting
+        * for the packet to be actually send out.
+        */
 
-       return _sun8i_write_hwaddr(priv, pdata->enetaddr);
+       return 0;
 }
 
-static int sun8i_emac_board_setup(struct emac_eth_dev *priv)
+static int sun8i_emac_board_setup(struct udevice *dev,
+                                 struct emac_eth_dev *priv)
 {
        int ret;
 
@@ -695,12 +718,12 @@ err_tx_clk:
        return ret;
 }
 
-#if defined(CONFIG_DM_GPIO)
+#if CONFIG_IS_ENABLED(DM_GPIO)
 static int sun8i_mdio_reset(struct mii_dev *bus)
 {
        struct udevice *dev = bus->priv;
        struct emac_eth_dev *priv = dev_get_priv(dev);
-       struct sun8i_eth_pdata *pdata = dev_get_platdata(dev);
+       struct sun8i_eth_pdata *pdata = dev_get_plat(dev);
        int ret;
 
        if (!dm_gpio_is_valid(&priv->reset_gpio))
@@ -742,47 +765,25 @@ static int sun8i_mdio_init(const char *name, struct udevice *priv)
        bus->write = sun8i_mdio_write;
        snprintf(bus->name, sizeof(bus->name), name);
        bus->priv = (void *)priv;
-#if defined(CONFIG_DM_GPIO)
+#if CONFIG_IS_ENABLED(DM_GPIO)
        bus->reset = sun8i_mdio_reset;
 #endif
 
        return  mdio_register(bus);
 }
 
-static int sun8i_emac_eth_start(struct udevice *dev)
-{
-       struct eth_pdata *pdata = dev_get_platdata(dev);
-
-       return _sun8i_emac_eth_init(dev->priv, pdata->enetaddr);
-}
-
-static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length)
-{
-       struct emac_eth_dev *priv = dev_get_priv(dev);
-
-       return _sun8i_emac_eth_send(priv, packet, length);
-}
-
-static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet,
+                             int length)
 {
        struct emac_eth_dev *priv = dev_get_priv(dev);
-
-       return _sun8i_eth_recv(priv, packetp);
-}
-
-static int _sun8i_free_pkt(struct emac_eth_dev *priv)
-{
        u32 desc_num = priv->rx_currdescnum;
        struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num];
-       uintptr_t desc_start = (uintptr_t)desc_p;
-       uintptr_t desc_end = desc_start +
-               roundup(sizeof(u32), ARCH_DMA_MINALIGN);
 
-       /* Make the current descriptor valid again */
-       desc_p->status |= BIT(31);
+       /* give the current descriptor back to the MAC */
+       desc_p->status |= EMAC_DESC_OWN_DMA;
 
        /* Flush Status field of descriptor */
-       flush_dcache_range(desc_start, desc_end);
+       cache_clean_descriptor(desc_p);
 
        /* Move to next desc and wrap-around condition. */
        if (++desc_num >= CONFIG_RX_DESCR_NUM)
@@ -792,38 +793,31 @@ static int _sun8i_free_pkt(struct emac_eth_dev *priv)
        return 0;
 }
 
-static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet,
-                             int length)
-{
-       struct emac_eth_dev *priv = dev_get_priv(dev);
-
-       return _sun8i_free_pkt(priv);
-}
-
 static void sun8i_emac_eth_stop(struct udevice *dev)
 {
        struct emac_eth_dev *priv = dev_get_priv(dev);
 
        /* Stop Rx/Tx transmitter */
-       clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31));
-       clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31));
+       clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, EMAC_RX_CTL0_RX_EN);
+       clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, EMAC_TX_CTL0_TX_EN);
 
-       /* Stop TX DMA */
-       clrbits_le32(priv->mac_reg + EMAC_TX_CTL1, BIT(30));
+       /* Stop RX/TX DMA */
+       clrbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_EN);
+       clrbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN);
 
        phy_shutdown(priv->phydev);
 }
 
 static int sun8i_emac_eth_probe(struct udevice *dev)
 {
-       struct sun8i_eth_pdata *sun8i_pdata = dev_get_platdata(dev);
+       struct sun8i_eth_pdata *sun8i_pdata = dev_get_plat(dev);
        struct eth_pdata *pdata = &sun8i_pdata->eth_pdata;
        struct emac_eth_dev *priv = dev_get_priv(dev);
        int ret;
 
        priv->mac_reg = (void *)pdata->iobase;
 
-       ret = sun8i_emac_board_setup(priv);
+       ret = sun8i_emac_board_setup(dev, priv);
        if (ret)
                return ret;
 
@@ -844,34 +838,30 @@ static const struct eth_ops sun8i_emac_eth_ops = {
        .stop                   = sun8i_emac_eth_stop,
 };
 
-static int sun8i_get_ephy_nodes(struct emac_eth_dev *priv)
+static int sun8i_handle_internal_phy(struct udevice *dev, struct emac_eth_dev *priv)
 {
-       int node, ret;
-
-       /* look for mdio-mux node for internal PHY node */
-       node = fdt_path_offset(gd->fdt_blob,
-                       "/soc/ethernet@1c30000/mdio-mux/mdio@1/ethernet-phy@1");
-       if (node < 0) {
-               debug("failed to get mdio-mux with internal PHY\n");
-               return node;
-       }
+       struct ofnode_phandle_args phandle;
+       int ret;
 
-       ret = fdt_node_check_compatible(gd->fdt_blob, node,
-                                       "allwinner,sun8i-h3-mdio-internal");
-       if (ret < 0) {
-               debug("failed to find mdio-internal node\n");
+       ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "phy-handle",
+                                            NULL, 0, 0, &phandle);
+       if (ret)
                return ret;
-       }
 
-       ret = clk_get_by_index_nodev(offset_to_ofnode(node), 0,
-                                    &priv->ephy_clk);
+       /* If the PHY node is not a child of the internal MDIO bus, we are
+        * using some external PHY.
+        */
+       if (!ofnode_device_is_compatible(ofnode_get_parent(phandle.node),
+                                        "allwinner,sun8i-h3-mdio-internal"))
+               return 0;
+
+       ret = clk_get_by_index_nodev(phandle.node, 0, &priv->ephy_clk);
        if (ret) {
                dev_err(dev, "failed to get EPHY TX clock\n");
                return ret;
        }
 
-       ret = reset_get_by_index_nodev(offset_to_ofnode(node), 0,
-                                      &priv->ephy_rst);
+       ret = reset_get_by_index_nodev(phandle.node, 0, &priv->ephy_rst);
        if (ret) {
                dev_err(dev, "failed to get EPHY TX reset\n");
                return ret;
@@ -882,21 +872,21 @@ static int sun8i_get_ephy_nodes(struct emac_eth_dev *priv)
        return 0;
 }
 
-static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev)
+static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
 {
-       struct sun8i_eth_pdata *sun8i_pdata = dev_get_platdata(dev);
+       struct sun8i_eth_pdata *sun8i_pdata = dev_get_plat(dev);
        struct eth_pdata *pdata = &sun8i_pdata->eth_pdata;
        struct emac_eth_dev *priv = dev_get_priv(dev);
        const char *phy_mode;
        const fdt32_t *reg;
        int node = dev_of_offset(dev);
        int offset = 0;
-#ifdef CONFIG_DM_GPIO
+#if CONFIG_IS_ENABLED(DM_GPIO)
        int reset_flags = GPIOD_IS_OUT;
 #endif
        int ret;
 
-       pdata->iobase = devfdt_get_addr(dev);
+       pdata->iobase = dev_read_addr(dev);
        if (pdata->iobase == FDT_ADDR_T_NONE) {
                debug("%s: Cannot find MAC base address\n", __func__);
                return -EINVAL;
@@ -963,7 +953,7 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev)
        }
 
        if (priv->variant == H3_EMAC) {
-               ret = sun8i_get_ephy_nodes(priv);
+               ret = sun8i_handle_internal_phy(dev, priv);
                if (ret)
                        return ret;
        }
@@ -985,7 +975,7 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev)
                printf("%s: Invalid RX delay value %d\n", __func__,
                       sun8i_pdata->rx_delay_ps);
 
-#ifdef CONFIG_DM_GPIO
+#if CONFIG_IS_ENABLED(DM_GPIO)
        if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
                            "snps,reset-active-low"))
                reset_flags |= GPIOD_ACTIVE_LOW;
@@ -1013,6 +1003,8 @@ static const struct udevice_id sun8i_emac_eth_ids[] = {
                .data = (uintptr_t)A83T_EMAC },
        {.compatible = "allwinner,sun8i-r40-gmac",
                .data = (uintptr_t)R40_GMAC },
+       {.compatible = "allwinner,sun50i-h6-emac",
+               .data = (uintptr_t)H6_EMAC },
        { }
 };
 
@@ -1020,10 +1012,10 @@ U_BOOT_DRIVER(eth_sun8i_emac) = {
        .name   = "eth_sun8i_emac",
        .id     = UCLASS_ETH,
        .of_match = sun8i_emac_eth_ids,
-       .ofdata_to_platdata = sun8i_emac_eth_ofdata_to_platdata,
+       .of_to_plat = sun8i_emac_eth_of_to_plat,
        .probe  = sun8i_emac_eth_probe,
        .ops    = &sun8i_emac_eth_ops,
-       .priv_auto_alloc_size = sizeof(struct emac_eth_dev),
-       .platdata_auto_alloc_size = sizeof(struct sun8i_eth_pdata),
+       .priv_auto      = sizeof(struct emac_eth_dev),
+       .plat_auto      = sizeof(struct sun8i_eth_pdata),
        .flags = DM_FLAG_ALLOC_PRIV_DMA,
 };