This patch add support for sun8i-emac ethernet MAC hardware.
It could be found in Allwinner H3/A83T/A64 SoCs.

Signed-off-by: LABBE Corentin <clabbe.montj...@gmail.com>
---
 drivers/net/ethernet/allwinner/Kconfig      |   14 +
 drivers/net/ethernet/allwinner/Makefile     |    1 +
 drivers/net/ethernet/allwinner/sun8i-emac.c | 1493 +++++++++++++++++++++++++++
 3 files changed, 1508 insertions(+)
 create mode 100644 drivers/net/ethernet/allwinner/sun8i-emac.c

diff --git a/drivers/net/ethernet/allwinner/Kconfig 
b/drivers/net/ethernet/allwinner/Kconfig
index 47da7e7..43cf91f 100644
--- a/drivers/net/ethernet/allwinner/Kconfig
+++ b/drivers/net/ethernet/allwinner/Kconfig
@@ -33,4 +33,18 @@ config SUN4I_EMAC
           To compile this driver as a module, choose M here.  The module
           will be called sun4i-emac.
 
+config SUN8I_EMAC
+        tristate "Allwinner H3 EMAC support"
+       depends on ARCH_SUNXI
+       depends on OF
+       select CRC32
+       select MII
+       select PHYLIB
+       select MDIO_SUN4I
+        ---help---
+          Support for Allwinner H3 EMAC ethernet driver.
+
+          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 @@
 #
 
 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
