The UniPhier platform from Socionext provides the AVE ethernet
controller that includes MAC and MDIO bus supporting RGMII/RMII
modes. The controller is named AVE.

Signed-off-by: Kunihiko Hayashi <hayashi.kunih...@socionext.com>
Signed-off-by: Jassi Brar <jaswinder.si...@linaro.org>
---
 drivers/net/ethernet/Kconfig             |    1 +
 drivers/net/ethernet/Makefile            |    1 +
 drivers/net/ethernet/socionext/Kconfig   |   22 +
 drivers/net/ethernet/socionext/Makefile  |    4 +
 drivers/net/ethernet/socionext/sni_ave.c | 1746 ++++++++++++++++++++++++++++++
 5 files changed, 1774 insertions(+)
 create mode 100644 drivers/net/ethernet/socionext/Kconfig
 create mode 100644 drivers/net/ethernet/socionext/Makefile
 create mode 100644 drivers/net/ethernet/socionext/sni_ave.c

diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index c604213..d50519e 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -170,6 +170,7 @@ source "drivers/net/ethernet/sis/Kconfig"
 source "drivers/net/ethernet/sfc/Kconfig"
 source "drivers/net/ethernet/sgi/Kconfig"
 source "drivers/net/ethernet/smsc/Kconfig"
+source "drivers/net/ethernet/socionext/Kconfig"
 source "drivers/net/ethernet/stmicro/Kconfig"
 source "drivers/net/ethernet/sun/Kconfig"
 source "drivers/net/ethernet/tehuti/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index a0a03d4..9f55b36 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_SFC) += sfc/
 obj-$(CONFIG_SFC_FALCON) += sfc/falcon/
 obj-$(CONFIG_NET_VENDOR_SGI) += sgi/
 obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
+obj-$(CONFIG_NET_VENDOR_SOCIONEXT) += socionext/
 obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
 obj-$(CONFIG_NET_VENDOR_SUN) += sun/
 obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/