new file mode 100644
index 0000000..a4e52cf
--- /dev/null
+++ b/drivers/net/ethernet/allwinner/sun8i-emac.c
@@ -0,0 +1,1493 @@
+/*
+ * sun8i-h3-emac driver
+ *
+ * Copyright (C) 2015-2016 Corentin LABBE <clabbe.montj...@gmail.com>
+ *
+ * This is the driver for Allwinner Ethernet MAC found in H3/A83T/A64 SoC
+ *
+ * This is a mono block driver that need to be splited:
+ * - A classic ethernet MAC driver
+ * - A PHY driver
+ * - A clk driver
+ */
+#include <linux/clk.h>
+#include <linux/mii.h>
+#include <linux/gpio.h>
+#include <linux/crc32.h>
+#include <linux/skbuff.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/reset.h>
+#include <linux/of_net.h>
+/* for A83T */
+#include <linux/regulator/consumer.h>
+
+#define SUN8I_EMAC_BASIC_CTL0 0x00
+#define SUN8I_EMAC_BASIC_CTL1 0x04
+
+#define SUN8I_EMAC_MDIO_CMD 0x48
+#define SUN8I_EMAC_MDIO_DATA 0x4C
+
+#define SUN8I_EMAC_RX_CTL0 0x24
+#define SUN8I_EMAC_RX_CTL1 0x28
+
+#define SUN8I_EMAC_TX_CTL0 0x10
+#define SUN8I_EMAC_TX_CTL1 0x14
+
+#define SUN8I_EMAC_TX_FLOW_CTL 0x1C
+
+#define SUN8I_EMAC_INT_STA 0x08
+#define SUN8I_EMAC_INT_EN 0x0C
+#define SUN8I_EMAC_RGMII_STA 0xD0
+
+#define SUN8I_EMAC_TX_DMA_STA 0xB0
+#define SUN8I_EMAC_TX_CUR_DDESC 0xB4
+#define SUN8I_EMAC_RX_DMA_STA 0xC0
+
+#define MII_BUSY       BIT(0)
+#define MII_WRITE      BIT(1)
+
+#define SUN8I_EMAC_MACADDR_HI  0x50
+#define SUN8I_EMAC_MACADDR_LO  0x54
+
+#define SUN8I_EMAC_RX_DESC_LIST 0x34
+#define SUN8I_EMAC_TX_DESC_LIST 0x20
+
+#define SUN8I_COULD_BE_USED_BY_DMA BIT(31)
+
+struct dma_desc {
+       u32 status;
+       u32 st;
+       u32 buf_addr;
+       u32 next;
+} __attribute__((packed, aligned(4)));
+
+static int flow_ctrl;
+static int pause = 0x400;
+
+static int nbdesc = 4;
+struct sun8i_emac_priv {
+       void __iomem *base;
+       int irq;
+       struct device *dev;
+       struct net_device *ndev;
+       struct mii_bus *mii;
+       spinlock_t lock;
+       spinlock_t tx_lock;
+       int duplex;
+       int speed;
+       int link;
+       u32 phy_interface;
+       struct clk *busclk;
+       struct clk *miiclk;
+       u32 mdc;
+
+       struct reset_control *rst_phy;
+       struct reset_control *rst_mac;
+
+       struct dma_desc *dd_rx ____cacheline_aligned;
+       dma_addr_t dd_rx_phy ____cacheline_aligned;
+       struct dma_desc *dd_tx;
+       dma_addr_t dd_tx_phy;
+       struct sk_buff **rx_sk;
+       struct sk_buff **tx_sk;
+
+       struct regulator *power;
+};
+
+/* allocate a sk in a dma descriptor
+*/
+static int sun8i_emac_rx_sk(struct net_device *ndev, int i)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       struct dma_desc *ddesc;
+       struct sk_buff *sk;
+
+       ddesc = priv->dd_rx + i;
+
+       ddesc->st = 0;
+
+       sk = netdev_alloc_skb_ip_align(ndev, ndev->mtu);
+       if (!sk)
+               return -ENOMEM;
+
+       if (priv->rx_sk[i]) {
+               dev_warn(priv->dev, "WARN: Leaking a skbuff\n");
+               /* TODO should not happen */
+       }
+
+       priv->rx_sk[i] = sk;
+
+       ddesc->buf_addr = dma_map_single(priv->dev, sk->data,
+                                        ndev->mtu, DMA_FROM_DEVICE);
+       if (dma_mapping_error(priv->dev, ddesc->buf_addr)) {
+               dev_err(priv->dev, "ERROR: Cannot dma_map RX\n");
+               dev_kfree_skb(sk);
+               return -EINVAL;
+       }
+       ddesc->st |= ndev->mtu;
+       ddesc->status = BIT(31);
+
+       dev_info(priv->dev, "Init ddesc %02d at %pad buff=%p %x status=(%x 
%x)\n",
+                i, &ddesc, &sk->data, ddesc->buf_addr, ddesc->status, 
ddesc->st);
+
+       return 0;
+}
+
+/* Set MAC address for slot index
+ * */
+void sun8i_emac_set_macaddr(struct sun8i_emac_priv *priv, unsigned char *addr,
+                           int index)
+{
+       u32 v;
+
+       if (!is_valid_ether_addr(addr)) {
+               random_ether_addr(priv->ndev->dev_addr);
+               addr = priv->ndev->dev_addr;
+       }
+       dev_info(priv->dev, "%s %x %x %x %x %x %x\n", __func__,
+               addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]),
+
+       v = (addr[5] << 8) | addr[4];
+       writel(v, priv->base + SUN8I_EMAC_MACADDR_HI + index * 8);
+       dev_info(priv->dev, "Set macaddr %x\n", v);
+       v = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
+       writel(v, priv->base + SUN8I_EMAC_MACADDR_HI + index * 8);
+       dev_info(priv->dev, "Set macaddr %x\n", v);
+}
+
+void sun8i_emac_set_link_mode(struct sun8i_emac_priv *priv)
+{
+       u32 v;
+
+       dev_info(priv->dev, "%s duplex=%x\n", __func__, priv->duplex);
+
+       switch (priv->phy_interface) {
+       case PHY_INTERFACE_MODE_MII:
+               v = readl(priv->base + SUN8I_EMAC_BASIC_CTL0);
+
+               if (!priv->duplex)
+                       v &= ~BIT(0);
+               else
+                       v |= BIT(0);
+
+               v &= ~0x0C;
+               switch (priv->speed) {
+               case 1000:
+                       break;
+               case 100:
+                       v |= BIT(2);
+                       v |= BIT(3);
+                       break;
+               case 10:
+                       v |= BIT(3);
+                       break;
+               }
+
+               writel(v, priv->base + SUN8I_EMAC_BASIC_CTL0);
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+               v = readl(priv->base + SUN8I_EMAC_RGMII_STA);
+
+               if (!priv->duplex)
+                       v &= ~BIT(0);
+               else
+                       v |= BIT(0);
+
+               v &= ~0x06;
+               switch (priv->speed) {
+               case 1000:
+                       v |= BIT(2);
+                       break;
+               case 100:
+                       v |= BIT(1);
+                       break;
+               case 10:
+                       break;
+               }
+
+               writel(v, priv->base + SUN8I_EMAC_RGMII_STA);
+               break;
+       default:
+               dev_err(priv->dev, "Unknown PHY type %d\n", 
priv->phy_interface);
+               return;
+       }
+
+       dev_info(priv->dev, "%s set %x\n", __func__, v);
+}
+
+void sun8i_emac_flow_ctrl(struct sun8i_emac_priv *priv, int duplex, int fc,
+                         int pause)
+{
+       u32 flow = 0;
+
+       dev_info(priv->dev, "%s %d %d %d\n", __func__, duplex, fc, pause);
+
+       if (fc & BIT(0)) {
+               flow = readl(priv->base + SUN8I_EMAC_RX_CTL0);
+               flow |= 0x10000;
+               /*flow |= BIT(16);*/
+               writel(flow, priv->base + SUN8I_EMAC_RX_CTL0);
+       }
+
+       if (fc & BIT(1)) {
+               flow = readl(priv->base + SUN8I_EMAC_TX_FLOW_CTL);
+               flow |= BIT(0);
+               writel(flow, priv->base + SUN8I_EMAC_TX_FLOW_CTL);
+       }
+
+       if (duplex) {
+               flow = readl(priv->base + SUN8I_EMAC_TX_FLOW_CTL);
+               flow |= (pause << 4);
+               /* pause & BIT(4)*/
+               writel(flow, priv->base + SUN8I_EMAC_TX_FLOW_CTL);
+       }
+}
+
+/*
+ * Grab a frame into a skb
+*/
+static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i)
+{
+       struct sk_buff *skb;
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       struct dma_desc *ddesc;
+       int frame_len;
+
+       ddesc = priv->dd_rx + i;
+
+       frame_len = (ddesc->status >> 16) & 0x3FFF;
+       skb = priv->rx_sk[i];
+
+       dev_info(priv->dev, "%s from %02d %pad len=%d st=%x\n",
+               __func__, i, &ddesc, frame_len, ddesc->st);
+
+       skb_put(skb, frame_len);
+
+       dma_unmap_single(priv->dev, ddesc->buf_addr, ndev->mtu, 
DMA_FROM_DEVICE);
+       skb->protocol = eth_type_trans(skb, priv->ndev);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       skb->dev = priv->ndev;
+
+       priv->ndev->stats.rx_packets++;
+       priv->ndev->stats.rx_bytes += frame_len;
+
+       netif_rx(skb);
+
+       priv->rx_sk[i] = NULL;
+
+       sun8i_emac_rx_sk(ndev, i);
+
+       return 0;
+}
+
+
+static int sun8i_emac_receive_all(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       struct dma_desc *ddesc;
+       int i;
+
+       for (i = 0; i < nbdesc; i++) {
+               ddesc = priv->dd_rx + i;
+               if (!(ddesc->status & BIT(31))) {
+                       sun8i_emac_rx_from_ddesc(ndev, i);
+               }
+       }
+       return 0;
+}
+
+/* iterate over dma desc for finding completed xmit */
+static int sun8i_emac_complete_xmit(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       struct dma_desc *ddesc;
+       int i, frame_len;
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       for (i = 0; i < nbdesc; i++) {
+               ddesc = priv->dd_tx + i;
+               if (ddesc->status & BIT(31))
+                       continue;
+               if (ddesc->status != 0 || ddesc->st) {
+                       frame_len = ddesc->st & 0x3FF;
+                       dev_info(priv->dev, "%s found slot to clean %d at %pad 
%x %x\n",
+                                __func__, i, &ddesc, ddesc->status, ddesc->st);
+                       dma_unmap_single(priv->dev, ddesc->buf_addr,
+                                        frame_len, DMA_TO_DEVICE);
+                       priv->tx_sk[i] = NULL;
+                       ddesc->status = 0;
+                       ddesc->st = 0;
+               }
+       }
+
+       return 0;
+}
+
+static int debug_printall_desc(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       struct dma_desc *ddesc;
+       int i;
+       int len;
+/*
+       ddesc = priv->dd_rx;
+       if (ddesc == NULL)
+               return 0;
+
+       for (i = 0; i < nbdesc; i++) {
+               ddesc = priv->dd_rx + i;
+               len = (ddesc->status >> 16) & 0x3FFF;
+               if (!(ddesc->status & BIT(31))) {
+                       ptr = (char *)priv->rx_sk[i]->data;
+                       dev_info(priv->dev, "%x %x %x %x %x %x %x %x %x %x %x 
%x %x %x %x %x\n",
+                               ptr[0],
+                               ptr[1],
+                               ptr[2],
+                               ptr[3],
+                               ptr[4],
+                               ptr[5],
+                               ptr[6],
+                               ptr[7],
+                               ptr[8],
+                               ptr[9],
+                               ptr[10],
+                               ptr[11],
+                               ptr[12],
+                               ptr[13],
+                               ptr[14],
+                               ptr[15]);
+                       rx_from_ddesc(ndev, i);
+               }
+               ddesc++;
+       }
+*/
+       ddesc = priv->dd_tx;
+       if (ddesc == NULL)
+               return 0;
+       for (i = 0; i < nbdesc; i++) {
+               ddesc = priv->dd_tx + i;
+               len = ddesc->st & 0x7FF;
+               /*dev_info(priv->dev, "tx%02d status=%x %x d=%x len=%d n=%x", 
i, ddesc->status, ddesc->st, ddesc->buf_addr, len, ddesc->next);*/
+       }
+       return 0;
+}
+
+static int sun8i_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
+{
+       u32 v, c, t, dt, dr, x = 0;
+       struct net_device *ndev = bus->priv;
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       struct dma_desc *ddesc;
+       u32 cur_rd, cur_td;
+
+       dt = readl(priv->base + SUN8I_EMAC_TX_DMA_STA);
+       dr = readl(priv->base + SUN8I_EMAC_RX_DMA_STA);
+       debug_printall_desc(ndev);
+
+       ddesc = priv->dd_rx;
+       if (ddesc != NULL)
+               x = ddesc->status;
+
+       do {
+               v = readl(priv->base + SUN8I_EMAC_MDIO_CMD);
+       } while ((v & MII_BUSY) == 1);
+       /* TODO timeout */
+
+       c = 0;
+       c |= ((priv->mdc & 0x07) << 20);
+       c |= (((phyaddr << 12) & (0x0001F000)) |
+                       ((phyreg << 4) & (0x000007F0)) | MII_BUSY);
+       writel(c, priv->base + SUN8I_EMAC_MDIO_CMD);
+       t = 0;
+       do {
+               v = readl(priv->base + SUN8I_EMAC_MDIO_CMD);
+       } while ((v & MII_BUSY) == 1 && t++ < 1500);
+       /* TODO timeout */
+
+       v = readl(priv->base + SUN8I_EMAC_MDIO_DATA);
+       cur_rd = readl(priv->base + 0xC4);
+       cur_td = readl(priv->base + SUN8I_EMAC_TX_CUR_DDESC);
+
+       /*dev_info(priv->dev, "%s %d %d %x %x t=%u dmar=%x dmat=%x %x cur=%x 
%x\n", __func__, phyaddr, phyreg, c, v, t, dr, dt, x, cur_rd, cur_td);*/
+       return (int)v;
+}
+
+static int sun8i_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, u16 
data)
+{
+       struct net_device *ndev = bus->priv;
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       u32 v;
+
+       do {
+               v = readl(priv->base + SUN8I_EMAC_MDIO_CMD);
+       } while ((v & MII_BUSY) == 1);
+       /* TODO timeout */
+
+       /*v = readl(priv->base + SUN8I_EMAC_MDIO_CMD);
+        * */
+
+       v = ((0x07 << 20) & readl(priv->base + SUN8I_EMAC_MDIO_CMD)) | 
(priv->mdc << 20);
+       v |= (((phyaddr << 12) & (0x0001F000)) |
+                       ((phyreg << 4) & (0x000007F0))) | MII_WRITE | MII_BUSY;
+       writel(data, priv->base + SUN8I_EMAC_MDIO_DATA);
+       writel(v, priv->base + SUN8I_EMAC_MDIO_CMD);
+
+       dev_info(priv->dev, "%s %d %d %x ctl=%x\n", __func__, phyaddr, phyreg, 
data, v);
+
+       do {
+               v = readl(priv->base + SUN8I_EMAC_MDIO_CMD);
+       } while ((v & MII_BUSY) == 1);
+       /* TODO timeout */
+
+       return 0;
+}
+
+static int sun8i_mdio_reset(struct mii_bus *bus)
+{
+       struct net_device *ndev = bus->priv;
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       /* No datasheet, undocumented */
+       /*writel((4 << 2), priv->base + SUN8I_EMAC_MDIO_CMD);*/
+       return 0;
+}
+/* END of need to moved in phy/ */
+
+static void sun8i_phy_adjust_link(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       struct phy_device *phydev = ndev->phydev;
+       unsigned long flags;
+       int new_state = 0;
+       int i;
+
+       dev_info(priv->dev, "%s link=%x duplex=%x speed=%x\n", __func__, 
phydev->link,
+               phydev->duplex, phydev->speed);
+       if (!phydev)
+               return;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       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,
+                                            flow_ctrl, pause);
+
+               if (phydev->speed != priv->speed) {
+                       new_state = 1;
+                       priv->speed = phydev->speed;
+               }
+
+               if (priv->link == 0) {
+                       new_state = 1;
+                       priv->link = phydev->link;
+               }
+
+               dev_info(priv->dev, "%s new=%d link=%d pause=%d\n",
+                        __func__, new_state, priv->link, phydev->pause);
+               if (new_state)
+                       sun8i_emac_set_link_mode(priv);
+
+       } else if (priv->link != phydev->link) {
+               new_state = 1;
+               priv->link = 0;
+               priv->speed = 0;
+               priv->duplex = -1;
+       }
+       if (new_state)
+               phy_print_status(phydev);
+
+       for (i = 0 ; i < 0xd0; i += 16)
+               dev_info(priv->dev, "%x %x %x %x %x %x %x %x",
+                        readl(priv->base + i),
+                        readl(priv->base + i + 4),
+                        readl(priv->base + i + 8),
+                        readl(priv->base + i + 12),
+                        i, i + 4, i + 8, i + 12);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/* this function do lots of things that will be splited away (clk/phy) */
+static int sun8i_phy_init(struct net_device *ndev)
+{
+       int value;
+       struct mii_bus *new_bus;
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       struct phy_device *phydev = NULL;
+       int err;
+       void __iomem *sc;
+       u32 v;
+       int timeout;
+       int do_ephy_clk = 1;
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       /* find type of PHY */
+       priv->phy_interface = of_get_phy_mode(priv->dev->of_node);
+       dev_info(priv->dev, "%s %x\n", __func__, priv->phy_interface);
+       /*priv->phy_interface = PHY_INTERFACE_MODE_RGMII;*/
+
+
+       /* fallback to integrate MII */
+       switch (priv->phy_interface) {
+       case PHY_INTERFACE_MODE_MII:
+               dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_MII\n", 
__func__);
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+               dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_RGMII\n", 
__func__);
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_RMII\n", 
__func__);
+               break;
+       case PHY_INTERFACE_MODE_GMII:
+               dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_GMII\n", 
__func__);
+               break;
+       default:
+               dev_info(priv->dev, "Fallback to MII\n");
+               priv->phy_interface = PHY_INTERFACE_MODE_MII;
+       }
+
+       /* A83T power TODO: no regulator yet */
+       priv->power = regulator_get(NULL, "vc-ephy-io");
+       if (IS_ERR(priv->power)) {
+               dev_err(priv->dev, "Cannot get regulator\n");
+       } else {
+               err = regulator_set_voltage(priv->power, 3300000, 3300000);
+               if (err) {
+                       dev_err(priv->dev, "Cannot set regulator voltage\n");
+               } else {
+                       err = regulator_enable(priv->power);
+                       if (err)
+                               dev_err(priv->dev, "Cannot enable regulator\n");
+                       else
+                               dev_info(priv->dev, "Enabled regulator\n");
+
+               }
+       }
+
+       /* systemcontrol */
+       /* TODO put that in phy clock */
+       sc = ioremap(0x01C00030, 0x20);
+       if (sc) {
+               v = readl(sc);
+               dev_info(priv->dev, "SystemControl %x\n", v);
+               /* crappy switch to be moved */
+               switch (v) {
+               case 0: /* A83T */
+                       do_ephy_clk = 0;
+                       switch (priv->phy_interface) {
+                       case PHY_INTERFACE_MODE_MII:
+                               v &= ~BIT(2);
+                               break;
+                       case PHY_INTERFACE_MODE_RGMII:
+                               v |= BIT(1);
+                               v |= BIT(2);
+                               v |= BIT(15);
+                               break;
+                       case PHY_INTERFACE_MODE_GMII:
+                               v &= ~BIT(2);
+                               break;
+                       default:
+                               dev_err(priv->dev, "Unknown PHY type %d\n", 
priv->phy_interface);
+                       }
+                       break;
+               case 0x58000: /* H3 */
+                       switch (priv->phy_interface) {
+                       case PHY_INTERFACE_MODE_MII:
+                               /* PHY_SELECT: Internal PHY */
+                               v |= BIT(15);
+                               /* SHUTDOWN: Power up */
+                               v &= ~BIT(16);
+                               /* 24 Mhz */
+                               /*v &= ~BIT(18);*/
+                               /* LED POL */
+                               v |= BIT(17);
+                               break;
+                       case PHY_INTERFACE_MODE_RGMII:
+                               v |= BIT(1);
+                               v |= BIT(2);
+                               /* External PHY */
+                               v &= ~BIT(15);
+                               /* SHUTDOWN: Shutdown */
+                               v |= BIT(16);
+                               break;
+                               /* TODO RMII */
+                       default:
+                               dev_err(priv->dev, "Unknown PHY type %d\n", 
priv->phy_interface);
+                       }
+                       break;
+               default:
+                       dev_err(priv->dev, "Unknown platform %x\n", v);
+               }
+               dev_info(priv->dev, "SystemControl %x\n", v);
+               writel(v, sc);
+               iounmap(sc);
+       }
+       /* end phy clock */
+
+       /* PWM */
+       sc = ioremap(0x01C21400, 0x20);
+       if (sc) {
+               v = readl(sc);
+               dev_info(priv->dev, "PWM %x\n", v);
+               v = readl(sc + 0x04);
+               dev_info(priv->dev, "PWM %x\n", v);
+               iounmap(sc);
+       }
+
+       if (do_ephy_clk == 1) {
+               priv->miiclk = devm_clk_get(priv->dev, "ahb1_ephy");
+               if (IS_ERR(priv->miiclk)) {
+                       err = PTR_ERR(priv->miiclk);
+                       dev_err(priv->dev, "Cannot get MII clock err=%d\n", 
err);
+                       return err;
+               }
+               err = clk_prepare_enable(priv->miiclk);
+               if (err != 0) {
+                       dev_err(priv->dev, "Cannot prepare_enable PHY\n");
+                       return err;
+               } else {
+                       dev_info(priv->dev, "PHY clk is enabled\n");
+               }
+
+               priv->rst_phy = devm_reset_control_get(priv->dev, "ephy");
+               if (IS_ERR(priv->rst_phy)) {
+                       err = PTR_ERR(priv->rst_phy);
+                       dev_info(priv->dev, "no PHY reset control found %d\n", 
err);
+                       priv->rst_phy = NULL;
+               }
+               if (priv->rst_phy) {
+                       err = reset_control_deassert(priv->rst_phy);
+                       if (err)
+                               dev_err(priv->dev, "Cannot deassert PHY\n");
+                       else
+                               dev_info(priv->dev, "PHY is de-asserted\n");
+               }
+               msleep(300);
+       }
+
+       new_bus = mdiobus_alloc();
+       if (!new_bus) {
+               netdev_err(ndev, "Failed to alloc new mdio bus\n");
+               return -ENOMEM;
+       }
+
+       new_bus->name = dev_name(priv->dev);
+       new_bus->read = &sun8i_mdio_read;
+       new_bus->write = &sun8i_mdio_write;
+       new_bus->reset = &sun8i_mdio_reset;
+       snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", new_bus->name, 0);
+
+       new_bus->parent = priv->dev;
+       new_bus->priv = ndev;
+
+       err = mdiobus_register(new_bus);
+       if (err) {
+               dev_err(priv->dev, "%s: Cannot register as MDIO bus\n",
+                       new_bus->name);
+               goto mdiobus_err;
+       }
+
+       priv->mii = new_bus;
+
+       phydev = phy_find_first(new_bus);
+       if (!phydev) {
+               netdev_err(ndev, "No PHY found!\n");
+               err = PTR_ERR(phydev);
+               goto mdiobus_unreg;
+       }
+
+       phydev->irq = PHY_POLL;
+
+       /*priv->phy_interface = PHY_INTERFACE_MODE_MII;*/
+
+/*     phydev = of_phy_connect(ndev, */
+       phydev = phy_connect(ndev, dev_name(&phydev->dev),
+                            &sun8i_phy_adjust_link, priv->phy_interface);
+
+       if (IS_ERR(phydev)) {
+               netdev_err(ndev, "Could not attach to PHY\n");
+               err = PTR_ERR(phydev);
+               goto mdiobus_unreg;
+       }
+
+       netdev_info(ndev, "%s: PHY ID %08x at %d IRQ %s (%s)\n",
+                   ndev->name, phydev->phy_id, phydev->addr,
+                   "poll", dev_name(&phydev->dev));
+
+       phy_write(phydev, MII_BMCR, BMCR_RESET);
+       timeout = 0;
+       while (BMCR_RESET & phy_read(phydev, MII_BMCR) && timeout++ < 15)
+               msleep(3);
+       if (timeout >= 15)
+               dev_warn(priv->dev, "PHY reset timeout\n");
+
+       value = phy_read(phydev, MII_BMCR);
+       phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN));
+
+       dev_info(priv->dev, "PHY supported %x\n", phydev->supported);
+       /*phydev->supported &= PHY_GBIT_FEATURES;*/
+       phydev->advertising = phydev->supported;
+       phy_print_status(phydev);
+
+       return 0;
+
+mdiobus_unreg:
+       mdiobus_unregister(new_bus);
+mdiobus_err:
+       mdiobus_free(new_bus);
+       return err;
+}
+
+static int sun8i_emac_set_mac_address(struct net_device *ndev, void *p)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s\n", __func__);
+       sun8i_emac_set_macaddr(priv, p, 0);
+       return 0;
+}
+
+static int sun8i_emac_open(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       int err;
+       u32 v, dr, dt, vtxphy;
+       struct dma_desc *ddesc;
+       int i;
+       int timeout = 0;
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       err = sun8i_phy_init(ndev);
+       if (err)
+               return err;
+
+       /* Do SOFT RST */
+       v = readl(priv->base + SUN8I_EMAC_BASIC_CTL1);
+       writel(v | 0x01, priv->base + SUN8I_EMAC_BASIC_CTL1);
+
+       /* wait for reset to be ended */
+       do {
+               v = readl(priv->base + SUN8I_EMAC_BASIC_CTL1);
+       } while ((v & 0x01) != 0 && timeout++ < 50);
+       if (timeout >= 50)
+               dev_warn(priv->dev, "EMAC reset timeout\n");
+
+       /* DMA */
+       v = (8 << 24);/* burst len */
+       writel(v, priv->base + SUN8I_EMAC_BASIC_CTL1);
+#define RX_INT                  BIT(8)
+#define TX_INT                  BIT(0)
+#define TX_UNF_INT              BIT(4)
+       writel(RX_INT | TX_INT | TX_UNF_INT, priv->base + SUN8I_EMAC_INT_EN);
+       v = readl(priv->base + SUN8I_EMAC_INT_EN);
+       dev_info(priv->dev, "INT %x\n", v);
+
+       v = readl(priv->base + SUN8I_EMAC_TX_CTL0);
+       /* TX_FRM_LEN_CL */
+       /*v |= BIT(30);*/
+       writel(v, priv->base + SUN8I_EMAC_TX_CTL0);
+
+       v = readl(priv->base + SUN8I_EMAC_RX_CTL0);
+       /* CHECK_CRC */
+       v |= BIT(27);
+       /* STRIP_FCS */
+       v |= BIT(28);
+       /* JUMBO_FRM_EN */
+       v |= BIT(29);
+       writel(v, priv->base + SUN8I_EMAC_RX_CTL0);
+       dev_info(priv->dev, "SUN8I_EMAC_RX_CTL0 %x\n", v);
+
+       /* TODO MDC must be set according to clock */
+       priv->mdc = 0x02;
+       writel((priv->mdc << 20), priv->base + SUN8I_EMAC_MDIO_CMD); /* 
MDC_DIV_RATIO */
+
+       v = readl(priv->base + SUN8I_EMAC_TX_CTL1);
+       /* TX_MD Transmission starts after a full frame located in TX DMA FIFO 
*/
+       v |= BIT(1);
+       writel(v, priv->base + SUN8I_EMAC_TX_CTL1);
+
+       v = readl(priv->base + SUN8I_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 + SUN8I_EMAC_RX_CTL1);
+
+       sun8i_emac_set_mac_address(ndev, ndev->dev_addr);
+
+       priv->rx_sk = kcalloc(nbdesc, sizeof(struct sk_buff *), GFP_KERNEL);
+       if (!priv->rx_sk) {
+               err = -ENOMEM;
+               goto rx_sk_error;
+       }
+       priv->tx_sk = kcalloc(nbdesc, sizeof(struct sk_buff *), GFP_KERNEL);
+       if (!priv->tx_sk) {
+               err = -ENOMEM;
+               goto tx_sk_error;
+       }
+
+       priv->dd_rx = dma_alloc_coherent(priv->dev,
+                       nbdesc * sizeof(struct dma_desc),
+                       &priv->dd_rx_phy,
+                       GFP_KERNEL);
+       if (!priv->dd_rx) {
+               dev_err(priv->dev, "ERROR: cannot DMA RX");
+               err = -ENOMEM;
+               goto dma_rx_error;
+       }
+       memset(priv->dd_rx, 0, nbdesc * sizeof(struct dma_desc));
+       ddesc = priv->dd_rx;
+       for (i = 0; i < nbdesc; i++) {
+               sun8i_emac_rx_sk(ndev, i);
+               ddesc->next = (u32)priv->dd_rx_phy + (i + 1) * sizeof(struct 
dma_desc);
+               dev_info(priv->dev, "%d %pad to %x (base is %pad)",
+                        i, &ddesc, ddesc->next, &priv->dd_rx);
+               ddesc++;
+       }
+       /* last descriptor point back to first one */
+       ddesc--;
+       ddesc->next = (u32)priv->dd_rx_phy;
+
+       priv->dd_tx = dma_alloc_coherent(priv->dev,
+                       nbdesc * sizeof(struct dma_desc),
+                       &priv->dd_tx_phy,
+                       GFP_KERNEL);
+       if (!priv->dd_tx) {
+               dev_err(priv->dev, "ERROR: cannot DMA TX");
+               err = -ENOMEM;
+               goto dma_tx_error;
+       }
+       memset(priv->dd_tx, 0, nbdesc * sizeof(struct dma_desc));
+       ddesc = priv->dd_tx;
+       for (i = 0; i < nbdesc; i++) {
+               ddesc->status = 0;
+               ddesc->st = 0;
+               ddesc->next = (u32)(priv->dd_tx_phy + (i + 1) * sizeof(struct 
dma_desc));
+               dev_info(priv->dev, "TXdesc %02d %p to %x",
+                        i, ddesc, ddesc->next);
+               ddesc++;
+       }
+       /* last descriptor point back to first one */
+       ddesc--;
+       ddesc->next = (u32)priv->dd_tx_phy;
+
+       if (ndev->phydev) {
+               dev_info(priv->dev, "on start le phy\n");
+               phy_start(ndev->phydev);
+       }
+
+       writel(priv->dd_rx_phy, priv->base + SUN8I_EMAC_RX_DESC_LIST);
+       v = readl(priv->base + SUN8I_EMAC_RX_CTL1);
+       v |= BIT(30);
+       /*v |= (1 << 31);*/
+       /*dev_info(priv->dev, "%s %lx %lx\n", __func__, 0x40000000, BIT(30));*/
+       writel(v, priv->base + SUN8I_EMAC_RX_CTL1);
+       dev_info(priv->dev, "SUN8I_EMAC_RX_CTL1 %x\n", v);
+
+       writel(priv->dd_tx_phy, priv->base + SUN8I_EMAC_TX_DESC_LIST);
+       v = readl(priv->base + SUN8I_EMAC_TX_CTL1);
+       v |= BIT(30);
+/*     v |= (1 << 31);*/
+       writel(v, priv->base + SUN8I_EMAC_TX_CTL1);
+       dev_info(priv->dev, "SUN8I_EMAC_TX_CTL1 %x\n", v);
+
+       /* activate TX */
+       v = readl(priv->base + SUN8I_EMAC_TX_CTL0);
+       v |= (1 << 31);
+       writel(v, priv->base + SUN8I_EMAC_TX_CTL0);
+       dev_info(priv->dev, "SUN8I_EMAC_TX_CTL0 %x\n", v);
+
+       /* activate RX */
+       v = readl(priv->base + SUN8I_EMAC_RX_CTL0);
+       v |= (1 << 31);
+       writel(v, priv->base + SUN8I_EMAC_RX_CTL0);
+       dev_info(priv->dev, "SUN8I_EMAC_RX_CTL0 %x\n", v);
+
+       writel(0x3FFF, priv->base + SUN8I_EMAC_INT_STA);
+       netif_start_queue(ndev);
+
+       v = readl(priv->base + SUN8I_EMAC_RX_DESC_LIST);
+       vtxphy = readl(priv->base + SUN8I_EMAC_TX_DESC_LIST);
+       dt = readl(priv->base + SUN8I_EMAC_TX_DMA_STA);
+       dr = readl(priv->base + SUN8I_EMAC_RX_DMA_STA);
+       dev_info(priv->dev, "Verify DMA RX LIST %x %x %x %x TXphy=%x/%x\n", v, 
priv->dd_rx_phy, dr, dt, vtxphy, priv->dd_tx_phy);
+
+       return 0;
+dma_tx_error:
+       dma_free_coherent(priv->dev, nbdesc * sizeof(struct dma_desc),
+                         priv->dd_rx, priv->dd_rx_phy);
+dma_rx_error:
+       kfree(priv->tx_sk);
+tx_sk_error:
+       kfree(priv->rx_sk);
+rx_sk_error:
+       return err;
+}
+
+static int sun8i_emac_stop(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       /* TODO */
+
+       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;
+       int i = 0;
+       unsigned int len;
+       char *p;
+       u32 v;
+
+       p = skb->data;
+       len = skb_headlen(skb);
+
+       dev_info(priv->dev, "%s xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx len=%u %x %x 
%x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", __func__, len,
+               p[0], p[1], p[2], p[3],
+               p[4], p[5], p[6], p[7],
+               p[8], p[9], p[10], p[11],
+               p[12], p[13], p[14], p[15]
+               );
+
+       spin_lock(&priv->tx_lock);
+       /* find first empty descriptor not optimized */
+       while (i < nbdesc) {
+               ddesc = priv->dd_tx + i;
+               /*if ((ddesc->status & BIT(31)) == 0) {*/
+               if (ddesc->status == 0) {
+                       dev_info(priv->dev, "%s found slot %d at %pad\n",
+                                __func__, i, &ddesc);
+                       break;
+               }
+               i++;
+       }
+       if (i >= nbdesc) {
+               dev_err(priv->dev, "ERROR: TX is full\n");
+               spin_unlock(&priv->tx_lock);
+               return NETDEV_TX_OK; /* TODO DEBUG */
+               return NETDEV_TX_BUSY;
+       }
+
+       priv->tx_sk[i] = skb;
+       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 dmamap buf\n");
+               return -EFAULT;
+       }
+
+       ddesc->st = len;
+       /* TODO remove/test 12 - 28 */
+       ddesc->st |= BIT(12);
+       ddesc->st |= BIT(13);
+       ddesc->st |= BIT(14);
+       ddesc->st |= BIT(15);
+       ddesc->st |= BIT(16);
+       ddesc->st |= BIT(17);
+       ddesc->st |= BIT(18);
+       ddesc->st |= BIT(19);
+       ddesc->st |= BIT(20);
+       ddesc->st |= BIT(21);
+       ddesc->st |= BIT(22);
+       ddesc->st |= BIT(23);
+       ddesc->st |= BIT(24);
+       ddesc->st |= BIT(25);
+       ddesc->st |= BIT(26);
+       ddesc->st |= BIT(27);
+       ddesc->st |= BIT(28);
+       /* frame begin */
+       ddesc->st |= BIT(29);
+       /* frame end */
+       ddesc->st |= BIT(30);
+       /* We want an interrupt after transmission */
+       ddesc->st |= BIT(31);
+       /* DMA can work on it */
+       ddesc->status = BIT(31);
+
+/*     v = readl(priv->base + SUN8I_EMAC_INT_STA);
+       writel(v & 0x3FFF, priv->base + SUN8I_EMAC_INT_STA);
+*/
+       v = readl(priv->base + SUN8I_EMAC_TX_CTL0);
+       v |= BIT(31);/* TODO do a define */
+       writel(v, priv->base + SUN8I_EMAC_TX_CTL0);
+       v = readl(priv->base + SUN8I_EMAC_TX_CTL1);
+       v |= BIT(31);/* mandatory */
+       v |= BIT(30);/* TODO do a define */
+       writel(v, priv->base + SUN8I_EMAC_TX_CTL1);
+
+       ndev->stats.tx_bytes += skb->len;
+
+       spin_unlock(&priv->tx_lock);
+
+       return NETDEV_TX_OK;
+}
+
+static int sun8i_emac_change_mtu(struct net_device *ndev, int new_mtu)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       int max_mtu;
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       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 netdev_features_t sun8i_emac_fix_features(struct net_device *ndev,
+                                                netdev_features_t features)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s %llx\n", __func__, features);
+       return features;
+}
+
+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 + SUN8I_EMAC_BASIC_CTL0);
+       if (features & NETIF_F_LOOPBACK && netif_running(ndev)) {
+               dev_info(priv->dev, "Must set loopback\n");
+               v |= BIT(1);
+       } else {
+               dev_info(priv->dev, "Must unset loopback\n");
+               v &= ~BIT(1);
+       }
+       writel(v, priv->base + SUN8I_EMAC_BASIC_CTL0);
+
+       dev_info(priv->dev, "%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);
+
+       dev_info(priv->dev, "%s\n", __func__);
+       /* TODO netdev_for_each_mc_addr and IFF_PROMISC */
+}
+
+static void sun8i_emac_tx_timeout(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s\n", __func__);
+       /* TODO reset/re-init all (see stmmac)*/
+}
+
+static int sun8i_emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       int ret = 0;
+
+       dev_info(priv->dev, "%s %x\n", __func__, cmd);
+
+       if (!netif_running(ndev))
+               return -EINVAL;
+
+       switch (cmd) {
+       case SIOCGMIIPHY:
+       case SIOCGMIIREG:
+       case SIOCSMIIREG:
+               if (!ndev->phydev)
+                       return -EINVAL;
+               ret = phy_mii_ioctl(ndev->phydev, rq, cmd);
+               break;
+       case SIOCSHWTSTAMP:
+               /*ret = stmmac_hwtstamp_ioctl(dev, rq);*/
+               return -EINVAL;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int sun8i_emac_config(struct net_device *ndev, struct ifmap *map)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s\n", __func__);
+       return -EINVAL;
+}
+
+static void sun8i_emac_poll_controller(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s\n", __func__);
+}
+
+static int sun8i_emac_check_if_running(struct net_device *ndev)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s %d\n", __func__, netif_running(ndev));
+       if (!netif_running(ndev))
+               return -EBUSY;
+       return 0;
+}
+
+static int sun8i_emac_get_sset_count(struct net_device *ndev, int sset)
+{
+       int len;
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       switch (sset) {
+       case ETH_SS_STATS:
+               len = 0;
+               return len;
+       default:
+               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);
+       int rc;
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       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;
+       }
+
+       cmd->transceiver = XCVR_INTERNAL;
+       rc = phy_ethtool_gset(phy, cmd);
+
+       return rc;
+}
+
+static int sun8i_emac_ethtool_set_settings(struct net_device *ndev,
+                                          struct ethtool_cmd *cmd)
+{
+       struct phy_device *phy = ndev->phydev;
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       int rc;
+
+       dev_info(priv->dev, "%s\n", __func__);
+
+       rc = phy_ethtool_sset(phy, cmd);
+
+       return rc;
+}
+
+static void sun8i_emac_ethtool_getdrvinfo(struct net_device *ndev,
+                                         struct ethtool_drvinfo *info)
+{
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       dev_info(priv->dev, "%s\n", __func__);
+       strlcpy(info->driver, "sun8i_emac", sizeof(info->driver));
+       strcpy(info->version, "00");
+       info->fw_version[0] = '\0';
+}
+
+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 = NULL,
+       .set_pauseparam = NULL,
+       .get_ethtool_stats = NULL,
+       .get_strings = NULL,
+       .get_wol = NULL,
+       .set_wol = NULL,
+       .get_sset_count = sun8i_emac_get_sset_count,
+       .get_drvinfo = sun8i_emac_ethtool_getdrvinfo,
+};
+
+static const struct net_device_ops sun8i_emac_netdev_ops = {
+       .ndo_init = NULL,
+       .ndo_open = sun8i_emac_open,
+       .ndo_start_xmit = sun8i_emac_xmit,
+       .ndo_stop = sun8i_emac_stop,
+       .ndo_change_mtu = sun8i_emac_change_mtu,
+       .ndo_fix_features = sun8i_emac_fix_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_config = sun8i_emac_config,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller = sun8i_emac_poll_controller,
+#endif
+       .ndo_set_mac_address = sun8i_emac_set_mac_address,
+       .ndo_set_features = sun8i_emac_set_features,
+};
+
+static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id)
+{
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+       u32 v, u;
+       struct dma_desc *ddesc;
+
+       v = readl(priv->base + SUN8I_EMAC_INT_STA);
+
+       dev_info(priv->dev, "%s %x\n", __func__, v);
+
+       /* When this bit is asserted, a frame transmission is completed. */
+       if (v & BIT(0))
+               sun8i_emac_complete_xmit(ndev);
+
+       /* When this bit is asserted, the TX DMA FSM is stopped. */
+       if (v & BIT(1)) {
+               u = readl(priv->base + SUN8I_EMAC_TX_CUR_DDESC);
+               dev_info(priv->dev, "TX DMA currddesc=%x\n", u);
+               u = readl(priv->base + SUN8I_EMAC_TX_CTL1);
+               dev_info(priv->dev, "Re-start TX DMA %x\n", u);
+               writel(u | BIT(31), priv->base + SUN8I_EMAC_TX_CTL1);
+       }
+       /* When this asserted, the TX DMA can not acquire next TX descriptor
+        * and TX DMA FSM is suspended.
+       */
+       if (v & BIT(2)) {
+               u = readl(priv->base + SUN8I_EMAC_TX_CUR_DDESC);
+               dev_info(priv->dev, "TX DMA currddesc=%x\n", u);
+               ddesc = priv->dd_tx;
+               ddesc->status = BIT(31);
+               /*writel(u + 0x10, priv->base + SUN8I_EMAC_TX_DESC_LIST);*/
+
+               u = readl(priv->base + SUN8I_EMAC_TX_CTL1);
+               dev_info(priv->dev, "Re-run TX DMA %x\n", u);
+               writel(u | BIT(31), priv->base + SUN8I_EMAC_TX_CTL1);
+       }
+
+       if (v & BIT(3))
+               dev_info(priv->dev, "Unhandled interrupt TX TIMEOUT\n");
+       if (v & BIT(4))
+               dev_info(priv->dev, "Unhandled interrupt TX EARLY\n");
+
+       /* When this bit asserted , the frame is transmitted to FIFO totally. */
+       if (v & BIT(5))
+               dev_info(priv->dev, "Unhandled interrupt TX_EARLY_INT\n");
+
+       /* When this bit is asserted, a frame reception is completed  */
+       if (v & BIT(8))
+               sun8i_emac_receive_all(ndev);
+
+       /* When this asserted, the RX DMA can not acquire next TX descriptor
+        * and TX DMA FSM is suspended.
+       */
+       if (v & BIT(9)) {
+               u = readl(priv->base + SUN8I_EMAC_RX_CTL1);
+               dev_info(priv->dev, "Re-run RX DMA %x\n", u);
+               writel(u | BIT(31), priv->base + SUN8I_EMAC_RX_CTL1);
+       }
+
+       if (v & BIT(10))
+               dev_info(priv->dev, "Unhandled interrupt RX_DMA_STOPPED_INT\n");
+       if (v & BIT(11))
+               dev_info(priv->dev, "Unhandled interrupt RX_TIMEOUT\n");
+       if (v & BIT(12))
+               dev_info(priv->dev, "Unhandled interrupt RX OVERFLOW\n");
+       if (v & BIT(13))
+               dev_info(priv->dev, "Unhandled interrupt RX EARLY\n");
+       if (v & BIT(16))
+               dev_info(priv->dev, "Unhandled interrupt RGMII\n");
+
+       writel(v & 0x3FFF, priv->base + SUN8I_EMAC_INT_STA);
+
+       return IRQ_HANDLED;
+}
+
+static int sun8i_emac_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct sun8i_emac_priv *priv;
+       struct net_device *ndev;
+       int ret;
+
+       if (!pdev->dev.of_node)
+               return -ENODEV;
+
+       ndev = alloc_etherdev(sizeof(*priv));
+       if (!ndev)
+               return -ENOMEM;
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+       priv = netdev_priv(ndev);
+       platform_set_drvdata(pdev, ndev);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->base)) {
+               dev_err(&pdev->dev, "Cannot request MMIO\n");
+               return PTR_ERR(priv->base);
+       }
+
+       priv->irq = platform_get_irq(pdev, 0);
+       if (priv->irq < 0) {
+               dev_err(&pdev->dev, "Cannot claim IRQ\n");
+               return priv->irq;
+       }
+
+       ret = devm_request_irq(&pdev->dev, priv->irq, sun8i_emac_dma_interrupt,
+                              0, dev_name(&pdev->dev), ndev);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot request IRQ\n");
+               return ret;
+       }
+
+       priv->busclk = devm_clk_get(&pdev->dev, "ahb2_gmac");
+       if (IS_ERR(priv->busclk)) {
+               ret = PTR_ERR(priv->busclk);
+               dev_err(&pdev->dev, "Cannot get AHB clock err=%d\n", ret);
+               return ret;
+       }
+       ret = clk_prepare_enable(priv->busclk);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Cannot prepare_enable busclk\n");
+               return ret;
+       }
+/*
+       cr = clk_get_rate(priv->miiclk);
+       dev_info(&pdev->dev, "Current MII clkrate %lu\n", cr);
+
+       ret = clk_set_rate(priv->miiclk, cr / 4);
+       if (ret)
+               dev_err(&pdev->dev, "Cannot change ephy clock speed %d", ret);
+       ret = clk_set_rate(priv->busclk, 125 * 1000 * 1000);
+       if (ret)
+               dev_err(&pdev->dev, "Cannot change emac clock speed %d", ret);
+*/
+       priv->rst_mac = devm_reset_control_get_optional(&pdev->dev, "ahb");
+       if (IS_ERR(priv->rst_mac)) {
+               ret = PTR_ERR(priv->rst_mac);
+               dev_info(&pdev->dev, "no mac reset control found %d\n", ret);
+               priv->rst_mac = NULL;
+       }
+       if (priv->rst_mac) {
+               ret = reset_control_deassert(priv->rst_mac);
+               if (ret)
+                       dev_info(&pdev->dev, "Cannot deassert mac\n");
+       }
+
+       spin_lock_init(&priv->lock);
+       spin_lock_init(&priv->tx_lock);
+
+       ether_setup(ndev);
+       ndev->netdev_ops = &sun8i_emac_netdev_ops;
+       ndev->ethtool_ops = &sun8i_emac_ethtool_ops;
+
+       priv->ndev = ndev;
+       priv->dev = &pdev->dev;
+
+       ndev->base_addr = (unsigned long)priv->base;
+       ndev->irq = priv->irq;
+
+       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_LOOPBACK;
+       ndev->priv_flags |= IFF_UNICAST_FLT;
+
+       ndev->watchdog_timeo = msecs_to_jiffies(5000);
+
+       /*stmmac_set_ethtool_ops(ndev);*/
+
+       ret = register_netdev(ndev);
+       if (ret) {
+               dev_err(&pdev->dev, "ERROR: Register %s failed\n", ndev->name);
+               return ret;
+       }
+
+       sun8i_emac_set_macaddr(priv, ndev->dev_addr, 0);
+
+       return 0;
+}
+
+static int sun8i_emac_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct sun8i_emac_priv *priv = netdev_priv(ndev);
+
+       clk_disable_unprepare(priv->busclk);
+       clk_disable_unprepare(priv->miiclk);
+       if (priv->rst_mac)
+               reset_control_assert(priv->rst_mac);
+       if (priv->rst_phy)
+               reset_control_assert(priv->rst_phy);
+
+       platform_set_drvdata(pdev, NULL);
+       free_netdev(ndev);
+       return 0;
+}
+
+static const struct of_device_id sun8i_emac_of_match_table[] = {
+       { .compatible = "allwinner,sun8i-h3-emac" },
+       {}
+};
+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_platform_driver(sun8i_emac_driver);
+
+MODULE_DESCRIPTION("SUN8I Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("LABBE Corentin <clabbe.montj...@gmail.co");
-- 
2.4.10

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to linux-sunxi+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to