diff --git a/drivers/net/ethernet/socionext/Kconfig 
b/drivers/net/ethernet/socionext/Kconfig
new file mode 100644
index 0000000..3a1829e
--- /dev/null
+++ b/drivers/net/ethernet/socionext/Kconfig
@@ -0,0 +1,22 @@
+config NET_VENDOR_SOCIONEXT
+       bool "Socionext ethernet drivers"
+       default y
+       ---help---
+         Option to select ethernet drivers for Socionext platforms.
+
+         Note that the answer to this question doesn't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about Socionext devices. If you say Y, you will be asked
+         for your specific card in the following questions.
+
+if NET_VENDOR_SOCIONEXT
+
+config SNI_AVE
+       tristate "Socionext AVE ethernet support"
+       depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
+       select PHYLIB
+       ---help---
+         Driver for gigabit ethernet MACs, called AVE, in the
+         Socionext UniPhier family.
+
+endif #NET_VENDOR_SOCIONEXT
diff --git a/drivers/net/ethernet/socionext/Makefile 
b/drivers/net/ethernet/socionext/Makefile
new file mode 100644
index 0000000..0356341
--- /dev/null
+++ b/drivers/net/ethernet/socionext/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for all ethernet ip drivers on Socionext platforms
+#
+obj-$(CONFIG_SNI_AVE) += sni_ave.o
diff --git a/drivers/net/ethernet/socionext/sni_ave.c 
b/drivers/net/ethernet/socionext/sni_ave.c
new file mode 100644
index 0000000..ed0f4a3
--- /dev/null
+++ b/drivers/net/ethernet/socionext/sni_ave.c
@@ -0,0 +1,1746 @@
+/**
+ * sni_ave.c - Socionext UniPhier AVE ethernet driver
+ *
+ * Copyright 2014 Panasonic Corporation
+ * Copyright 2015-2017 Socionext Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * 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/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+#include <linux/u64_stats_sync.h>
+
+/* General Register Group */
+#define AVE_IDR                0x0     /* ID */
+#define AVE_VR         0x4     /* Version */
+#define AVE_GRR                0x8     /* Global Reset */
+#define AVE_CFGR       0xc     /* Configuration */
+
+/* Interrupt Register Group */
+#define AVE_GIMR       0x100   /* Global Interrupt Mask */
+#define AVE_GISR       0x104   /* Global Interrupt Status */
+
+/* MAC Register Group */
+#define AVE_TXCR       0x200   /* TX Setup */
+#define AVE_RXCR       0x204   /* RX Setup */
+#define AVE_RXMAC1R    0x208   /* MAC address (lower) */
+#define AVE_RXMAC2R    0x20c   /* MAC address (upper) */
+#define AVE_MDIOCTR    0x214   /* MDIO Control */
+#define AVE_MDIOAR     0x218   /* MDIO Address */
+#define AVE_MDIOWDR    0x21c   /* MDIO Data */
+#define AVE_MDIOSR     0x220   /* MDIO Status */
+#define AVE_MDIORDR    0x224   /* MDIO Rd Data */
+
+/* Descriptor Control Register Group */
+#define AVE_DESCC      0x300   /* Descriptor Control */
+#define AVE_TXDC       0x304   /* TX Descriptor Configuration */
+#define AVE_RXDC0      0x308   /* RX Descriptor Ring0 Configuration */
+#define AVE_IIRQC      0x34c   /* Interval IRQ Control */
+
+/* Packet Filter Register Group */
+#define AVE_PKTF_BASE          0x800   /* PF Base Address */
+#define AVE_PFMBYTE_BASE       0xd00   /* PF Mask Byte Base Address */
+#define AVE_PFMBIT_BASE                0xe00   /* PF Mask Bit Base Address */
+#define AVE_PFSEL_BASE         0xf00   /* PF Selector Base Address */
+#define AVE_PFEN               0xffc   /* Packet Filter Enable */
+#define AVE_PKTF(ent)          (AVE_PKTF_BASE + (ent) * 0x40)
+#define AVE_PFMBYTE(ent)       (AVE_PFMBYTE_BASE + (ent) * 8)
+#define AVE_PFMBIT(ent)                (AVE_PFMBIT_BASE + (ent) * 4)
+#define AVE_PFSEL(ent)         (AVE_PFSEL_BASE + (ent) * 4)
+
+/* 64bit descriptor memory */
+#define AVE_DESC_SIZE_64       12      /* Descriptor Size */
+
+#define AVE_TXDM_64            0x1000  /* Tx Descriptor Memory */
+#define AVE_RXDM_64            0x1c00  /* Rx Descriptor Memory */
+
+#define AVE_TXDM_SIZE_64       0x0ba0  /* Tx Descriptor Memory Size 3KB */
+#define AVE_RXDM_SIZE_64       0x6000  /* Rx Descriptor Memory Size 24KB */
+
+/* 32bit descriptor memory */
+#define AVE_DESC_SIZE_32       8       /* Descriptor Size */
+
+#define AVE_TXDM_32            0x1000  /* Tx Descriptor Memory */
+#define AVE_RXDM_32            0x1800  /* Rx Descriptor Memory */
+
+#define AVE_TXDM_SIZE_32       0x07c0  /* Tx Descriptor Memory Size 2KB */
+#define AVE_RXDM_SIZE_32       0x4000  /* Rx Descriptor Memory Size 16KB */
+
+/* RMII Bridge Register Group */
+#define AVE_RSTCTRL            0x8028  /* Reset control */
+#define AVE_RSTCTRL_RMIIRST    BIT(16)
+#define AVE_LINKSEL            0x8034  /* Link speed setting */
+#define AVE_LINKSEL_100M       BIT(0)
+
+/* AVE_GRR */
+#define AVE_GRR_RXFFR          BIT(5)  /* Reset RxFIFO */
+#define AVE_GRR_PHYRST         BIT(4)  /* Reset external PHY */
+#define AVE_GRR_GRST           BIT(0)  /* Reset all MAC */
+
+/* AVE_GISR (common with GIMR) */
+#define AVE_GI_PHY             BIT(24) /* PHY interrupt */
+#define AVE_GI_TX              BIT(16) /* Tx complete */
+#define AVE_GI_RXERR           BIT(8)  /* Receive frame more than max size */
+#define AVE_GI_RXOVF           BIT(7)  /* Overflow at the RxFIFO */
+#define AVE_GI_RXDROP          BIT(6)  /* Drop packet */
+#define AVE_GI_RXIINT          BIT(5)  /* Interval interrupt */
+
+/* AVE_TXCR */
+#define AVE_TXCR_FLOCTR                BIT(18) /* Flow control */
+#define AVE_TXCR_TXSPD_1G      BIT(17)
+#define AVE_TXCR_TXSPD_100     BIT(16)
+
+/* AVE_RXCR */
+#define AVE_RXCR_RXEN          BIT(30) /* Rx enable */
+#define AVE_RXCR_FDUPEN                BIT(22) /* Interface mode */
+#define AVE_RXCR_FLOCTR                BIT(21) /* Flow control */
+#define AVE_RXCR_AFEN          BIT(19) /* MAC address filter */
+#define AVE_RXCR_DRPEN         BIT(18) /* Drop pause frame */
+#define AVE_RXCR_MPSIZ_MASK    GENMASK(10, 0)
+
+/* AVE_MDIOCTR */
+#define AVE_MDIOCTR_RREQ       BIT(3)  /* Read request */
+#define AVE_MDIOCTR_WREQ       BIT(2)  /* Write request */
+
+/* AVE_MDIOSR */
+#define AVE_MDIOSR_STS         BIT(0)  /* access status */
+
+/* AVE_DESCC */
+#define AVE_DESCC_TD           BIT(0)  /* Enable Tx descriptor */
+#define AVE_DESCC_RDSTP                BIT(4)  /* Pause Rx descriptor */
+#define AVE_DESCC_RD0          BIT(8)  /* Enable Rx descriptor Ring0 */
+#define AVE_DESCC_STATUS_MASK  GENMASK(31, 16)
+
+/* AVE_TXDC */
+#define AVE_TXDC_SIZE          GENMASK(27, 16) /* Size of Tx descriptor */
+#define AVE_TXDC_ADDR          GENMASK(11, 0)  /* Start address */
+#define AVE_TXDC_ADDR_START    0
+
+/* AVE_RXDC0 */
+#define AVE_RXDC0_SIZE         GENMASK(30, 16) /* Size of Rx descriptor */
+#define AVE_RXDC0_ADDR         GENMASK(14, 0)  /* Start address */
+#define AVE_RXDC0_ADDR_START   0
+
+/* AVE_IIRQC */
+#define AVE_IIRQC_EN0          BIT(27) /* Enable interval interrupt Ring0 */
+#define AVE_IIRQC_BSCK         GENMASK(15, 0)  /* Interval count unit */
+
+/* Command status for Descriptor */
+#define AVE_STS_OWN            BIT(31) /* Descriptor ownership */
+#define AVE_STS_INTR           BIT(29) /* Request for interrupt */
+#define AVE_STS_OK             BIT(27) /* Normal transmit */
+/* TX */
+#define AVE_STS_NOCSUM         BIT(28) /* No use HW checksum */
+#define AVE_STS_1ST            BIT(26) /* Head of buffer chain */
+#define AVE_STS_LAST           BIT(25) /* Tail of buffer chain */
+#define AVE_STS_OWC            BIT(21) /* Out of window,Late Collision */
+#define AVE_STS_EC             BIT(20) /* Excess collision occurred */
+#define AVE_STS_PKTLEN_TX_MASK GENMASK(15, 0)
+/* RX */
+#define AVE_STS_CSSV           BIT(21) /* Checksum check performed */
+#define AVE_STS_CSER           BIT(20) /* Checksum error detected */
+#define AVE_STS_PKTLEN_RX_MASK GENMASK(10, 0)
+
+/* AVE_CFGR */
+#define AVE_CFGR_FLE           BIT(31) /* Filter Function */
+#define AVE_CFGR_CHE           BIT(30) /* Checksum Function */
+#define AVE_CFGR_MII           BIT(27) /* Func mode (1:MII/RMII, 0:RGMII) */
+#define AVE_CFGR_IPFCEN                BIT(24) /* IP fragment sum Enable */
+
+#define AVE_MAX_ETHFRAME       1518
+
+/* Packet filter size */
+#define AVE_PF_SIZE            17      /* Number of all packet filter */
+#define AVE_PF_MULTICAST_SIZE  7       /* Number of multicast filter */
+
+/* Packet filter definition */
+#define AVE_PFNUM_FILTER       0       /* No.0 */
+#define AVE_PFNUM_UNICAST      1       /* No.1 */
+#define AVE_PFNUM_BROADCAST    2       /* No.2 */
+#define AVE_PFNUM_MULTICAST    11      /* No.11-17 */
+
+/* NETIF Message control */
+#define AVE_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV    |     \
+                                NETIF_MSG_PROBE  |     \
+                                NETIF_MSG_LINK   |     \
+                                NETIF_MSG_TIMER  |     \
+                                NETIF_MSG_IFDOWN |     \
+                                NETIF_MSG_IFUP   |     \
+                                NETIF_MSG_RX_ERR |     \
+                                NETIF_MSG_TX_ERR)
+
+/* Parameter for descriptor */
+#define AVE_NR_TXDESC          32      /* Tx descriptor */
+#define AVE_NR_RXDESC          64      /* Rx descriptor */
+
+#define AVE_DESC_OFS_CMDSTS    0
+#define AVE_DESC_OFS_ADDRL     4
+#define AVE_DESC_OFS_ADDRU     8
+
+/* Parameter for interrupt */
+#define AVE_INTM_COUNT         20
+#define AVE_FORCE_TXINTCNT     1
+
+#define IS_DESC_64BIT(p)       ((p)->data->is_desc_64bit)
+
+enum desc_id {
+       AVE_DESCID_TX = 0,
+       AVE_DESCID_RX,
+};
+
+enum desc_state {
+       AVE_DESC_STOP = 0,
+       AVE_DESC_START,
+       AVE_DESC_RX_SUSPEND,
+       AVE_DESC_RX_PERMIT,
+};
+
+struct ave_desc {
+       struct sk_buff  *skbs;
+       dma_addr_t      skbs_dma;
+       size_t          skbs_dmalen;
+};
+
+struct ave_desc_info {
+       u32     ndesc;          /* number of descriptor */
+       u32     daddr;          /* start address of descriptor */
+       u32     proc_idx;       /* index of processing packet */
+       u32     done_idx;       /* index of processed packet */
+       struct ave_desc *desc;  /* skb info related descriptor */
+};
+
+struct ave_soc_data {
+       bool    is_desc_64bit;
+};
+
+struct ave_stats {
+       struct  u64_stats_sync  syncp;
+       u64     packets;
+       u64     bytes;
+       u64     errors;
+       u64     dropped;
+       u64     collisions;
+       u64     fifo_errors;
+};
+
+struct ave_private {
+       void __iomem            *base;
+       int                     irq;
+       int                     phy_id;
+       unsigned int            desc_size;
+       u32                     msg_enable;
+       struct clk              *clk;
+       struct reset_control    *rst;
+       phy_interface_t         phy_mode;
+       struct phy_device       *phydev;
+       struct mii_bus          *mdio;
+
+       /* stats */
+       struct ave_stats        stats_rx;
+       struct ave_stats        stats_tx;
+
+       /* NAPI support */
+       struct net_device       *ndev;
+       struct napi_struct      napi_rx;
+       struct napi_struct      napi_tx;
+
+       /* descriptor */
+       struct ave_desc_info    rx;
+       struct ave_desc_info    tx;
+
+       /* flow control */
+       int pause_auto;
+       int pause_rx;
+       int pause_tx;
+
+       const struct ave_soc_data *data;
+};
+
+static inline u32 ave_r32(struct net_device *ndev, u32 offset)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+
+       return readl_relaxed(priv->base + offset);
+}
+
+static inline void ave_w32(struct net_device *ndev, u32 offset, u32 value)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+
+       writel_relaxed(value, priv->base + offset);
+}
+
+static inline u32 ave_rdesc(struct net_device *ndev, enum desc_id id,
+                           int entry, int offset)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       u32 addr = (id == AVE_DESCID_TX) ? priv->tx.daddr : priv->rx.daddr;
+
+       addr += entry * priv->desc_size + offset;
+
+       return ave_r32(ndev, addr);
+}
+
+static inline u32 ave_rdesc_cmdsts(struct net_device *ndev, enum desc_id id,
+                                  int entry)
+{
+       return ave_rdesc(ndev, id, entry, AVE_DESC_OFS_CMDSTS);
+}
+
+static inline void ave_wdesc(struct net_device *ndev, enum desc_id id,
+                            int entry, int offset, u32 val)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       u32 addr = (id == AVE_DESCID_TX) ? priv->tx.daddr : priv->rx.daddr;
+
+       addr += entry * priv->desc_size + offset;
+
+       ave_w32(ndev, addr, val);
+}
+
+static inline void ave_wdesc_cmdsts(struct net_device *ndev, enum desc_id id,
+                                   int entry, u32 val)
+{
+       ave_wdesc(ndev, id, entry, AVE_DESC_OFS_CMDSTS, val);
+}
+
+static void ave_wdesc_addr(struct net_device *ndev, enum desc_id id,
+                          int entry, dma_addr_t paddr)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+
+       ave_wdesc(ndev, id, entry, AVE_DESC_OFS_ADDRL, lower_32_bits(paddr));
+       if (IS_DESC_64BIT(priv))
+               ave_wdesc(ndev, id,
+                         entry, AVE_DESC_OFS_ADDRU, upper_32_bits(paddr));
+       else if ((u64)paddr > (u64)U32_MAX)
+               netdev_warn(ndev, "DMA address exceeds the address space\n");
+}
+
+static void ave_get_fwversion(struct net_device *ndev, char *buf, int len)
+{
+       u32 major, minor;
+
+       major = (ave_r32(ndev, AVE_VR) & GENMASK(15, 8)) >> 8;
+       minor = (ave_r32(ndev, AVE_VR) & GENMASK(7, 0));
+       snprintf(buf, len, "v%u.%u", major, minor);
+}
+
+static void ave_get_drvinfo(struct net_device *ndev,
+                           struct ethtool_drvinfo *info)
+{
+       struct device *dev = ndev->dev.parent;
+
+       strlcpy(info->driver, dev->driver->name, sizeof(info->driver));
+       strlcpy(info->bus_info, dev_name(dev), sizeof(info->bus_info));
+       ave_get_fwversion(ndev, info->fw_version, sizeof(info->fw_version));
+}
+
+static u32 ave_get_msglevel(struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+
+       return priv->msg_enable;
+}
+
+static void ave_set_msglevel(struct net_device *ndev, u32 val)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+
+       priv->msg_enable = val;
+}
+
+static void ave_get_wol(struct net_device *ndev,
+                       struct ethtool_wolinfo *wol)
+{
+       wol->supported = 0;
+       wol->wolopts   = 0;
+
+       if (ndev->phydev)
+               phy_ethtool_get_wol(ndev->phydev, wol);
+}
+
+static int ave_set_wol(struct net_device *ndev,
+                      struct ethtool_wolinfo *wol)
+{
+       int ret;
+
+       if (!ndev->phydev ||
+           (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE)))
+               return -EOPNOTSUPP;
+
+       ret = phy_ethtool_set_wol(ndev->phydev, wol);
+       if (!ret)
+               device_set_wakeup_enable(&ndev->dev, !!wol->wolopts);
+
+       return ret;
+}
+
+static void ave_get_pauseparam(struct net_device *ndev,
+                              struct ethtool_pauseparam *pause)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+
+       pause->autoneg  = priv->pause_auto;
+       pause->rx_pause = priv->pause_rx;
+       pause->tx_pause = priv->pause_tx;
+}
+
+static int ave_set_pauseparam(struct net_device *ndev,
+                             struct ethtool_pauseparam *pause)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       struct phy_device *phydev = ndev->phydev;
+
+       if (!phydev)
+               return -EINVAL;
+
+       priv->pause_auto = pause->autoneg;
+       priv->pause_rx   = pause->rx_pause;
+       priv->pause_tx   = pause->tx_pause;
+
+       phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
+       if (pause->rx_pause)
+               phydev->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
+       if (pause->tx_pause)
+               phydev->advertising ^= ADVERTISED_Asym_Pause;
+
+       if (pause->autoneg) {
+               if (netif_running(ndev))
+                       phy_start_aneg(phydev);
+       }
+
+       return 0;
+}
+
+static const struct ethtool_ops ave_ethtool_ops = {
+       .get_link_ksettings     = phy_ethtool_get_link_ksettings,
+       .set_link_ksettings     = phy_ethtool_set_link_ksettings,
+       .get_drvinfo            = ave_get_drvinfo,
+       .nway_reset             = phy_ethtool_nway_reset,
+       .get_link               = ethtool_op_get_link,
+       .get_msglevel           = ave_get_msglevel,
+       .set_msglevel           = ave_set_msglevel,
+       .get_wol                = ave_get_wol,
+       .set_wol                = ave_set_wol,
+       .get_pauseparam         = ave_get_pauseparam,
+       .set_pauseparam         = ave_set_pauseparam,
+};
+
+static int ave_pfsel_start(struct net_device *ndev, unsigned int entry)
+{
+       u32 val;
+
+       if (WARN_ON(entry > AVE_PF_SIZE))
+               return -EINVAL;
+
+       val = ave_r32(ndev, AVE_PFEN);
+       ave_w32(ndev, AVE_PFEN, val | BIT(entry));
+
+       return 0;
+}
+
+static int ave_pfsel_stop(struct net_device *ndev, unsigned int entry)
+{
+       u32 val;
+
+       if (WARN_ON(entry > AVE_PF_SIZE))
+               return -EINVAL;
+
+       val = ave_r32(ndev, AVE_PFEN);
+       ave_w32(ndev, AVE_PFEN, val & ~BIT(entry));
+
+       return 0;
+}
+
+static int ave_pfsel_macaddr_set(struct net_device *ndev,
+                                unsigned int entry, unsigned char *mac_addr,
+                                unsigned int set_size)
+{
+       u32 val;
+
+       if (WARN_ON(entry > AVE_PF_SIZE))
+               return -EINVAL;
+       if (WARN_ON(set_size > 6))
+               return -EINVAL;
+
+       ave_pfsel_stop(ndev, entry);
+
+       /* set MAC address for the filter */
+       val = mac_addr[0] | (mac_addr[1] << 8) | (mac_addr[2] << 16)
+               | (mac_addr[3] << 24);
+       ave_w32(ndev, AVE_PKTF(entry), val);
+       val = mac_addr[4] | (mac_addr[5] << 8);
+       ave_w32(ndev, AVE_PKTF(entry) + 4, val);
+
+       /* set byte mask */
+       ave_w32(ndev, AVE_PFMBYTE(entry), GENMASK(31, set_size) & ~0xc0);
+       ave_w32(ndev, AVE_PFMBYTE(entry) + 4, 0xFFFFFFFF);
+
+       /* set bit mask filter */
+       ave_w32(ndev, AVE_PFMBIT(entry), 0x0000FFFF);
+
+       /* set selector to ring 0 */
+       ave_w32(ndev, AVE_PFSEL(entry), 0);
+
+       /* restart filter */
+       ave_pfsel_start(ndev, entry);
+
+       return 0;
+}
+
+static int ave_mdio_busywait(struct net_device *ndev)
+{
+       int ret = 0, loop = 100;
+       u32 mdiosr;
+
+       /* wait until completion */
+       while (--loop) {
+               mdiosr = ave_r32(ndev, AVE_MDIOSR);
+               if (!(mdiosr & AVE_MDIOSR_STS))
+                       break;
+
+               usleep_range(10, 20);
+       }
+
+       if (!loop) {
+               netdev_err(ndev,
+                          "failed to read from MDIO (status:0x%08x)\n",
+                          mdiosr);
+               ret = -ETIMEDOUT;
+       }
+
+       return ret;
+}
+
+static int ave_mdiobus_read(struct mii_bus *bus, int phyid, int regnum)
+{
+       struct net_device *ndev = bus->priv;
+       u32 mdioctl;
+       int ret;
+
+       /* write address */
+       ave_w32(ndev, AVE_MDIOAR, (phyid << 8) | regnum);
+
+       /* read request */
+       mdioctl = ave_r32(ndev, AVE_MDIOCTR);
+       ave_w32(ndev, AVE_MDIOCTR,
+               (mdioctl | AVE_MDIOCTR_RREQ) & ~AVE_MDIOCTR_WREQ);
+
+       ret = ave_mdio_busywait(ndev);
+       if (ret) {
+               netdev_err(ndev, "phy-%d reg-%x read failed\n",
+                          phyid, regnum);
+               return ret;
+       }
+
+       return ave_r32(ndev, AVE_MDIORDR) & GENMASK(15, 0);
+}
+
+static int ave_mdiobus_write(struct mii_bus *bus,
+                            int phyid, int regnum, u16 val)
+{
+       struct net_device *ndev = bus->priv;
+       u32 mdioctl;
+       int ret;
+
+       /* write address */
+       ave_w32(ndev, AVE_MDIOAR, (phyid << 8) | regnum);
+
+       /* write data */
+       ave_w32(ndev, AVE_MDIOWDR, val);
+
+       /* write request */
+       mdioctl = ave_r32(ndev, AVE_MDIOCTR);
+       ave_w32(ndev, AVE_MDIOCTR,
+               (mdioctl | AVE_MDIOCTR_WREQ) & ~AVE_MDIOCTR_RREQ);
+
+       ret = ave_mdio_busywait(ndev);
+       if (ret)
+               netdev_err(ndev, "phy-%d reg-%x write failed\n",
+                          phyid, regnum);
+
+       return ret;
+}
+
+static dma_addr_t ave_dma_map(struct net_device *ndev, struct ave_desc *desc,
+                             void *ptr, size_t len,
+                             enum dma_data_direction dir)
+{
+       dma_addr_t paddr;
+
+       paddr = dma_map_single(ndev->dev.parent, ptr, len, dir);
+       if (unlikely(dma_mapping_error(ndev->dev.parent, paddr))) {
+               paddr = (dma_addr_t)-ENOMEM;
+       } else {
+               desc->skbs_dma = paddr;
+               desc->skbs_dmalen = len;
+       }
+
+       return paddr;
+}
+
+static void ave_dma_unmap(struct net_device *ndev, struct ave_desc *desc,
+                         enum dma_data_direction dir)
+{
+       if (!desc->skbs_dma)
+               return;
+
+       dma_unmap_single(ndev->dev.parent,
+                        desc->skbs_dma, desc->skbs_dmalen, dir);
+       desc->skbs_dma = 0;
+}
+
+/* Set Rx descriptor and memory */
+static int ave_set_rxdesc(struct net_device *ndev, int entry)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+       int ret = 0;
+
+       skb = priv->rx.desc[entry].skbs;
+       if (!skb) {
+               skb = netdev_alloc_skb_ip_align(ndev,
+                                               AVE_MAX_ETHFRAME);
+               if (!skb) {
+                       netdev_err(ndev, "can't allocate skb for Rx\n");
+                       return -ENOMEM;
+               }
+       }
+
+       /* set disable to cmdsts */
+       ave_wdesc_cmdsts(ndev, AVE_DESCID_RX, entry,
+                        AVE_STS_INTR | AVE_STS_OWN);
+
+       /* map Rx buffer
+        * Rx buffer set to the Rx descriptor has two restrictions:
+        * - Rx buffer address is 4 byte aligned.
+        * - Rx buffer begins with 2 byte headroom, and data will be put from
+        *   (buffer + 2).
+        * To satisfy this, specify the address to put back the buffer
+        * pointer advanced by NET_IP_ALIGN by netdev_alloc_skb_ip_align(),
+        * and expand the map size by NET_IP_ALIGN.
+        */
+       paddr = ave_dma_map(ndev, &priv->rx.desc[entry],
+                           skb->data - NET_IP_ALIGN,
+                           AVE_MAX_ETHFRAME + NET_IP_ALIGN, DMA_FROM_DEVICE);
+       if (paddr == -ENOMEM) {
+               netdev_err(ndev, "can't map skb for Rx\n");
+               dev_kfree_skb_any(skb);
+               return -ENOMEM;
+       }
+       priv->rx.desc[entry].skbs = skb;
+
+       /* set buffer pointer */
+       ave_wdesc_addr(ndev, AVE_DESCID_RX, entry, paddr);
+
+       /* set enable to cmdsts */
+       ave_wdesc_cmdsts(ndev, AVE_DESCID_RX, entry,
+                        AVE_STS_INTR | AVE_MAX_ETHFRAME);
+
+       return ret;
+}
+
+/* Switch state of descriptor */
+static int ave_desc_switch(struct net_device *ndev, enum desc_state state)
+{
+       int counter;
+       int ret = 0;
+       u32 val;
+
+       switch (state) {
+       case AVE_DESC_START:
+               ave_w32(ndev, AVE_DESCC, AVE_DESCC_TD | AVE_DESCC_RD0);
+               break;
+
+       case AVE_DESC_STOP:
+               ave_w32(ndev, AVE_DESCC, 0);
+               counter = 100;
+               while (--counter) {
+                       usleep_range(100, 150);
+                       if (!ave_r32(ndev, AVE_DESCC))
+                               break;
+               }
+               if (!counter) {
+                       netdev_err(ndev, "can't stop descriptor\n");
+                       ret = -EBUSY;
+               }
+               break;
+
+       case AVE_DESC_RX_SUSPEND:
+               val = ave_r32(ndev, AVE_DESCC);
+               val |= AVE_DESCC_RDSTP;
+               val &= ~AVE_DESCC_STATUS_MASK;
+               ave_w32(ndev, AVE_DESCC, val);
+
+               counter = 1000;
+               while (--counter) {
+                       usleep_range(100, 150);
+                       val = ave_r32(ndev, AVE_DESCC);
+                       if (val & (AVE_DESCC_RDSTP << 16))
+                               break;
+               }
+               if (!counter) {
+                       netdev_err(ndev, "can't suspend descriptor\n");
+                       ret = -EBUSY;
+               }
+               break;
+
+       case AVE_DESC_RX_PERMIT:
+               val = ave_r32(ndev, AVE_DESCC);
+               val &= ~AVE_DESCC_RDSTP;
+               val &= ~AVE_DESCC_STATUS_MASK;
+               ave_w32(ndev, AVE_DESCC, val);
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int ave_tx_completion(struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       u32 proc_idx, done_idx, ndesc, cmdsts;
+       unsigned int nr_freebuf = 0;
+       unsigned int tx_packets = 0;
+       unsigned int tx_bytes = 0;
+
+       proc_idx = priv->tx.proc_idx;
+       done_idx = priv->tx.done_idx;
+       ndesc    = priv->tx.ndesc;
+
+       /* free pre-stored skb from done_idx to proc_idx */
+       while (proc_idx != done_idx) {
+               cmdsts = ave_rdesc_cmdsts(ndev, AVE_DESCID_TX, done_idx);
+
+               /* do nothing if owner is HW (==1 for Tx) */
+               if (cmdsts & AVE_STS_OWN)
+                       break;
+
+               /* check Tx status and updates statistics */
+               if (cmdsts & AVE_STS_OK) {
+                       tx_bytes += cmdsts & AVE_STS_PKTLEN_TX_MASK;
+                       /* success */
+                       if (cmdsts & AVE_STS_LAST)
+                               tx_packets++;
+               } else {
+                       /* error */
+                       if (cmdsts & AVE_STS_LAST) {
+                               priv->stats_tx.errors++;
+                               if (cmdsts & (AVE_STS_OWC | AVE_STS_EC))
+                                       priv->stats_tx.collisions++;
+                       }
+               }
+
+               /* release skb */
+               if (priv->tx.desc[done_idx].skbs) {
+                       ave_dma_unmap(ndev, &priv->tx.desc[done_idx],
+                                     DMA_TO_DEVICE);
+                       dev_consume_skb_any(priv->tx.desc[done_idx].skbs);
+                       priv->tx.desc[done_idx].skbs = NULL;
+                       nr_freebuf++;
+               }
+               done_idx = (done_idx + 1) % ndesc;
+       }
+
+       priv->tx.done_idx = done_idx;
+
+       /* update stats */
+       u64_stats_update_begin(&priv->stats_tx.syncp);
+       priv->stats_tx.packets += tx_packets;
+       priv->stats_tx.bytes   += tx_bytes;
+       u64_stats_update_end(&priv->stats_tx.syncp);
+
+       /* wake queue for freeing buffer */
+       if (unlikely(netif_queue_stopped(ndev)) && nr_freebuf)
+               netif_wake_queue(ndev);
+
+       return nr_freebuf;
+}
+
+static int ave_rx(struct net_device *ndev, int num)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       struct sk_buff *skb;
+       u32 proc_idx, done_idx, ndesc, cmdsts;
+       int restpkt, npkts;
+       unsigned int pktlen;
+       unsigned int rx_packets = 0;
+       unsigned int rx_bytes = 0;
+
+       proc_idx = priv->rx.proc_idx;
+       done_idx = priv->rx.done_idx;
+       ndesc    = priv->rx.ndesc;
+       restpkt  = ((proc_idx + ndesc - 1) - done_idx) % ndesc;
+
+       for (npkts = 0; npkts < num; npkts++) {
+               /* we can't receive more packet, so fill desc quickly */
+               if (--restpkt < 0)
+                       break;
+
+               cmdsts = ave_rdesc_cmdsts(ndev, AVE_DESCID_RX, proc_idx);
+
+               /* do nothing if owner is HW (==0 for Rx) */
+               if (!(cmdsts & AVE_STS_OWN))
+                       break;
+
+               if (!(cmdsts & AVE_STS_OK)) {
+                       priv->stats_rx.errors++;
+                       proc_idx = (proc_idx + 1) % ndesc;
+                       continue;
+               }
+
+               pktlen = cmdsts & AVE_STS_PKTLEN_RX_MASK;
+
+               /* get skbuff for rx */
+               skb = priv->rx.desc[proc_idx].skbs;
+               priv->rx.desc[proc_idx].skbs = NULL;
+
+               ave_dma_unmap(ndev, &priv->rx.desc[proc_idx], DMA_FROM_DEVICE);
+
+               skb->dev = ndev;
+               skb_put(skb, pktlen);
+               skb->protocol = eth_type_trans(skb, ndev);
+
+               if ((cmdsts & AVE_STS_CSSV) && (!(cmdsts & AVE_STS_CSER)))
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+               rx_packets++;
+               rx_bytes += pktlen;
+
+               netif_receive_skb(skb);
+
+               proc_idx = (proc_idx + 1) % ndesc;
+       }
+
+       priv->rx.proc_idx = proc_idx;
+
+       /* update stats */
+       u64_stats_update_begin(&priv->stats_rx.syncp);
+       priv->stats_rx.packets += rx_packets;
+       priv->stats_rx.bytes   += rx_bytes;
+       u64_stats_update_end(&priv->stats_rx.syncp);
+
+       /* refill the Rx buffers */
+       while (proc_idx != done_idx) {
+               if (ave_set_rxdesc(ndev, done_idx))
+                       break;
+               done_idx = (done_idx + 1) % ndesc;
+       }
+
+       priv->rx.done_idx = done_idx;
+
+       return npkts;
+}
+
+static inline u32 ave_irq_disable_all(struct net_device *ndev)
+{
+       u32 ret;
+
+       ret = ave_r32(ndev, AVE_GIMR);
+       ave_w32(ndev, AVE_GIMR, 0);
+
+       return ret;
+}
+
+static inline void ave_irq_restore(struct net_device *ndev, u32 val)
+{
+       ave_w32(ndev, AVE_GIMR, val);
+}
+
+static inline void ave_irq_enable(struct net_device *ndev, u32 bitflag)
+{
+       ave_w32(ndev, AVE_GIMR, ave_r32(ndev, AVE_GIMR) | bitflag);
+       ave_w32(ndev, AVE_GISR, bitflag);
+}
+
+static inline void ave_irq_disable(struct net_device *ndev, u32 bitflag)
+{
+       ave_w32(ndev, AVE_GIMR, ave_r32(ndev, AVE_GIMR) & ~bitflag);
+}
+
+static void ave_global_reset(struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       u32 val;
+
+       /* set config register */
+       val = AVE_CFGR_FLE | AVE_CFGR_IPFCEN | AVE_CFGR_CHE;
+       if (!phy_interface_mode_is_rgmii(priv->phy_mode))
+               val |= AVE_CFGR_MII;
+       ave_w32(ndev, AVE_CFGR, val);
+
+       /* reset RMII register */
+       val = ave_r32(ndev, AVE_RSTCTRL);
+       val &= ~AVE_RSTCTRL_RMIIRST;
+       ave_w32(ndev, AVE_RSTCTRL, val);
+
+       /* assert reset */
+       ave_w32(ndev, AVE_GRR, AVE_GRR_GRST | AVE_GRR_PHYRST);
+       msleep(20);
+
+       /* 1st, negate PHY reset only */
+       ave_w32(ndev, AVE_GRR, AVE_GRR_GRST);
+       msleep(40);
+
+       /* negate reset */
+       ave_w32(ndev, AVE_GRR, 0);
+       msleep(40);
+
+       /* negate RMII register */
+       val = ave_r32(ndev, AVE_RSTCTRL);
+       val |= AVE_RSTCTRL_RMIIRST;
+       ave_w32(ndev, AVE_RSTCTRL, val);
+
+       ave_irq_disable_all(ndev);
+}
+
+static void ave_rxf_reset(struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       u32 rxcr_org;
+
+       /* save and disable MAC receive op */
+       rxcr_org = ave_r32(ndev, AVE_RXCR);
+       ave_w32(ndev, AVE_RXCR, rxcr_org & (~AVE_RXCR_RXEN));
+
+       /* suspend Rx descriptor */
+       ave_desc_switch(ndev, AVE_DESC_RX_SUSPEND);
+
+       /* receive all packets before descriptor starts */
+       ave_rx(ndev, priv->rx.ndesc);
+
+       /* assert reset */
+       ave_w32(ndev, AVE_GRR, AVE_GRR_RXFFR);
+       usleep_range(40, 50);
+
+       /* negate reset */
+       ave_w32(ndev, AVE_GRR, 0);
+       usleep_range(10, 20);
+
+       /* negate interrupt status */
+       ave_w32(ndev, AVE_GISR, AVE_GI_RXOVF);
+
+       /* permit descriptor */
+       ave_desc_switch(ndev, AVE_DESC_RX_PERMIT);
+
+       /* restore MAC reccieve op */
+       ave_w32(ndev, AVE_RXCR, rxcr_org);
+}
+
+static irqreturn_t ave_interrupt(int irq, void *netdev)
+{
+       struct net_device *ndev = (struct net_device *)netdev;
+       struct ave_private *priv = netdev_priv(ndev);
+       u32 gimr_val, gisr_val;
+
+       gimr_val = ave_irq_disable_all(ndev);
+
+       /* get interrupt status */
+       gisr_val = ave_r32(ndev, AVE_GISR);
+
+       /* PHY */
+       if (gisr_val & AVE_GI_PHY)
+               ave_w32(ndev, AVE_GISR, AVE_GI_PHY);
+
+       /* check exceeding packet */
+       if (gisr_val & AVE_GI_RXERR) {
+               ave_w32(ndev, AVE_GISR, AVE_GI_RXERR);
+               netdev_err(ndev, "receive a packet exceeding frame buffer\n");
+       }
+
+       gisr_val &= gimr_val;
+       if (!gisr_val)
+               goto exit_isr;
+
+       /* RxFIFO overflow */
+       if (gisr_val & AVE_GI_RXOVF) {
+               priv->stats_rx.fifo_errors++;
+               ave_rxf_reset(ndev);
+               goto exit_isr;
+       }
+
+       /* Rx drop */
+       if (gisr_val & AVE_GI_RXDROP) {
+               priv->stats_rx.dropped++;
+               ave_w32(ndev, AVE_GISR, AVE_GI_RXDROP);
+       }
+
+       /* Rx interval */
+       if (gisr_val & AVE_GI_RXIINT) {
+               napi_schedule(&priv->napi_rx);
+               /* still force to disable Rx interrupt until NAPI finishes */
+               gimr_val &= ~AVE_GI_RXIINT;
+       }
+
+       /* Tx completed */
+       if (gisr_val & AVE_GI_TX) {
+               napi_schedule(&priv->napi_tx);
+               /* still force to disable Tx interrupt until NAPI finishes */
+               gimr_val &= ~AVE_GI_TX;
+       }
+
+exit_isr:
+       ave_irq_restore(ndev, gimr_val);
+
+       return IRQ_HANDLED;
+}
+
+static int ave_poll_rx(struct napi_struct *napi, int budget)
+{
+       struct ave_private *priv;
+       struct net_device *ndev;
+       int num;
+
+       priv = container_of(napi, struct ave_private, napi_rx);
+       ndev = priv->ndev;
+
+       num = ave_rx(ndev, budget);
+       if (num < budget) {
+               napi_complete_done(napi, num);
+
+               /* enable Rx interrupt when NAPI finishes */
+               ave_irq_enable(ndev, AVE_GI_RXIINT);
+       }
+
+       return num;
+}
+
+static int ave_poll_tx(struct napi_struct *napi, int budget)
+{
+       struct ave_private *priv;
+       struct net_device *ndev;
+       int num;
+
+       priv = container_of(napi, struct ave_private, napi_tx);
+       ndev = priv->ndev;
+
+       num = ave_tx_completion(ndev);
+       napi_complete(napi);
+
+       /* enable Tx interrupt when NAPI finishes */
+       ave_irq_enable(ndev, AVE_GI_TX);
+
+       return num;
+}
+
+static void ave_pfsel_promisc_set(struct net_device *ndev,
+                                 unsigned int entry, u32 rxring)
+{
+       if (WARN_ON(entry > AVE_PF_SIZE))
+               return;
+
+       ave_pfsel_stop(ndev, entry);
+
+       /* set byte mask */
+       ave_w32(ndev, AVE_PFMBYTE(entry),     0xFFFFFFFF);
+       ave_w32(ndev, AVE_PFMBYTE(entry) + 4, 0xFFFFFFFF);
+
+       /* set bit mask filter */
+       ave_w32(ndev, AVE_PFMBIT(entry), 0x0000FFFF);
+
+       /* set selector to rxring */
+       ave_w32(ndev, AVE_PFSEL(entry), rxring);
+
+       ave_pfsel_start(ndev, entry);
+}
+
+static void ave_pfsel_init(struct net_device *ndev)
+{
+       int i;
+       unsigned char bcast_mac[ETH_ALEN];
+
+       eth_broadcast_addr(bcast_mac);
+
+       for (i = 0; i < AVE_PF_SIZE; i++)
+               ave_pfsel_stop(ndev, i);
+
+       /* promiscious entry, select ring 0 */
+       ave_pfsel_promisc_set(ndev, AVE_PFNUM_FILTER, 0);
+
+       /* unicast entry */
+       ave_pfsel_macaddr_set(ndev, AVE_PFNUM_UNICAST, ndev->dev_addr, 6);
+
+       /* broadcast entry */
+       ave_pfsel_macaddr_set(ndev, AVE_PFNUM_BROADCAST, bcast_mac, 6);
+}
+
+static void ave_adjust_link(struct net_device *ndev)
+{
+       struct phy_device *phydev = ndev->phydev;
+       u32 val, txcr, rxcr, rxcr_org;
+       u16 rmt_adv = 0, lcl_adv = 0;
+       u8 cap;
+
+       /* set RGMII speed */
+       val = ave_r32(ndev, AVE_TXCR);
+       val &= ~(AVE_TXCR_TXSPD_100 | AVE_TXCR_TXSPD_1G);
+
+       if (phy_interface_is_rgmii(phydev) && phydev->speed == SPEED_1000)
+               val |= AVE_TXCR_TXSPD_1G;
+       else if (phydev->speed == SPEED_100)
+               val |= AVE_TXCR_TXSPD_100;
+
+       ave_w32(ndev, AVE_TXCR, val);
+
+       /* set RMII speed (100M/10M only) */
+       if (!phy_interface_is_rgmii(phydev)) {
+               val = ave_r32(ndev, AVE_LINKSEL);
+               if (phydev->speed == SPEED_10)
+                       val &= ~AVE_LINKSEL_100M;
+               else
+                       val |= AVE_LINKSEL_100M;
+               ave_w32(ndev, AVE_LINKSEL, val);
+       }
+
+       /* check current RXCR/TXCR */
+       rxcr = ave_r32(ndev, AVE_RXCR);
+       txcr = ave_r32(ndev, AVE_TXCR);
+       rxcr_org = rxcr;
+
+       if (phydev->duplex) {
+               rxcr |= AVE_RXCR_FDUPEN;
+
+               if (phydev->pause)
+                       rmt_adv |= LPA_PAUSE_CAP;
+               if (phydev->asym_pause)
+                       rmt_adv |= LPA_PAUSE_ASYM;
+               if (phydev->advertising & ADVERTISED_Pause)
+                       lcl_adv |= ADVERTISE_PAUSE_CAP;
+               if (phydev->advertising & ADVERTISED_Asym_Pause)
+                       lcl_adv |= ADVERTISE_PAUSE_ASYM;
+
+               cap = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
+               if (cap & FLOW_CTRL_TX)
+                       txcr |= AVE_TXCR_FLOCTR;
+               else
+                       txcr &= ~AVE_TXCR_FLOCTR;
+               if (cap & FLOW_CTRL_RX)
+                       rxcr |= AVE_RXCR_FLOCTR;
+               else
+                       rxcr &= ~AVE_RXCR_FLOCTR;
+       } else {
+               rxcr &= ~AVE_RXCR_FDUPEN;
+               rxcr &= ~AVE_RXCR_FLOCTR;
+               txcr &= ~AVE_TXCR_FLOCTR;
+       }
+
+       if (rxcr_org != rxcr) {
+               /* disable Rx mac */
+               ave_w32(ndev, AVE_RXCR, rxcr & ~AVE_RXCR_RXEN);
+               /* change and enable TX/Rx mac */
+               ave_w32(ndev, AVE_TXCR, txcr);
+               ave_w32(ndev, AVE_RXCR, rxcr);
+       }
+
+       phy_print_status(phydev);
+}
+
+static void ave_macaddr_init(struct net_device *ndev)
+{
+       unsigned char *mac_addr;
+       u32 val;
+
+       /* set macaddr */
+       mac_addr = ndev->dev_addr;
+       val = mac_addr[0] | (mac_addr[1] << 8) | (mac_addr[2] << 16)
+               | (mac_addr[3] << 24);
+       ave_w32(ndev, AVE_RXMAC1R, val);
+       val = mac_addr[4] | (mac_addr[5] << 8);
+       ave_w32(ndev, AVE_RXMAC2R, val);
+
+       /* pfsel unicast entry */
+       ave_pfsel_macaddr_set(ndev, AVE_PFNUM_UNICAST, ndev->dev_addr, 6);
+}
+
+static int ave_init(struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       struct device *dev = ndev->dev.parent;
+       struct device_node *np = dev->of_node, *mdio_np;
+       struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+       struct phy_device *phydev;
+       int ret = 0;
+
+       /* enable clk because of hw access until ndo_open */
+       clk_prepare_enable(priv->clk);
+       reset_control_deassert(priv->rst);
+
+       ave_global_reset(ndev);
+
+       mdio_np = of_get_child_by_name(np, "mdio");
+       if (!mdio_np) {
+               dev_err(dev, "mdio node not found\n");
+               ret = -EINVAL;
+               goto out_clk_disable;
+       }
+       ret = of_mdiobus_register(priv->mdio, mdio_np);
+       of_node_put(mdio_np);
+       if (ret) {
+               dev_err(dev, "failed to register mdiobus\n");
+               goto out_clk_disable;
+       }
+
+       phydev = of_phy_get_and_connect(ndev, np, ave_adjust_link);
+       if (!phydev) {
+               dev_err(dev, "could not attach to PHY\n");
+               ret = -ENODEV;
+               goto out_mdio_unregister;
+       }
+
+       priv->phydev = phydev;
+
+       phy_ethtool_get_wol(phydev, &wol);
+       device_set_wakeup_capable(&ndev->dev, !!wol.supported);
+
+       if (!phy_interface_is_rgmii(phydev)) {
+               phydev->supported &= ~PHY_GBIT_FEATURES;
+               phydev->supported |= PHY_BASIC_FEATURES;
+       }
+       phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+
+       phy_attached_info(phydev);
+
+       return ret;
+
+out_mdio_unregister:
+       mdiobus_unregister(priv->mdio);
+out_clk_disable:
+       reset_control_assert(priv->rst);
+       clk_disable_unprepare(priv->clk);
+
+       return ret;
+}
+
+static void ave_uninit(struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+
+       phy_disconnect(priv->phydev);
+
+       /* disable clk because of hw access after ndo_stop */
+       reset_control_assert(priv->rst);
+       clk_disable_unprepare(priv->clk);
+}
+
+static int ave_open(struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       int entry;
+       int ret;
+       u32 val;
+
+       ret = request_irq(priv->irq, ave_interrupt, IRQF_SHARED, ndev->name,
+                         ndev);
+       if (ret)
+               return ret;
+
+       priv->tx.desc = kcalloc(priv->tx.ndesc, sizeof(*priv->tx.desc),
+                               GFP_KERNEL);
+       if (!priv->tx.desc) {
+               ret = -ENOMEM;
+               goto out_free_irq;
+       }
+
+       priv->rx.desc = kcalloc(priv->rx.ndesc, sizeof(*priv->rx.desc),
+                               GFP_KERNEL);
+       if (!priv->rx.desc) {
+               kfree(priv->tx.desc);
+               ret = -ENOMEM;
+               goto out_free_irq;
+       }
+
+       /* initialize Tx work and descriptor */
+       priv->tx.proc_idx = 0;
+       priv->tx.done_idx = 0;
+       for (entry = 0; entry < priv->tx.ndesc; entry++) {
+               ave_wdesc_cmdsts(ndev, AVE_DESCID_TX, entry, 0);
+               ave_wdesc_addr(ndev, AVE_DESCID_TX, entry, 0);
+       }
+       ave_w32(ndev, AVE_TXDC, AVE_TXDC_ADDR_START
+               | (((priv->tx.ndesc * priv->desc_size) << 16) & AVE_TXDC_SIZE));
+
+       /* initialize Rx work and descriptor */
+       priv->rx.proc_idx = 0;
+       priv->rx.done_idx = 0;
+       for (entry = 0; entry < priv->rx.ndesc; entry++) {
+               if (ave_set_rxdesc(ndev, entry))
+                       break;
+       }
+       ave_w32(ndev, AVE_RXDC0, AVE_RXDC0_ADDR_START
+           | (((priv->rx.ndesc * priv->desc_size) << 16) & AVE_RXDC0_SIZE));
+
+       ave_desc_switch(ndev, AVE_DESC_START);
+
+       ave_pfsel_init(ndev);
+       ave_macaddr_init(ndev);
+
+       /* set Rx configuration */
+       /* full duplex, enable pause drop, enalbe flow control */
+       val = AVE_RXCR_RXEN | AVE_RXCR_FDUPEN | AVE_RXCR_DRPEN |
+               AVE_RXCR_FLOCTR | (AVE_MAX_ETHFRAME & AVE_RXCR_MPSIZ_MASK);
+       ave_w32(ndev, AVE_RXCR, val);
+
+       /* set Tx configuration */
+       /* enable flow control, disable loopback */
+       ave_w32(ndev, AVE_TXCR, AVE_TXCR_FLOCTR);
+
+       /* enable timer, clear EN,INTM, and mask interval unit(BSCK) */
+       val = ave_r32(ndev, AVE_IIRQC) & AVE_IIRQC_BSCK;
+       val |= AVE_IIRQC_EN0 | (AVE_INTM_COUNT << 16);
+       ave_w32(ndev, AVE_IIRQC, val);
+
+       val = AVE_GI_RXIINT | AVE_GI_RXOVF | AVE_GI_TX;
+       ave_irq_restore(ndev, val);
+
+       napi_enable(&priv->napi_rx);
+       napi_enable(&priv->napi_tx);
+
+       phy_start(ndev->phydev);
+       phy_start_aneg(ndev->phydev);
+       netif_start_queue(ndev);
+
+       return 0;
+
+out_free_irq:
+       disable_irq(priv->irq);
+       free_irq(priv->irq, ndev);
+
+       return ret;
+}
+
+static int ave_stop(struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       int entry;
+
+       ave_irq_disable_all(ndev);
+       disable_irq(priv->irq);
+       free_irq(priv->irq, ndev);
+
+       netif_tx_disable(ndev);
+       phy_stop(ndev->phydev);
+       napi_disable(&priv->napi_tx);
+       napi_disable(&priv->napi_rx);
+
+       ave_desc_switch(ndev, AVE_DESC_STOP);
+
+       /* free Tx buffer */
+       for (entry = 0; entry < priv->tx.ndesc; entry++) {
+               if (!priv->tx.desc[entry].skbs)
+                       continue;
+
+               ave_dma_unmap(ndev, &priv->tx.desc[entry], DMA_TO_DEVICE);
+               dev_kfree_skb_any(priv->tx.desc[entry].skbs);
+               priv->tx.desc[entry].skbs = NULL;
+       }
+       priv->tx.proc_idx = 0;
+       priv->tx.done_idx = 0;
+
+       /* free Rx buffer */
+       for (entry = 0; entry < priv->rx.ndesc; entry++) {
+               if (!priv->rx.desc[entry].skbs)
+                       continue;
+
+               ave_dma_unmap(ndev, &priv->rx.desc[entry], DMA_FROM_DEVICE);
+               dev_kfree_skb_any(priv->rx.desc[entry].skbs);
+               priv->rx.desc[entry].skbs = NULL;
+       }
+       priv->rx.proc_idx = 0;
+       priv->rx.done_idx = 0;
+
+       kfree(priv->tx.desc);
+       kfree(priv->rx.desc);
+
+       return 0;
+}
+
+static int ave_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       u32 proc_idx, done_idx, ndesc, cmdsts;
+       int freepkt;
+       dma_addr_t paddr;
+
+       proc_idx = priv->tx.proc_idx;
+       done_idx = priv->tx.done_idx;
+       ndesc = priv->tx.ndesc;
+       freepkt = ((done_idx + ndesc - 1) - proc_idx) % ndesc;
+
+       /* stop queue when not enough entry */
+       if (unlikely(freepkt < 1)) {
+               netif_stop_queue(ndev);
+               return NETDEV_TX_BUSY;
+       }
+
+       /* add padding for short packet */
+       if (skb_put_padto(skb, ETH_ZLEN)) {
+               priv->stats_tx.dropped++;
+               return NETDEV_TX_OK;
+       }
+
+       /* map Tx buffer
+        * Tx buffer set to the Tx descriptor doesn't have any restriction.
+        */
+       paddr = ave_dma_map(ndev, &priv->tx.desc[proc_idx],
+                           skb->data,
+                           skb->len, DMA_TO_DEVICE);
+       if (paddr == -ENOMEM) {
+               dev_kfree_skb_any(skb);
+               priv->stats_tx.dropped++;
+               return NETDEV_TX_OK;
+       }
+
+       priv->tx.desc[proc_idx].skbs = skb;
+
+       ave_wdesc_addr(ndev, AVE_DESCID_TX, proc_idx, paddr);
+
+       cmdsts = AVE_STS_OWN | AVE_STS_1ST | AVE_STS_LAST
+               | (skb->len & AVE_STS_PKTLEN_TX_MASK);
+
+       /* set interrupt per AVE_FORCE_TXINTCNT or when queue is stopped */
+       if (!(proc_idx % AVE_FORCE_TXINTCNT) || netif_queue_stopped(ndev))
+               cmdsts |= AVE_STS_INTR;
+
+       /* disable checksum calculation when skb doesn't calurate checksum */
+       if (skb->ip_summed == CHECKSUM_NONE ||
+           skb->ip_summed == CHECKSUM_UNNECESSARY)
+               cmdsts |= AVE_STS_NOCSUM;
+
+       ave_wdesc_cmdsts(ndev, AVE_DESCID_TX, proc_idx, cmdsts);
+
+       priv->tx.proc_idx = (proc_idx + 1) % ndesc;
+
+       return NETDEV_TX_OK;
+}
+
+static int ave_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+       return phy_mii_ioctl(ndev->phydev, ifr, cmd);
+}
+
+static void ave_set_rx_mode(struct net_device *ndev)
+{
+       int count, mc_cnt = netdev_mc_count(ndev);
+       struct netdev_hw_addr *hw_adr;
+       u32 val;
+       u8 v4multi_macadr[6] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       u8 v6multi_macadr[6] = { 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       /* MAC addr filter enable for promiscious mode */
+       val = ave_r32(ndev, AVE_RXCR);
+       if (ndev->flags & IFF_PROMISC || !mc_cnt)
+               val &= ~AVE_RXCR_AFEN;
+       else
+               val |= AVE_RXCR_AFEN;
+       ave_w32(ndev, AVE_RXCR, val);
+
+       /* set all multicast address */
+       if ((ndev->flags & IFF_ALLMULTI) || mc_cnt > AVE_PF_MULTICAST_SIZE) {
+               ave_pfsel_macaddr_set(ndev, AVE_PFNUM_MULTICAST,
+                                     v4multi_macadr, 1);
+               ave_pfsel_macaddr_set(ndev, AVE_PFNUM_MULTICAST + 1,
+                                     v6multi_macadr, 1);
+       } else {
+               /* stop all multicast filter */
+               for (count = 0; count < AVE_PF_MULTICAST_SIZE; count++)
+                       ave_pfsel_stop(ndev, AVE_PFNUM_MULTICAST + count);
+
+               /* set multicast addresses */
+               count = 0;
+               netdev_for_each_mc_addr(hw_adr, ndev) {
+                       if (count == mc_cnt)
+                               break;
+                       ave_pfsel_macaddr_set(ndev, AVE_PFNUM_MULTICAST + count,
+                                             hw_adr->addr, 6);
+                       count++;
+               }
+       }
+}
+
+static void ave_get_stats64(struct net_device *ndev,
+                           struct rtnl_link_stats64 *stats)
+{
+       struct ave_private *priv = netdev_priv(ndev);
+       unsigned int start;
+
+       do {
+               start = u64_stats_fetch_begin_irq(&priv->stats_rx.syncp);
+               stats->rx_packets = priv->stats_rx.packets;
+               stats->rx_bytes   = priv->stats_rx.bytes;
+       } while (u64_stats_fetch_retry_irq(&priv->stats_rx.syncp, start));
+
+       do {
+               start = u64_stats_fetch_begin_irq(&priv->stats_tx.syncp);
+               stats->tx_packets = priv->stats_tx.packets;
+               stats->tx_bytes   = priv->stats_tx.bytes;
+       } while (u64_stats_fetch_retry_irq(&priv->stats_tx.syncp, start));
+
+       stats->rx_errors      = priv->stats_rx.errors;
+       stats->tx_errors      = priv->stats_tx.errors;
+       stats->rx_dropped     = priv->stats_rx.dropped;
+       stats->tx_dropped     = priv->stats_tx.dropped;
+       stats->rx_fifo_errors = priv->stats_rx.fifo_errors;
+       stats->collisions     = priv->stats_tx.collisions;
+}
+
+static int ave_set_mac_address(struct net_device *ndev, void *p)
+{
+       int ret = eth_mac_addr(ndev, p);
+
+       if (ret)
+               return ret;
+
+       ave_macaddr_init(ndev);
+
+       return 0;
+}
+
+static const struct net_device_ops ave_netdev_ops = {
+       .ndo_init               = ave_init,
+       .ndo_uninit             = ave_uninit,
+       .ndo_open               = ave_open,
+       .ndo_stop               = ave_stop,
+       .ndo_start_xmit         = ave_start_xmit,
+       .ndo_do_ioctl           = ave_ioctl,
+       .ndo_set_rx_mode        = ave_set_rx_mode,
+       .ndo_get_stats64        = ave_get_stats64,
+       .ndo_set_mac_address    = ave_set_mac_address,
+};
+
+static int ave_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       u32 ave_id;
+       struct ave_private *priv;
+       const struct ave_soc_data *data;
+       phy_interface_t phy_mode;
+       struct net_device *ndev;
+       struct resource *res;
+       void __iomem *base;
+       int irq, ret = 0;
+       char buf[ETHTOOL_FWVERS_LEN];
+       const void *mac_addr;
+
+       data = of_device_get_match_data(dev);
+       if (WARN_ON(!data))
+               return -EINVAL;
+
+       phy_mode = of_get_phy_mode(np);
+       if (phy_mode < 0) {
+               dev_err(dev, "phy-mode not found\n");
+               return -EINVAL;
+       }
+       if ((!phy_interface_mode_is_rgmii(phy_mode)) &&
+           phy_mode != PHY_INTERFACE_MODE_RMII &&
+           phy_mode != PHY_INTERFACE_MODE_MII) {
+               dev_err(dev, "phy-mode is invalid\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(dev, "IRQ not found\n");
+               return irq;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       ndev = alloc_etherdev(sizeof(struct ave_private));
+       if (!ndev) {
+               dev_err(dev, "can't allocate ethernet device\n");
+               return -ENOMEM;
+       }
+
+       ndev->netdev_ops = &ave_netdev_ops;
+       ndev->ethtool_ops = &ave_ethtool_ops;
+       SET_NETDEV_DEV(ndev, dev);
+
+       ndev->features    |= (NETIF_F_IP_CSUM | NETIF_F_RXCSUM);
+       ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_RXCSUM);
+
+       ndev->max_mtu = AVE_MAX_ETHFRAME - (ETH_HLEN + ETH_FCS_LEN);
+
+       mac_addr = of_get_mac_address(np);
+       if (mac_addr)
+               ether_addr_copy(ndev->dev_addr, mac_addr);
+
+       /* if the mac address is invalid, use random mac address */
+       if (!is_valid_ether_addr(ndev->dev_addr)) {
+               eth_hw_addr_random(ndev);
+               dev_warn(dev, "Using random MAC address: %pM\n",
+                        ndev->dev_addr);
+       }
+
+       priv = netdev_priv(ndev);
+       priv->base = base;
+       priv->irq = irq;
+       priv->ndev = ndev;
+       priv->msg_enable = netif_msg_init(-1, AVE_DEFAULT_MSG_ENABLE);
+       priv->phy_mode = phy_mode;
+       priv->data = data;
+
+       if (IS_DESC_64BIT(priv)) {
+               priv->desc_size = AVE_DESC_SIZE_64;
+               priv->tx.daddr  = AVE_TXDM_64;
+               priv->rx.daddr  = AVE_RXDM_64;
+       } else {
+               priv->desc_size = AVE_DESC_SIZE_32;
+               priv->tx.daddr  = AVE_TXDM_32;
+               priv->rx.daddr  = AVE_RXDM_32;
+       }
+       priv->tx.ndesc = AVE_NR_TXDESC;
+       priv->rx.ndesc = AVE_NR_RXDESC;
+
+       u64_stats_init(&priv->stats_rx.syncp);
+       u64_stats_init(&priv->stats_tx.syncp);
+
+       priv->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               ret = PTR_ERR(priv->clk);
+               goto out_free_netdev;
+       }
+
+       priv->rst = devm_reset_control_get_optional_shared(dev, NULL);
+       if (IS_ERR(priv->rst)) {
+               ret = PTR_ERR(priv->rst);
+               goto out_free_netdev;
+       }
+
+       priv->mdio = devm_mdiobus_alloc(dev);
+       if (!priv->mdio) {
+               ret = -ENOMEM;
+               goto out_free_netdev;
+       }
+       priv->mdio->priv = ndev;
+       priv->mdio->parent = dev;
+       priv->mdio->read = ave_mdiobus_read;
+       priv->mdio->write = ave_mdiobus_write;
+       priv->mdio->name = "uniphier-mdio";
+       snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "%s-%x",
+                pdev->name, pdev->id);
+
+       /* Register as a NAPI supported driver */
+       netif_napi_add(ndev, &priv->napi_rx, ave_poll_rx, priv->rx.ndesc);
+       netif_tx_napi_add(ndev, &priv->napi_tx, ave_poll_tx, priv->tx.ndesc);
+
+       ret = register_netdev(ndev);
+       if (ret) {
+               dev_err(dev, "failed to register netdevice\n");
+               goto out_del_napi;
+       }
+
+       platform_set_drvdata(pdev, ndev);
+
+       /* get ID and version */
+       ave_id = ave_r32(ndev, AVE_IDR);
+       ave_get_fwversion(ndev, buf, sizeof(buf));
+
+       dev_info(dev, "Socionext %c%c%c%c Ethernet IP %s (irq=%d, phy=%s)\n",
+                (ave_id >> 24) & 0xff, (ave_id >> 16) & 0xff,
+                (ave_id >> 8) & 0xff, (ave_id >> 0) & 0xff,
+                buf, priv->irq, phy_modes(phy_mode));
+
+       return 0;
+
+out_del_napi:
+       netif_napi_del(&priv->napi_rx);
+       netif_napi_del(&priv->napi_tx);
+out_free_netdev:
+       free_netdev(ndev);
+
+       return ret;
+}
+
+static int ave_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct ave_private *priv = netdev_priv(ndev);
+
+       unregister_netdev(ndev);
+       netif_napi_del(&priv->napi_rx);
+       netif_napi_del(&priv->napi_tx);
+       mdiobus_unregister(priv->mdio);
+       free_netdev(ndev);
+
+       return 0;
+}
+
+static const struct ave_soc_data ave_pro4_data = {
+       .is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_pxs2_data = {
+       .is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_ld11_data = {
+       .is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_ld20_data = {
+       .is_desc_64bit = true,
+};
+
+static const struct of_device_id of_ave_match[] = {
+       {
+               .compatible = "socionext,uniphier-pro4-ave4",
+               .data = &ave_pro4_data,
+       },
+       {
+               .compatible = "socionext,uniphier-pxs2-ave4",
+               .data = &ave_pxs2_data,
+       },
+       {
+               .compatible = "socionext,uniphier-ld11-ave4",
+               .data = &ave_ld11_data,
+       },
+       {
+               .compatible = "socionext,uniphier-ld20-ave4",
+               .data = &ave_ld20_data,
+       },
+       { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_ave_match);
+
+static struct platform_driver ave_driver = {
+       .probe  = ave_probe,
+       .remove = ave_remove,
+       .driver = {
+               .name = "ave",
+               .of_match_table = of_ave_match,
+       },
+};
+module_platform_driver(ave_driver);
+
+MODULE_DESCRIPTION("Socionext UniPhier AVE ethernet driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

Reply via email to