[RFC PATCH v2 11/11] riscv: soc: Allwinner D1 GMAC driver only for temp use

Guo Ren guoren at kernel.org
Sun Jun 6 08:32:22 PDT 2021


 ,

On Sun, Jun 6, 2021 at 6:50 PM Andre Przywara <andre.przywara at arm.com> wrote:
>
> On Sun,  6 Jun 2021 09:04:09 +0000
> guoren at kernel.org wrote:
>
> Hi,
>
> > From: liush <liush at allwinnertech.com>
> >
> > This is a temporary driver, only guaranteed to work on allwinner
> > D1. In order to ensure the developer's demand for network usage.
>
> That looks like some Allwinner BSP driver, please don't endorse code
> of this quality (just look at all that commented code and the attempt
> for compile-time configuration).
>
> > It only could work at 1Gps mode.
> >
> > The correct gmac driver should follow (I guess)
> > drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
> >
> > If anyone is familiar with it and can help porting, I would be
> > very grateful.
>
> Have you tried compiling and using that driver? Ideally it should just
> work, Linux drivers are meant to be portable, by design. And the driver
> is already enabled by COMPILE_TEST.
It still needs some work with dwmac-sun8i.c glue layer, eg:
tx/rx-delay setting, clk & pinmux drivers.

The patch is just to help people using D1 with GMAC temporarily with
network function.

> But I guess you need some extra care to make the non-coherent DMA work?
> I haven't looked in detail, but are those new CMOs hooked into the
> generic DMA framework?
Yes, we have the simliar principle with arm & csky for non-coherent:
 - Using PTE attributes setting Using PTE attributes to support
_PAGE_IOREMAP & _PAGE_WRITECOMBINE
 - Using CMO instructions deal SYNC_DMA_FOR_CPU/DEVICE.

>
> Cheers,
> Andre
>
> > Signed-off-by: Liu Shaohua <liush at allwinnertech.com>
> > Tested-by: Guo Ren <guoren at kernel.org>
> > Signed-off-by: Guo Ren <guoren at kernel.org>
> > Cc: Maxime Ripard <mripard at kernel.org>
> > Cc: Corentin Labbe <clabbe at baylibre.com>
> > Cc: Samuel Holland <samuel at sholland.org>
> > Cc: Icenowy Zheng <icenowy at aosc.io>
> > Cc: LABBE Corentin <clabbe.montjoie at gmail.com>
> > Cc: Michael Walle <michael at walle.cc>
> > Cc: Chen-Yu Tsai <wens at csie.org>
> > Cc: Maxime Ripard <maxime at cerno.tech>
> > Cc: Wei Fu <wefu at redhat.com>
> > Cc: Wei Wu <lazyparser at gmail.com>
> > Signed-off-by: Guo Ren <guoren at linux.alibaba.com>
> > ---
> >  .../boot/dts/allwinner/allwinner-d1-nezha-kit.dts  |    2 +-
> >  arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi    |   16 +
> >  drivers/net/ethernet/Kconfig                       |    1 +
> >  drivers/net/ethernet/Makefile                      |    1 +
> >  drivers/net/ethernet/allwinnertmp/Kconfig          |   17 +
> >  drivers/net/ethernet/allwinnertmp/Makefile         |    7 +
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c |  690 ++++++
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.c     | 2240 ++++++++++++++++++++
> >  drivers/net/ethernet/allwinnertmp/sunxi-gmac.h     |  258 +++
> >  drivers/net/phy/realtek.c                          |    2 +-
> >  10 files changed, 3232 insertions(+), 2 deletions(-)
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/Kconfig
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/Makefile
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> >  create mode 100644 drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> >
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > index cd9f7c9..31b681d 100644
> > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1-nezha-kit.dts
> > @@ -11,7 +11,7 @@
> >       compatible = "allwinner,d1-nezha-kit";
> >
> >       chosen {
> > -             bootargs = "console=ttyS0,115200";
> > +             bootargs = "console=ttyS0,115200 rootwait init=/sbin/init root=/dev/nfs rw nfsroot=192.168.101.200:/tmp/rootfs_nfs,v3,tcp,nolock ip=192.168.101.23";
> >               stdout-path = &serial0;
> >       };
> >
> > diff --git a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > index 11cd938..d317e19 100644
> > --- a/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > +++ b/arch/riscv/boot/dts/allwinner/allwinner-d1.dtsi
> > @@ -80,5 +80,21 @@
> >                       clocks = <&dummy_apb>;
> >                       status = "disabled";
> >               };
> > +
> > +             eth at 4500000 {
> > +                     compatible = "allwinner,sunxi-gmac";
> > +                     reg = <0x00 0x4500000 0x00 0x10000 0x00 0x3000030 0x00 0x04>;
> > +                     interrupts-extended = <&plic 0x3e 0x04>;
> > +                     interrupt-names = "gmacirq";
> > +                     device_type = "gmac0";
> > +                     phy-mode = "rgmii";
> > +                     use_ephy25m = <0x01>;
> > +                     tx-delay = <0x03>;
> > +                     rx-delay = <0x03>;
> > +                     gmac-power0;
> > +                     gmac-power1;
> > +                     gmac-power2;
> > +                     status = "okay";
> > +             };
> >       };
> >  };
> > diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
> > index 1cdff1d..1f8e37c 100644
> > --- a/drivers/net/ethernet/Kconfig
> > +++ b/drivers/net/ethernet/Kconfig
> > @@ -18,6 +18,7 @@ config MDIO
> >  config SUNGEM_PHY
> >       tristate
> >
> > +source "drivers/net/ethernet/allwinnertmp/Kconfig"
> >  source "drivers/net/ethernet/3com/Kconfig"
> >  source "drivers/net/ethernet/actions/Kconfig"
> >  source "drivers/net/ethernet/adaptec/Kconfig"
> > diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
> > index cb3f908..3dacc0c 100644
> > --- a/drivers/net/ethernet/Makefile
> > +++ b/drivers/net/ethernet/Makefile
> > @@ -3,6 +3,7 @@
> >  # Makefile for the Linux network Ethernet device drivers.
> >  #
> >
> > +obj-y += allwinnertmp/
> >  obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
> >  obj-$(CONFIG_NET_VENDOR_8390) += 8390/
> >  obj-$(CONFIG_NET_VENDOR_ACTIONS) += actions/
> > diff --git a/drivers/net/ethernet/allwinnertmp/Kconfig b/drivers/net/ethernet/allwinnertmp/Kconfig
> > new file mode 100644
> > index 00000000..4b7b378
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/Kconfig
> > @@ -0,0 +1,17 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +#
> > +# Allwinner device configuration
> > +#
> > +
> > +config SUNXI_GMAC
> > +     tristate "Allwinner GMAC support"
> > +     default y
> > +     depends on OF
> > +     select CRC32
> > +     select MII
> > +     select PHYLIB
> > +     help
> > +       Support for Allwinner Gigabit ethernet driver.
> > +
> > +       To compile this driver as a module, choose M here.  The module
> > +       will be called sunxi-gmac.
> > diff --git a/drivers/net/ethernet/allwinnertmp/Makefile b/drivers/net/ethernet/allwinnertmp/Makefile
> > new file mode 100644
> > index 00000000..1375dea
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/Makefile
> > @@ -0,0 +1,7 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +#
> > +# Makefile for the Allwinner device drivers.
> > +#
> > +
> > +obj-$(CONFIG_SUNXI_GMAC) += sunxi_gmac.o
> > +sunxi_gmac-objs := sunxi-gmac.o sunxi-gmac-ops.o
> > diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > new file mode 100644
> > index 00000000..26ffd7f
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac-ops.c
> > @@ -0,0 +1,690 @@
> > +/*
> > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
> > + *
> > + * Copyright © 2016-2018, fuzhaoke
> > + *           Author: fuzhaoke <fuzhaoke at allwinnertech.com>
> > + *
> > + * This file is provided under a dual BSD/GPL license.  When using or
> > + * redistributing this file, you may do so under either license.
> > + *
> > + * 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/kernel.h>
> > +#include <linux/ctype.h>
> > +#include <linux/printk.h>
> > +#include <linux/io.h>
> > +#include "sunxi-gmac.h"
> > +
> > +/******************************************************************************
> > + *   sun8iw6 operations
> > + *****************************************************************************/
> > +#define GETH_BASIC_CTL0              0x00
> > +#define GETH_BASIC_CTL1              0x04
> > +#define GETH_INT_STA         0x08
> > +#define GETH_INT_EN          0x0C
> > +#define GETH_TX_CTL0         0x10
> > +#define GETH_TX_CTL1         0x14
> > +#define GETH_TX_FLOW_CTL     0x1C
> > +#define GETH_TX_DESC_LIST    0x20
> > +#define GETH_RX_CTL0         0x24
> > +#define GETH_RX_CTL1         0x28
> > +#define GETH_RX_DESC_LIST    0x34
> > +#define GETH_RX_FRM_FLT              0x38
> > +#define GETH_RX_HASH0                0x40
> > +#define GETH_RX_HASH1                0x44
> > +#define GETH_MDIO_ADDR               0x48
> > +#define GETH_MDIO_DATA               0x4C
> > +#define GETH_ADDR_HI(reg)    (0x50 + ((reg) << 3))
> > +#define GETH_ADDR_LO(reg)    (0x54 + ((reg) << 3))
> > +#define GETH_TX_DMA_STA              0xB0
> > +#define GETH_TX_CUR_DESC     0xB4
> > +#define GETH_TX_CUR_BUF              0xB8
> > +#define GETH_RX_DMA_STA              0xC0
> > +#define GETH_RX_CUR_DESC     0xC4
> > +#define GETH_RX_CUR_BUF              0xC8
> > +#define GETH_RGMII_STA               0xD0
> > +
> > +#define RGMII_IRQ            0x00000001
> > +
> > +#define      CTL0_LM                 0x02
> > +#define CTL0_DM                      0x01
> > +#define CTL0_SPEED           0x04
> > +
> > +#define BURST_LEN            0x3F000000
> > +#define RX_TX_PRI            0x02
> > +#define SOFT_RST             0x01
> > +
> > +#define TX_FLUSH             0x01
> > +#define TX_MD                        0x02
> > +#define TX_NEXT_FRM          0x04
> > +#define TX_TH                        0x0700
> > +
> > +#define RX_FLUSH             0x01
> > +#define RX_MD                        0x02
> > +#define RX_RUNT_FRM          0x04
> > +#define RX_ERR_FRM           0x08
> > +#define RX_TH                        0x0030
> > +
> > +#define TX_INT                       0x00001
> > +#define TX_STOP_INT          0x00002
> > +#define TX_UA_INT            0x00004
> > +#define TX_TOUT_INT          0x00008
> > +#define TX_UNF_INT           0x00010
> > +#define TX_EARLY_INT         0x00020
> > +#define RX_INT                       0x00100
> > +#define RX_UA_INT            0x00200
> > +#define RX_STOP_INT          0x00400
> > +#define RX_TOUT_INT          0x00800
> > +#define RX_OVF_INT           0x01000
> > +#define RX_EARLY_INT         0x02000
> > +#define LINK_STA_INT         0x10000
> > +
> > +#define DISCARD_FRAME        -1
> > +#define GOOD_FRAME   0
> > +#define CSUM_NONE    2
> > +#define LLC_SNAP     4
> > +
> > +#define SF_DMA_MODE          1
> > +
> > +/* Flow Control defines */
> > +#define FLOW_OFF     0
> > +#define FLOW_RX              1
> > +#define FLOW_TX              2
> > +#define FLOW_AUTO    (FLOW_TX | FLOW_RX)
> > +
> > +#define HASH_TABLE_SIZE 64
> > +#define PAUSE_TIME 0x200
> > +#define GMAC_MAX_UNICAST_ADDRESSES   8
> > +
> > +/* PHY address */
> > +#define PHY_ADDR             0x01
> > +#define PHY_DM                       0x0010
> > +#define PHY_AUTO_NEG         0x0020
> > +#define PHY_POWERDOWN                0x0080
> > +#define PHY_NEG_EN           0x1000
> > +
> > +#define MII_BUSY             0x00000001
> > +#define MII_WRITE            0x00000002
> > +#define MII_PHY_MASK         0x0000FFC0
> > +#define MII_CR_MASK          0x0000001C
> > +#define MII_CLK                      0x00000008
> > +/* bits 4 3 2 | AHB1 Clock   | MDC Clock
> > + * -------------------------------------------------------
> > + *      0 0 0 | 60 ~ 100 MHz | div-42
> > + *      0 0 1 | 100 ~ 150 MHz        | div-62
> > + *      0 1 0 | 20 ~ 35 MHz  | div-16
> > + *      0 1 1 | 35 ~ 60 MHz  | div-26
> > + *      1 0 0 | 150 ~ 250 MHz        | div-102
> > + *      1 0 1 | 250 ~ 300 MHz        | div-124
> > + *      1 1 x | Reserved     |
> > + */
> > +
> > +enum csum_insertion {
> > +     cic_dis         = 0, /* Checksum Insertion Control */
> > +     cic_ip          = 1, /* Only IP header */
> > +     cic_no_pse      = 2, /* IP header but not pseudoheader */
> > +     cic_full        = 3, /* IP header and pseudoheader */
> > +};
> > +
> > +struct gethdev {
> > +     void *iobase;
> > +     unsigned int ver;
> > +     unsigned int mdc_div;
> > +};
> > +
> > +static struct gethdev hwdev;
> > +
> > +/***************************************************************************
> > + * External interface
> > + **************************************************************************/
> > +/* Set a ring desc buffer */
> > +void desc_init_chain(struct dma_desc *desc, unsigned long addr, unsigned int size)
> > +{
> > +     /* In chained mode the desc3 points to the next element in the ring.
> > +      * The latest element has to point to the head.
> > +      */
> > +     int i;
> > +     struct dma_desc *p = desc;
> > +     unsigned long dma_phy = addr;
> > +
> > +     for (i = 0; i < (size - 1); i++) {
> > +             dma_phy += sizeof(struct dma_desc);
> > +             p->desc3 = (unsigned int)dma_phy;
> > +             /* Chain mode */
> > +             p->desc1.all |= (1 << 24);
> > +             p++;
> > +     }
> > +     p->desc1.all |= (1 << 24);
> > +     p->desc3 = (unsigned int)addr;
> > +}
> > +
> > +int sunxi_mdio_read(void *iobase, int phyaddr, int phyreg)
> > +{
> > +     unsigned int value = 0;
> > +
> > +     /* Mask the MDC_DIV_RATIO */
> > +     value |= ((hwdev.mdc_div & 0x07) << 20);
> > +     value |= (((phyaddr << 12) & (0x0001F000)) |
> > +                     ((phyreg << 4) & (0x000007F0)) |
> > +                     MII_BUSY);
> > +
> > +     while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> > +             ;
> > +
> > +     writel(value, iobase + GETH_MDIO_ADDR);
> > +     while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> > +             ;
> > +
> > +     return (int)readl(iobase + GETH_MDIO_DATA);
> > +}
> > +
> > +int sunxi_mdio_write(void *iobase, int phyaddr, int phyreg, unsigned short data)
> > +{
> > +     unsigned int value;
> > +
> > +     value = ((0x07 << 20) & readl(iobase + GETH_MDIO_ADDR)) |
> > +              (hwdev.mdc_div << 20);
> > +     value |= (((phyaddr << 12) & (0x0001F000)) |
> > +               ((phyreg << 4) & (0x000007F0))) |
> > +               MII_WRITE | MII_BUSY;
> > +
> > +     /* Wait until any existing MII operation is complete */
> > +     while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> > +             ;
> > +
> > +     /* Set the MII address register to write */
> > +     writel(data, iobase + GETH_MDIO_DATA);
> > +     writel(value, iobase + GETH_MDIO_ADDR);
> > +
> > +     /* Wait until any existing MII operation is complete */
> > +     while (((readl(iobase + GETH_MDIO_ADDR)) & MII_BUSY) == 1)
> > +             ;
> > +
> > +     return 0;
> > +}
> > +
> > +int sunxi_mdio_reset(void *iobase)
> > +{
> > +     writel((4 << 2), iobase + GETH_MDIO_ADDR);
> > +     return 0;
> > +}
> > +
> > +void sunxi_set_link_mode(void *iobase, int duplex, int speed)
> > +{
> > +     unsigned int ctrl = readl(iobase + GETH_BASIC_CTL0);
> > +
> > +     if (!duplex)
> > +             ctrl &= ~CTL0_DM;
> > +     else
> > +             ctrl |= CTL0_DM;
> > +
> > +     switch (speed) {
> > +     case 1000:
> > +             ctrl &= ~0x0C;
> > +             break;
> > +     case 100:
> > +     case 10:
> > +     default:
> > +             ctrl |= 0x08;
> > +             if (speed == 100)
> > +                     ctrl |= 0x04;
> > +             else
> > +                     ctrl &= ~0x04;
> > +             break;
> > +     }
> > +
> > +     writel(ctrl, iobase + GETH_BASIC_CTL0);
> > +}
> > +
> > +void sunxi_mac_loopback(void *iobase, int enable)
> > +{
> > +     int reg;
> > +
> > +     reg = readl(iobase + GETH_BASIC_CTL0);
> > +     if (enable)
> > +             reg |= 0x02;
> > +     else
> > +             reg &= ~0x02;
> > +     writel(reg, iobase + GETH_BASIC_CTL0);
> > +}
> > +
> > +void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause)
> > +{
> > +     unsigned int flow = 0;
> > +
> > +     if (fc & FLOW_RX) {
> > +             flow = readl(iobase + GETH_RX_CTL0);
> > +             flow |= 0x10000;
> > +             writel(flow, iobase + GETH_RX_CTL0);
> > +     }
> > +
> > +     if (fc & FLOW_TX) {
> > +             flow = readl(iobase + GETH_TX_FLOW_CTL);
> > +             flow |= 0x00001;
> > +             writel(flow, iobase + GETH_TX_FLOW_CTL);
> > +     }
> > +
> > +     if (duplex) {
> > +             flow = readl(iobase + GETH_TX_FLOW_CTL);
> > +             flow |= (pause << 4);
> > +             writel(flow, iobase + GETH_TX_FLOW_CTL);
> > +     }
> > +}
> > +
> > +int sunxi_int_status(void *iobase, struct geth_extra_stats *x)
> > +{
> > +     int ret = 0;
> > +     /* read the status register (CSR5) */
> > +     unsigned int intr_status;
> > +
> > +     intr_status = readl(iobase + GETH_RGMII_STA);
> > +     if (intr_status & RGMII_IRQ)
> > +             readl(iobase + GETH_RGMII_STA);
> > +
> > +     intr_status = readl(iobase + GETH_INT_STA);
> > +
> > +     /* ABNORMAL interrupts */
> > +     if (intr_status & TX_UNF_INT) {
> > +             ret = tx_hard_error_bump_tc;
> > +             x->tx_undeflow_irq++;
> > +     }
> > +     if (intr_status & TX_TOUT_INT) {
> > +             x->tx_jabber_irq++;
> > +     }
> > +     if (intr_status & RX_OVF_INT) {
> > +             x->rx_overflow_irq++;
> > +     }
> > +     if (intr_status & RX_UA_INT) {
> > +             x->rx_buf_unav_irq++;
> > +     }
> > +     if (intr_status & RX_STOP_INT) {
> > +             x->rx_process_stopped_irq++;
> > +     }
> > +     if (intr_status & RX_TOUT_INT) {
> > +             x->rx_watchdog_irq++;
> > +     }
> > +     if (intr_status & TX_EARLY_INT) {
> > +             x->tx_early_irq++;
> > +     }
> > +     if (intr_status & TX_STOP_INT) {
> > +             x->tx_process_stopped_irq++;
> > +             ret = tx_hard_error;
> > +     }
> > +
> > +     /* TX/RX NORMAL interrupts */
> > +     if (intr_status & (TX_INT | RX_INT | RX_EARLY_INT | TX_UA_INT)) {
> > +             x->normal_irq_n++;
> > +             if (intr_status & (TX_INT | RX_INT))
> > +                     ret = handle_tx_rx;
> > +     }
> > +     /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
> > +     writel(intr_status & 0x3FFF, iobase + GETH_INT_STA);
> > +
> > +     return ret;
> > +}
> > +
> > +void sunxi_start_rx(void *iobase, unsigned long rxbase)
> > +{
> > +     unsigned int value;
> > +
> > +     /* Write the base address of Rx descriptor lists into registers */
> > +     writel(rxbase, iobase + GETH_RX_DESC_LIST);
> > +
> > +     value = readl(iobase + GETH_RX_CTL1);
> > +     value |= 0x40000000;
> > +     writel(value, iobase + GETH_RX_CTL1);
> > +}
> > +
> > +void sunxi_stop_rx(void *iobase)
> > +{
> > +     unsigned int value;
> > +
> > +     value = readl(iobase + GETH_RX_CTL1);
> > +     value &= ~0x40000000;
> > +     writel(value, iobase + GETH_RX_CTL1);
> > +}
> > +
> > +void sunxi_start_tx(void *iobase, unsigned long txbase)
> > +{
> > +     unsigned int value;
> > +
> > +     /* Write the base address of Tx descriptor lists into registers */
> > +     writel(txbase, iobase + GETH_TX_DESC_LIST);
> > +
> > +     value = readl(iobase + GETH_TX_CTL1);
> > +     value |= 0x40000000;
> > +     writel(value, iobase + GETH_TX_CTL1);
> > +}
> > +
> > +void sunxi_stop_tx(void *iobase)
> > +{
> > +     unsigned int value = readl(iobase + GETH_TX_CTL1);
> > +
> > +     value &= ~0x40000000;
> > +     writel(value, iobase + GETH_TX_CTL1);
> > +}
> > +
> > +static int sunxi_dma_init(void *iobase)
> > +{
> > +     unsigned int value;
> > +
> > +     /* Burst should be 8 */
> > +     value = (8 << 24);
> > +
> > +#ifdef CONFIG_GMAC_DA
> > +     value |= RX_TX_PRI;     /* Rx has priority over tx */
> > +#endif
> > +     writel(value, iobase + GETH_BASIC_CTL1);
> > +
> > +     /* Mask interrupts by writing to CSR7 */
> > +     writel(RX_INT | TX_UNF_INT, iobase + GETH_INT_EN);
> > +
> > +     return 0;
> > +}
> > +
> > +int sunxi_mac_init(void *iobase, int txmode, int rxmode)
> > +{
> > +     unsigned int value;
> > +
> > +     sunxi_dma_init(iobase);
> > +
> > +     /* Initialize the core component */
> > +     value = readl(iobase + GETH_TX_CTL0);
> > +     value |= (1 << 30);     /* Jabber Disable */
> > +     writel(value, iobase + GETH_TX_CTL0);
> > +
> > +     value = readl(iobase + GETH_RX_CTL0);
> > +     value |= (1 << 27);     /* Enable CRC & IPv4 Header Checksum */
> > +     value |= (1 << 28);     /* Automatic Pad/CRC Stripping */
> > +     value |= (1 << 29);     /* Jumbo Frame Enable */
> > +     writel(value, iobase + GETH_RX_CTL0);
> > +
> > +     writel((hwdev.mdc_div << 20), iobase + GETH_MDIO_ADDR); /* MDC_DIV_RATIO */
> > +
> > +     /* Set the Rx&Tx mode */
> > +     value = readl(iobase + GETH_TX_CTL1);
> > +     if (txmode == SF_DMA_MODE) {
> > +             /* Transmit COE type 2 cannot be done in cut-through mode. */
> > +             value |= TX_MD;
> > +             /* Operating on second frame increase the performance
> > +              * especially when transmit store-and-forward is used.
> > +              */
> > +             value |= TX_NEXT_FRM;
> > +     } else {
> > +             value &= ~TX_MD;
> > +             value &= ~TX_TH;
> > +             /* Set the transmit threshold */
> > +             if (txmode <= 64)
> > +                     value |= 0x00000000;
> > +             else if (txmode <= 128)
> > +                     value |= 0x00000100;
> > +             else if (txmode <= 192)
> > +                     value |= 0x00000200;
> > +             else
> > +                     value |= 0x00000300;
> > +     }
> > +     writel(value, iobase + GETH_TX_CTL1);
> > +
> > +     value = readl(iobase + GETH_RX_CTL1);
> > +     if (rxmode == SF_DMA_MODE) {
> > +             value |= RX_MD;
> > +     } else {
> > +             value &= ~RX_MD;
> > +             value &= ~RX_TH;
> > +             if (rxmode <= 32)
> > +                     value |= 0x10;
> > +             else if (rxmode <= 64)
> > +                     value |= 0x00;
> > +             else if (rxmode <= 96)
> > +                     value |= 0x20;
> > +             else
> > +                     value |= 0x30;
> > +     }
> > +
> > +     /* Forward frames with error and undersized good frame. */
> > +     value |= (RX_ERR_FRM | RX_RUNT_FRM);
> > +
> > +     writel(value, iobase + GETH_RX_CTL1);
> > +
> > +     return 0;
> > +}
> > +
> > +void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high)
> > +{
> > +     writel(high, iobase + GETH_RX_HASH0);
> > +     writel(low, iobase + GETH_RX_HASH1);
> > +}
> > +
> > +void sunxi_set_filter(void *iobase, unsigned long flags)
> > +{
> > +     int tmp_flags = 0;
> > +
> > +     tmp_flags |= ((flags >> 31) |
> > +                     ((flags >> 9) & 0x00000002) |
> > +                     ((flags << 1) & 0x00000010) |
> > +                     ((flags >> 3) & 0x00000060) |
> > +                     ((flags << 7) & 0x00000300) |
> > +                     ((flags << 6) & 0x00003000) |
> > +                     ((flags << 12) & 0x00030000) |
> > +                     (flags << 31));
> > +
> > +     writel(tmp_flags, iobase + GETH_RX_FRM_FLT);
> > +}
> > +
> > +void sunxi_set_umac(void *iobase, unsigned char *addr, int index)
> > +{
> > +     unsigned long data;
> > +
> > +     data = (addr[5] << 8) | addr[4];
> > +     writel(data, iobase + GETH_ADDR_HI(index));
> > +     data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
> > +     writel(data, iobase + GETH_ADDR_LO(index));
> > +}
> > +
> > +void sunxi_mac_enable(void *iobase)
> > +{
> > +     unsigned long value;
> > +
> > +     value = readl(iobase + GETH_TX_CTL0);
> > +     value |= (1 << 31);
> > +     writel(value, iobase + GETH_TX_CTL0);
> > +
> > +     value = readl(iobase + GETH_RX_CTL0);
> > +     value |= (1 << 31);
> > +     writel(value, iobase + GETH_RX_CTL0);
> > +}
> > +
> > +void sunxi_mac_disable(void *iobase)
> > +{
> > +     unsigned long value;
> > +
> > +     value = readl(iobase + GETH_TX_CTL0);
> > +     value &= ~(1 << 31);
> > +     writel(value, iobase + GETH_TX_CTL0);
> > +
> > +     value = readl(iobase + GETH_RX_CTL0);
> > +     value &= ~(1 << 31);
> > +     writel(value, iobase + GETH_RX_CTL0);
> > +}
> > +
> > +void sunxi_tx_poll(void *iobase)
> > +{
> > +     unsigned int value;
> > +
> > +     value = readl(iobase + GETH_TX_CTL1);
> > +     writel(value | 0x80000000, iobase + GETH_TX_CTL1);
> > +}
> > +
> > +void sunxi_rx_poll(void *iobase)
> > +{
> > +     unsigned int value;
> > +
> > +     value = readl(iobase + GETH_RX_CTL1);
> > +     writel(value | 0x80000000, iobase + GETH_RX_CTL1);
> > +}
> > +
> > +void sunxi_int_enable(void *iobase)
> > +{
> > +     writel(RX_INT | TX_UNF_INT, iobase + GETH_INT_EN);
> > +}
> > +
> > +void sunxi_int_disable(void *iobase)
> > +{
> > +     writel(0, iobase + GETH_INT_EN);
> > +}
> > +
> > +void desc_buf_set(struct dma_desc *desc, unsigned long paddr, int size)
> > +{
> > +     desc->desc1.all &= (~((1 << 11) - 1));
> > +     desc->desc1.all |= (size & ((1 << 11) - 1));
> > +     desc->desc2 = paddr;
> > +}
> > +
> > +void desc_set_own(struct dma_desc *desc)
> > +{
> > +     desc->desc0.all |= 0x80000000;
> > +}
> > +
> > +void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert)
> > +{
> > +     struct dma_desc *desc = first;
> > +
> > +     first->desc1.tx.first_sg = 1;
> > +     end->desc1.tx.last_seg = 1;
> > +     end->desc1.tx.interrupt = 1;
> > +
> > +     if (csum_insert)
> > +             do {
> > +                     desc->desc1.tx.cic = 3;
> > +                     desc++;
> > +             } while (desc <= end);
> > +}
> > +
> > +void desc_init(struct dma_desc *desc)
> > +{
> > +     desc->desc1.all = 0;
> > +     desc->desc2  = 0;
> > +
> > +     desc->desc1.all |= (1 << 24);
> > +}
> > +
> > +int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x)
> > +{
> > +     int ret = 0;
> > +
> > +     if (desc->desc0.tx.under_err) {
> > +             x->tx_underflow++;
> > +             ret = -1;
> > +     }
> > +     if (desc->desc0.tx.no_carr) {
> > +             x->tx_carrier++;
> > +             ret = -1;
> > +     }
> > +     if (desc->desc0.tx.loss_carr) {
> > +             x->tx_losscarrier++;
> > +             ret = -1;
> > +     }
> > +
> > +#if 0
> > +     if ((desc->desc0.tx.ex_deferral) ||
> > +                     (desc->desc0.tx.ex_coll) ||
> > +                     (desc->desc0.tx.late_coll))
> > +             stats->collisions += desc->desc0.tx.coll_cnt;
> > +#endif
> > +
> > +     if (desc->desc0.tx.deferred)
> > +             x->tx_deferred++;
> > +
> > +     return ret;
> > +}
> > +
> > +int desc_buf_get_len(struct dma_desc *desc)
> > +{
> > +     return (desc->desc1.all & ((1 << 11) - 1));
> > +}
> > +
> > +int desc_buf_get_addr(struct dma_desc *desc)
> > +{
> > +     return desc->desc2;
> > +}
> > +
> > +int desc_rx_frame_len(struct dma_desc *desc)
> > +{
> > +     return desc->desc0.rx.frm_len;
> > +}
> > +
> > +int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x)
> > +{
> > +     int ret = good_frame;
> > +
> > +     if (desc->desc0.rx.last_desc == 0) {
> > +             return discard_frame;
> > +     }
> > +
> > +     if (desc->desc0.rx.err_sum) {
> > +             if (desc->desc0.rx.desc_err)
> > +                     x->rx_desc++;
> > +
> > +             if (desc->desc0.rx.sou_filter)
> > +                     x->sa_filter_fail++;
> > +
> > +             if (desc->desc0.rx.over_err)
> > +                     x->overflow_error++;
> > +
> > +             if (desc->desc0.rx.ipch_err)
> > +                     x->ipc_csum_error++;
> > +
> > +             if (desc->desc0.rx.late_coll)
> > +                     x->rx_collision++;
> > +
> > +             if (desc->desc0.rx.crc_err)
> > +                     x->rx_crc++;
> > +
> > +             ret = discard_frame;
> > +     }
> > +
> > +     if (desc->desc0.rx.len_err) {
> > +             ret = discard_frame;
> > +     }
> > +     if (desc->desc0.rx.mii_err) {
> > +             ret = discard_frame;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +int desc_get_own(struct dma_desc *desc)
> > +{
> > +     return desc->desc0.all & 0x80000000;
> > +}
> > +
> > +int desc_get_tx_ls(struct dma_desc *desc)
> > +{
> > +     return desc->desc1.tx.last_seg;
> > +}
> > +
> > +int sunxi_geth_register(void *iobase, int version, unsigned int div)
> > +{
> > +     hwdev.ver = version;
> > +     hwdev.iobase = iobase;
> > +     hwdev.mdc_div = div;
> > +
> > +     return 0;
> > +}
> > +
> > +int sunxi_mac_reset(void *iobase, void (*delay)(int), int n)
> > +{
> > +     unsigned int value;
> > +
> > +     /* DMA SW reset */
> > +     value = readl(iobase + GETH_BASIC_CTL1);
> > +     value |= SOFT_RST;
> > +     writel(value, iobase + GETH_BASIC_CTL1);
> > +
> > +     delay(n);
> > +
> > +     return !!(readl(iobase + GETH_BASIC_CTL1) & SOFT_RST);
> > +}
> > diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> > new file mode 100644
> > index 00000000..0c67877
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.c
> > @@ -0,0 +1,2240 @@
> > +/*
> > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac.c
> > + *
> > + * Copyright © 2016-2018, fuzhaoke
> > + *           Author: fuzhaoke <fuzhaoke at allwinnertech.com>
> > + *
> > + * This file is provided under a dual BSD/GPL license.  When using or
> > + * redistributing this file, you may do so under either license.
> > + *
> > + * 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/clk.h>
> > +//#include <linux/clk-provider.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 <crypto/algapi.h>
> > +#include <crypto/hash.h>
> > +#include <linux/err.h>
> > +#include <linux/scatterlist.h>
> > +//#include <linux/regulator/consumer.h>
> > +#include <linux/of_net.h>
> > +//#include <linux/of_gpio.h>
> > +#include <linux/io.h>
> > +//#include <linux/sunxi-sid.h>
> > +//#include <linux/sunxi-gpio.h>
> > +//#include <linux/reset.h>
> > +#include "sunxi-gmac.h"
> > +
> > +#define SUNXI_GMAC_VERSION "1.0.0"
> > +
> > +#define DMA_DESC_RX  256
> > +#define DMA_DESC_TX  256
> > +#define BUDGET               (dma_desc_rx / 4)
> > +#define TX_THRESH    (dma_desc_tx / 4)
> > +
> > +#define HASH_TABLE_SIZE      64
> > +#define MAX_BUF_SZ   (SZ_2K - 1)
> > +
> > +#define POWER_CHAN_NUM       3
> > +
> > +#undef PKT_DEBUG
> > +#undef DESC_PRINT
> > +
> > +#define circ_cnt(head, tail, size) (((head) > (tail)) ? \
> > +                                     ((head) - (tail)) : \
> > +                                     ((head) - (tail)) & ((size) - 1))
> > +
> > +#define circ_space(head, tail, size) circ_cnt((tail), ((head) + 1), (size))
> > +
> > +#define circ_inc(n, s) (((n) + 1) % (s))
> > +
> > +#define GETH_MAC_ADDRESS "00:00:00:00:00:00"
> > +static char *mac_str = GETH_MAC_ADDRESS;
> > +module_param(mac_str, charp, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(mac_str, "MAC Address String.(xx:xx:xx:xx:xx:xx)");
> > +
> > +static int rxmode = 1;
> > +module_param(rxmode, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(rxmode, "DMA threshold control value");
> > +
> > +static int txmode = 1;
> > +module_param(txmode, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(txmode, "DMA threshold control value");
> > +
> > +static int pause = 0x400;
> > +module_param(pause, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(pause, "Flow Control Pause Time");
> > +
> > +#define TX_TIMEO     5000
> > +static int watchdog = TX_TIMEO;
> > +module_param(watchdog, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds");
> > +
> > +static int dma_desc_rx = DMA_DESC_RX;
> > +module_param(dma_desc_rx, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(watchdog, "The number of receive's descriptors");
> > +
> > +static int dma_desc_tx = DMA_DESC_TX;
> > +module_param(dma_desc_tx, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(watchdog, "The number of transmit's descriptors");
> > +
> > +/* - 0: Flow Off
> > + * - 1: Rx Flow
> > + * - 2: Tx Flow
> > + * - 3: Rx & Tx Flow
> > + */
> > +static int flow_ctrl;
> > +module_param(flow_ctrl, int, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(flow_ctrl, "Flow control [0: off, 1: rx, 2: tx, 3: both]");
> > +
> > +struct geth_priv {
> > +     struct dma_desc *dma_tx;
> > +     struct sk_buff **tx_sk;
> > +     unsigned int tx_clean;
> > +     unsigned int tx_dirty;
> > +     dma_addr_t dma_tx_phy;
> > +
> > +     unsigned long buf_sz;
> > +
> > +     struct dma_desc *dma_rx;
> > +     struct sk_buff **rx_sk;
> > +     unsigned int rx_clean;
> > +     unsigned int rx_dirty;
> > +     dma_addr_t dma_rx_phy;
> > +
> > +     struct net_device *ndev;
> > +     struct device *dev;
> > +     struct napi_struct napi;
> > +
> > +     struct geth_extra_stats xstats;
> > +
> > +     struct mii_bus *mii;
> > +     int link;
> > +     int speed;
> > +     int duplex;
> > +#define INT_PHY 0
> > +#define EXT_PHY 1
> > +     int phy_ext;
> > +     phy_interface_t phy_interface;
> > +
> > +     void __iomem *base;
> > +     void __iomem *base_phy;
> > +/*
> > +     struct clk *geth_clk;
> > +     struct clk *ephy_clk;
> > +     struct reset_control *reset;
> > +     struct pinctrl *pinctrl;
> > +*/
> > +     struct regulator *gmac_power[POWER_CHAN_NUM];
> > +     bool is_suspend;
> > +     int phyrst;
> > +     u8  rst_active_low;
> > +     /* definition spinlock */
> > +     spinlock_t lock;
> > +     spinlock_t tx_lock;
> > +
> > +     /* whether using ephy_clk */
> > +     int use_ephy_clk;
> > +     int phy_addr;
> > +
> > +     /* adjust transmit clock delay, value: 0~7 */
> > +     /* adjust receive clock delay, value: 0~31 */
> > +     unsigned int tx_delay;
> > +     unsigned int rx_delay;
> > +
> > +     /* resume work */
> > +     struct work_struct eth_work;
> > +};
> > +
> > +static u64 geth_dma_mask = DMA_BIT_MASK(32);
> > +
> > +void sunxi_udelay(int n)
> > +{
> > +     udelay(n);
> > +}
> > +
> > +static int geth_stop(struct net_device *ndev);
> > +static int geth_open(struct net_device *ndev);
> > +static void geth_tx_complete(struct geth_priv *priv);
> > +static void geth_rx_refill(struct net_device *ndev);
> > +
> > +#ifdef CONFIG_GETH_ATTRS
> > +static ssize_t adjust_bgs_show(struct device *dev, struct device_attribute *attr, char *buf)
> > +{
> > +     int value = 0;
> > +     u32 efuse_value;
> > +     struct net_device *ndev = to_net_dev(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     if (priv->phy_ext == INT_PHY) {
> > +             value = readl(priv->base_phy) >> 28;
> > +             if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0)
> > +                     pr_err("get PHY efuse fail!\n");
> > +             else
> > +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> > +                     value = value - ((efuse_value >> 24) & 0x0F);
> > +#else
> > +                     pr_warn("miss config come from efuse!\n");
> > +#endif
> > +     }
> > +
> > +     return sprintf(buf, "bgs: %d\n", value);
> > +}
> > +
> > +static ssize_t adjust_bgs_write(struct device *dev, struct device_attribute *attr,
> > +                             const char *buf, size_t count)
> > +{
> > +     unsigned int out = 0;
> > +     struct net_device *ndev = to_net_dev(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     u32 clk_value = readl(priv->base_phy);
> > +     u32 efuse_value;
> > +
> > +     out = simple_strtoul(buf, NULL, 10);
> > +
> > +     if (priv->phy_ext == INT_PHY) {
> > +             clk_value &= ~(0xF << 28);
> > +             if (sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value) != 0)
> > +                     pr_err("get PHY efuse fail!\n");
> > +             else
> > +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> > +                     clk_value |= (((efuse_value >> 24) & 0x0F) + out) << 28;
> > +#else
> > +                     pr_warn("miss config come from efuse!\n");
> > +#endif
> > +     }
> > +
> > +     writel(clk_value, priv->base_phy);
> > +
> > +     return count;
> > +}
> > +
> > +static struct device_attribute adjust_reg[] = {
> > +     __ATTR(adjust_bgs, 0664, adjust_bgs_show, adjust_bgs_write),
> > +};
> > +
> > +static int geth_create_attrs(struct net_device *ndev)
> > +{
> > +     int j, ret;
> > +
> > +     for (j = 0; j < ARRAY_SIZE(adjust_reg); j++) {
> > +             ret = device_create_file(&ndev->dev, &adjust_reg[j]);
> > +             if (ret)
> > +                     goto sysfs_failed;
> > +     }
> > +     goto succeed;
> > +
> > +sysfs_failed:
> > +     while (j--)
> > +             device_remove_file(&ndev->dev, &adjust_reg[j]);
> > +succeed:
> > +     return ret;
> > +}
> > +#endif
> > +
> > +#ifdef DEBUG
> > +static void desc_print(struct dma_desc *desc, int size)
> > +{
> > +#ifdef DESC_PRINT
> > +     int i;
> > +
> > +     for (i = 0; i < size; i++) {
> > +             u32 *x = (u32 *)(desc + i);
> > +
> > +             pr_info("\t%d [0x%08lx]: %08x %08x %08x %08x\n",
> > +                     i, (unsigned long)(&desc[i]),
> > +                     x[0], x[1], x[2], x[3]);
> > +     }
> > +     pr_info("\n");
> > +#endif
> > +}
> > +#endif
> > +
> > +static ssize_t extra_tx_stats_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     if (!dev) {
> > +             pr_err("Argment is invalid\n");
> > +             return 0;
> > +     }
> > +
> > +     if (!ndev) {
> > +             pr_err("Net device is null\n");
> > +             return 0;
> > +     }
> > +
> > +     return sprintf(buf, "tx_underflow: %lu\ntx_carrier: %lu\n"
> > +                     "tx_losscarrier: %lu\nvlan_tag: %lu\n"
> > +                     "tx_deferred: %lu\ntx_vlan: %lu\n"
> > +                     "tx_jabber: %lu\ntx_frame_flushed: %lu\n"
> > +                     "tx_payload_error: %lu\ntx_ip_header_error: %lu\n\n",
> > +                     priv->xstats.tx_underflow, priv->xstats.tx_carrier,
> > +                     priv->xstats.tx_losscarrier, priv->xstats.vlan_tag,
> > +                     priv->xstats.tx_deferred, priv->xstats.tx_vlan,
> > +                     priv->xstats.tx_jabber, priv->xstats.tx_frame_flushed,
> > +                     priv->xstats.tx_payload_error, priv->xstats.tx_ip_header_error);
> > +}
> > +static DEVICE_ATTR(extra_tx_stats, 0444, extra_tx_stats_show, NULL);
> > +
> > +static ssize_t extra_rx_stats_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     if (!dev) {
> > +             pr_err("Argment is invalid\n");
> > +             return 0;
> > +     }
> > +
> > +     if (!ndev) {
> > +             pr_err("Net device is null\n");
> > +             return 0;
> > +     }
> > +
> > +     return sprintf(buf, "rx_desc: %lu\nsa_filter_fail: %lu\n"
> > +                     "overflow_error: %lu\nipc_csum_error: %lu\n"
> > +                     "rx_collision: %lu\nrx_crc: %lu\n"
> > +                     "dribbling_bit: %lu\nrx_length: %lu\n"
> > +                     "rx_mii: %lu\nrx_multicast: %lu\n"
> > +                     "rx_gmac_overflow: %lu\nrx_watchdog: %lu\n"
> > +                     "da_rx_filter_fail: %lu\nsa_rx_filter_fail: %lu\n"
> > +                     "rx_missed_cntr: %lu\nrx_overflow_cntr: %lu\n"
> > +                     "rx_vlan: %lu\n\n",
> > +                     priv->xstats.rx_desc, priv->xstats.sa_filter_fail,
> > +                     priv->xstats.overflow_error, priv->xstats.ipc_csum_error,
> > +                     priv->xstats.rx_collision, priv->xstats.rx_crc,
> > +                     priv->xstats.dribbling_bit, priv->xstats.rx_length,
> > +                     priv->xstats.rx_mii, priv->xstats.rx_multicast,
> > +                     priv->xstats.rx_gmac_overflow, priv->xstats.rx_length,
> > +                     priv->xstats.da_rx_filter_fail, priv->xstats.sa_rx_filter_fail,
> > +                     priv->xstats.rx_missed_cntr, priv->xstats.rx_overflow_cntr,
> > +                     priv->xstats.rx_vlan);
> > +}
> > +static DEVICE_ATTR(extra_rx_stats, 0444, extra_rx_stats_show, NULL);
> > +
> > +static ssize_t gphy_test_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +
> > +     if (!dev) {
> > +             pr_err("Argment is invalid\n");
> > +             return 0;
> > +     }
> > +
> > +     if (!ndev) {
> > +             pr_err("Net device is null\n");
> > +             return 0;
> > +     }
> > +
> > +     return sprintf(buf, "Usage:\necho [0/1/2/3/4] > gphy_test\n"
> > +                     "0 - Normal Mode\n"
> > +                     "1 - Transmit Jitter Test\n"
> > +                     "2 - Transmit Jitter Test(MASTER mode)\n"
> > +                     "3 - Transmit Jitter Test(SLAVE mode)\n"
> > +                     "4 - Transmit Distortion Test\n\n");
> > +}
> > +
> > +static ssize_t gphy_test_store(struct device *dev,
> > +             struct device_attribute *attr, const char *buf, size_t count)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     u16 value = 0;
> > +     int ret = 0;
> > +     u16 data = 0;
> > +
> > +     if (!dev) {
> > +             pr_err("Argument is invalid\n");
> > +             return count;
> > +     }
> > +
> > +     if (!ndev) {
> > +             pr_err("Net device is null\n");
> > +             return count;
> > +     }
> > +
> > +     data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_CTRL1000);
> > +
> > +     ret = kstrtou16(buf, 0, &value);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (value >= 0 && value <= 4) {
> > +             data &= ~(0x7 << 13);
> > +             data |= value << 13;
> > +             sunxi_mdio_write(priv->base, priv->phy_addr, MII_CTRL1000, data);
> > +             pr_info("Set MII_CTRL1000(0x09) Reg: 0x%x\n", data);
> > +     } else {
> > +             pr_info("unknown value (%d)\n", value);
> > +     }
> > +
> > +     return count;
> > +}
> > +
> > +static DEVICE_ATTR(gphy_test, 0664, gphy_test_show, gphy_test_store);
> > +
> > +static ssize_t mii_reg_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     struct net_device *ndev = NULL;
> > +     struct geth_priv *priv = NULL;
> > +
> > +     if (dev == NULL) {
> > +             pr_err("Argment is invalid\n");
> > +             return 0;
> > +     }
> > +
> > +     ndev = dev_get_drvdata(dev);
> > +     if (ndev == NULL) {
> > +             pr_err("Net device is null\n");
> > +             return 0;
> > +     }
> > +
> > +     priv = netdev_priv(ndev);
> > +     if (priv == NULL) {
> > +             pr_err("geth_priv is null\n");
> > +             return 0;
> > +     }
> > +
> > +     if (!netif_running(ndev)) {
> > +             pr_warn("eth is down!\n");
> > +             return 0;
> > +     }
> > +
> > +     return sprintf(buf,
> > +             "Current MII Registers:\n"
> > +             "BMCR[0x%02x] = 0x%04x,\t\tBMSR[0x%02x] = 0x%04x,\t\tPHYSID1[0x%02x] = 0x%04x\n"
> > +             "PHYSID2[0x%02x] = 0x%04x,\t\tADVERTISE[0x%02x] = 0x%04x,\tLPA[0x%02x] = 0x%04x\n"
> > +             "EXPANSION[0x%02x] = 0x%04x,\tCTRL1000[0x%02x] = 0x%04x,\tSTAT1000[0x%02x] = 0x%04x\n",
> > +             MII_BMCR, sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR),
> > +             MII_BMSR, sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMSR),
> > +             MII_PHYSID1, sunxi_mdio_read(priv->base, priv->phy_addr, MII_PHYSID1),
> > +             MII_PHYSID2, sunxi_mdio_read(priv->base, priv->phy_addr, MII_PHYSID2),
> > +             MII_ADVERTISE, sunxi_mdio_read(priv->base, priv->phy_addr, MII_ADVERTISE),
> > +             MII_LPA, sunxi_mdio_read(priv->base, priv->phy_addr, MII_LPA),
> > +             MII_EXPANSION, sunxi_mdio_read(priv->base, priv->phy_addr, MII_EXPANSION),
> > +             MII_CTRL1000, sunxi_mdio_read(priv->base, priv->phy_addr, MII_CTRL1000),
> > +             MII_STAT1000, sunxi_mdio_read(priv->base, priv->phy_addr, MII_STAT1000));
> > +}
> > +static DEVICE_ATTR(mii_reg, 0444, mii_reg_show, NULL);
> > +
> > +static ssize_t loopback_test_show(struct device *dev,
> > +             struct device_attribute *attr, char *buf)
> > +{
> > +     return sprintf(buf, "Usage:\necho [0/1/2] > loopback_test\n"
> > +                     "0 - Normal Mode\n"
> > +                     "1 - Mac loopback test mode\n"
> > +                     "2 - Phy loopback test mode\n");
> > +}
> > +
> > +static ssize_t loopback_test_store(struct device *dev,
> > +             struct device_attribute *attr, const char *buf, size_t count)
> > +{
> > +     struct net_device *ndev = NULL;
> > +     struct geth_priv *priv = NULL;
> > +     u16 value = 0;
> > +     int ret = 0;
> > +     u16 data = 0;
> > +
> > +     if (dev == NULL) {
> > +             pr_err("Argment is invalid\n");
> > +             return count;
> > +     }
> > +
> > +     ndev = dev_get_drvdata(dev);
> > +     if (ndev == NULL) {
> > +             pr_err("Net device is null\n");
> > +             return count;
> > +     }
> > +
> > +     priv = netdev_priv(ndev);
> > +     if (priv == NULL) {
> > +             pr_err("geth_priv is null\n");
> > +             return count;
> > +     }
> > +
> > +     if (!netif_running(ndev)) {
> > +             pr_warn("eth is down!\n");
> > +             return count;
> > +     }
> > +
> > +     ret = kstrtou16(buf, 0, &value);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (value == 0) { /* normal mode */
> > +             /* clear mac loopback */
> > +             sunxi_mac_loopback(priv->base, 0);
> > +
> > +             /* clear phy loopback */
> > +             data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> > +             sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data & ~BMCR_LOOPBACK);
> > +     } else if (value == 1) { /* mac loopback test mode */
> > +             data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> > +             sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data & ~BMCR_LOOPBACK);
> > +
> > +             sunxi_mac_loopback(priv->base, 1);
> > +     } else if (value == 2) { /* phy loopback test mode */
> > +             sunxi_mac_loopback(priv->base, 0);
> > +
> > +             data = sunxi_mdio_read(priv->base, priv->phy_addr, MII_BMCR);
> > +             sunxi_mdio_write(priv->base, priv->phy_addr, MII_BMCR, data | BMCR_LOOPBACK);
> > +     } else {
> > +             pr_err("Undefined value (%d)\n", value);
> > +     }
> > +
> > +     return count;
> > +}
> > +static DEVICE_ATTR(loopback_test, 0664, loopback_test_show, loopback_test_store);
> > +
> > +static int geth_power_on(struct geth_priv *priv)
> > +{
> > +     int value;
> > +
> > +     value = readl(priv->base_phy);
> > +     if (priv->phy_ext == INT_PHY) {
> > +             value |= (1 << 15);
> > +             value &= ~(1 << 16);
> > +             value |= (3 << 17);
> > +     } else {
> > +             value &= ~(1 << 15);
> > +/*
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> > +                             continue;
> > +                     if (regulator_enable(priv->gmac_power[i]) != 0) {
> > +                             pr_err("gmac-power%d enable error\n", i);
> > +                             return -EINVAL;
> > +                     }
> > +             }
> > +*/
> > +     }
> > +
> > +     writel(value, priv->base_phy);
> > +
> > +     return 0;
> > +}
> > +
> > +static void geth_power_off(struct geth_priv *priv)
> > +{
> > +     int value;
> > +
> > +     if (priv->phy_ext == INT_PHY) {
> > +             value = readl(priv->base_phy);
> > +             value |= (1 << 16);
> > +             writel(value, priv->base_phy);
> > +     } else {
> > +/*
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> > +                             continue;
> > +                     regulator_disable(priv->gmac_power[i]);
> > +             }
> > +*/
> > +     }
> > +}
> > +
> > +/* PHY interface operations */
> > +static int geth_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
> > +{
> > +     struct net_device *ndev = bus->priv;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     return (int)sunxi_mdio_read(priv->base,  phyaddr, phyreg);
> > +}
> > +
> > +static int geth_mdio_write(struct mii_bus *bus, int phyaddr,
> > +                        int phyreg, u16 data)
> > +{
> > +     struct net_device *ndev = bus->priv;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     sunxi_mdio_write(priv->base, phyaddr, phyreg, data);
> > +
> > +     return 0;
> > +}
> > +
> > +static int geth_mdio_reset(struct mii_bus *bus)
> > +{
> > +     struct net_device *ndev = bus->priv;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     return sunxi_mdio_reset(priv->base);
> > +}
> > +
> > +static void geth_adjust_link(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phydev = ndev->phydev;
> > +     unsigned long flags;
> > +     int new_state = 0;
> > +
> > +     if (!phydev)
> > +             return;
> > +
> > +     spin_lock_irqsave(&priv->lock, flags);
> > +     if (phydev->link) {
> > +             /* Now we make sure that we can be in full duplex mode.
> > +              * If not, we operate in half-duplex mode.
> > +              */
> > +             if (phydev->duplex != priv->duplex) {
> > +                     new_state = 1;
> > +                     priv->duplex = phydev->duplex;
> > +             }
> > +             /* Flow Control operation */
> > +             if (phydev->pause)
> > +                     sunxi_flow_ctrl(priv->base, 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;
> > +             }
> > +
> > +             if (new_state)
> > +                     sunxi_set_link_mode(priv->base, priv->duplex, priv->speed);
> > +
> > +#ifdef LOOPBACK_DEBUG
> > +             phydev->state = PHY_FORCING;
> > +#endif
> > +
> > +     } 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);
> > +
> > +     spin_unlock_irqrestore(&priv->lock, flags);
> > +}
> > +
> > +static int geth_phy_init(struct net_device *ndev)
> > +{
> > +     int value;
> > +     struct mii_bus *new_bus;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phydev = ndev->phydev;
> > +
> > +     /* Fixup the phy interface type */
> > +     if (priv->phy_ext == INT_PHY) {
> > +             priv->phy_interface = PHY_INTERFACE_MODE_MII;
> > +     } else {
> > +             /* If config gpio to reset the phy device, we should reset it */
> > +             /*
> > +             if (gpio_is_valid(priv->phyrst)) {
> > +                     gpio_direction_output(priv->phyrst,
> > +                                     priv->rst_active_low);
> > +                     msleep(50);
> > +                     gpio_direction_output(priv->phyrst,
> > +                                     !priv->rst_active_low);
> > +                     msleep(50);
> > +             }
> > +             */
> > +     }
> > +
> > +     if (priv->is_suspend && phydev)
> > +             goto resume;
> > +
> > +     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 = &geth_mdio_read;
> > +     new_bus->write = &geth_mdio_write;
> > +     new_bus->reset = &geth_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;
> > +
> > +     if (mdiobus_register(new_bus)) {
> > +             pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
> > +             goto reg_fail;
> > +     }
> > +
> > +     priv->mii = new_bus;
> > +
> > +     {
> > +             int addr;
> > +
> > +             for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
> > +                     struct phy_device *phydev_tmp = mdiobus_get_phy(new_bus, addr);
> > +
> > +                     if (phydev_tmp && (phydev_tmp->phy_id != 0x00)) {
> > +                             phydev = phydev_tmp;
> > +                             priv->phy_addr = addr;
> > +                             break;
> > +                     }
> > +             }
> > +     }
> > +
> > +     if (!phydev) {
> > +             netdev_err(ndev, "No PHY found!\n");
> > +             goto err;
> > +     }
> > +
> > +     phydev->irq = PHY_POLL;
> > +
> > +     value = phy_connect_direct(ndev, phydev, &geth_adjust_link, priv->phy_interface);
> > +     if (value) {
> > +             netdev_err(ndev, "Could not attach to PHY\n");
> > +             goto err;
> > +     } else {
> > +             netdev_info(ndev, "%s: Type(%d) PHY ID %08x at %d IRQ %s (%s)\n",
> > +                         ndev->name, phydev->interface, phydev->phy_id,
> > +                         phydev->mdio.addr, "poll", dev_name(&phydev->mdio.dev));
> > +     }
> > +
> > +     //phydev->supported &= PHY_GBIT_FEATURES;
> > +     phydev->is_gigabit_capable = 1;
> > +     //phydev->advertising = phydev->supported;
> > +
> > +resume:
> > +     phy_write(phydev, MII_BMCR, BMCR_RESET);
> > +     while (BMCR_RESET & phy_read(phydev, MII_BMCR))
> > +             msleep(30);
> > +
> > +     value = phy_read(phydev, MII_BMCR);
> > +     phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN));
> > +
> > +     if (priv->phy_ext == INT_PHY) {
> > +             /* EPHY Initial */
> > +             phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */
> > +             phy_write(phydev, 0x12, 0x4824); /* Disable APS */
> > +             phy_write(phydev, 0x1f, 0x0200); /* switchto page 2 */
> > +             phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */
> > +             phy_write(phydev, 0x1f, 0x0600); /* switchto page 6 */
> > +             phy_write(phydev, 0x14, 0x708F); /* PHYAFE TX optimization */
> > +             phy_write(phydev, 0x19, 0x0000);
> > +             phy_write(phydev, 0x13, 0xf000); /* PHYAFE RX optimization */
> > +             phy_write(phydev, 0x15, 0x1530);
> > +             phy_write(phydev, 0x1f, 0x0800); /* switch to page 8 */
> > +             phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */
> > +             phy_write(phydev, 0x1f, 0x0100); /* switchto page 1 */
> > +             /* reg 0x17 bit3,set 0 to disable iEEE */
> > +             phy_write(phydev, 0x17, phy_read(phydev, 0x17) & (~(1<<3)));
> > +             phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */
> > +     }
> > +     if (priv->is_suspend)
> > +             phy_init_hw(phydev);
> > +
> > +     return 0;
> > +
> > +err:
> > +     mdiobus_unregister(new_bus);
> > +reg_fail:
> > +     mdiobus_free(new_bus);
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int geth_phy_release(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phydev = ndev->phydev;
> > +     int value = 0;
> > +
> > +     /* Stop and disconnect the PHY */
> > +     if (phydev)
> > +             phy_stop(phydev);
> > +
> > +     priv->link = PHY_DOWN;
> > +     priv->speed = 0;
> > +     priv->duplex = -1;
> > +
> > +     if (phydev) {
> > +             value = phy_read(phydev, MII_BMCR);
> > +             phy_write(phydev, MII_BMCR, (value | BMCR_PDOWN));
> > +     }
> > +
> > +     if (priv->is_suspend)
> > +             return 0;
> > +
> > +     if (phydev) {
> > +             phy_disconnect(phydev);
> > +             ndev->phydev = NULL;
> > +     }
> > +
> > +     if (priv->mii) {
> > +             mdiobus_unregister(priv->mii);
> > +             priv->mii->priv = NULL;
> > +             mdiobus_free(priv->mii);
> > +             priv->mii = NULL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static void geth_rx_refill(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct dma_desc *desc;
> > +     struct sk_buff *sk = NULL;
> > +     dma_addr_t paddr;
> > +
> > +     while (circ_space(priv->rx_clean, priv->rx_dirty, dma_desc_rx) > 0) {
> > +             int entry = priv->rx_clean;
> > +
> > +             /* Find the dirty's desc and clean it */
> > +             desc = priv->dma_rx + entry;
> > +
> > +             if (priv->rx_sk[entry] == NULL) {
> > +                     sk = netdev_alloc_skb_ip_align(ndev, priv->buf_sz);
> > +
> > +                     if (unlikely(sk == NULL))
> > +                             break;
> > +
> > +                     priv->rx_sk[entry] = sk;
> > +                     paddr = dma_map_single(priv->dev, sk->data,
> > +                                            priv->buf_sz, DMA_FROM_DEVICE);
> > +                     desc_buf_set(desc, paddr, priv->buf_sz);
> > +             }
> > +
> > +             /* sync memery */
> > +             wmb();
> > +             desc_set_own(desc);
> > +             priv->rx_clean = circ_inc(priv->rx_clean, dma_desc_rx);
> > +     }
> > +}
> > +
> > +/* geth_dma_desc_init - initialize the RX/TX descriptor list
> > + * @ndev: net device structure
> > + * Description: initialize the list for dma.
> > + */
> > +static int geth_dma_desc_init(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     unsigned int buf_sz;
> > +
> > +     priv->rx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_rx,
> > +                             GFP_KERNEL);
> > +     if (!priv->rx_sk)
> > +             return -ENOMEM;
> > +
> > +     priv->tx_sk = kzalloc(sizeof(struct sk_buff *) * dma_desc_tx,
> > +                             GFP_KERNEL);
> > +     if (!priv->tx_sk)
> > +             goto tx_sk_err;
> > +
> > +     /* Set the size of buffer depend on the MTU & max buf size */
> > +     buf_sz = MAX_BUF_SZ;
> > +
> > +     priv->dma_tx = dma_alloc_coherent(priv->dev,
> > +                                     dma_desc_tx *
> > +                                     sizeof(struct dma_desc),
> > +                                     &priv->dma_tx_phy,
> > +                                     GFP_KERNEL);
> > +     if (!priv->dma_tx)
> > +             goto dma_tx_err;
> > +
> > +     priv->dma_rx = dma_alloc_coherent(priv->dev,
> > +                                     dma_desc_rx *
> > +                                     sizeof(struct dma_desc),
> > +                                     &priv->dma_rx_phy,
> > +                                     GFP_KERNEL);
> > +     if (!priv->dma_rx)
> > +             goto dma_rx_err;
> > +
> > +     priv->buf_sz = buf_sz;
> > +
> > +     return 0;
> > +
> > +dma_rx_err:
> > +     dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc),
> > +                       priv->dma_tx, priv->dma_tx_phy);
> > +dma_tx_err:
> > +     kfree(priv->tx_sk);
> > +tx_sk_err:
> > +     kfree(priv->rx_sk);
> > +
> > +     return -ENOMEM;
> > +}
> > +
> > +static void geth_free_rx_sk(struct geth_priv *priv)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < dma_desc_rx; i++) {
> > +             if (priv->rx_sk[i] != NULL) {
> > +                     struct dma_desc *desc = priv->dma_rx + i;
> > +
> > +                     dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> > +                                      desc_buf_get_len(desc),
> > +                                      DMA_FROM_DEVICE);
> > +                     dev_kfree_skb_any(priv->rx_sk[i]);
> > +                     priv->rx_sk[i] = NULL;
> > +             }
> > +     }
> > +}
> > +
> > +static void geth_free_tx_sk(struct geth_priv *priv)
> > +{
> > +     int i;
> > +
> > +     for (i = 0; i < dma_desc_tx; i++) {
> > +             if (priv->tx_sk[i] != NULL) {
> > +                     struct dma_desc *desc = priv->dma_tx + i;
> > +
> > +                     if (desc_buf_get_addr(desc))
> > +                             dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> > +                                              desc_buf_get_len(desc),
> > +                                              DMA_TO_DEVICE);
> > +                     dev_kfree_skb_any(priv->tx_sk[i]);
> > +                     priv->tx_sk[i] = NULL;
> > +             }
> > +     }
> > +}
> > +
> > +static void geth_free_dma_desc(struct geth_priv *priv)
> > +{
> > +     /* Free the region of consistent memory previously allocated for the DMA */
> > +     dma_free_coherent(priv->dev, dma_desc_tx * sizeof(struct dma_desc),
> > +                       priv->dma_tx, priv->dma_tx_phy);
> > +     dma_free_coherent(priv->dev, dma_desc_rx * sizeof(struct dma_desc),
> > +                       priv->dma_rx, priv->dma_rx_phy);
> > +
> > +     kfree(priv->rx_sk);
> > +     kfree(priv->tx_sk);
> > +}
> > +
> > +#if IS_ENABLED(CONFIG_PM)
> > +/*
> > +static int geth_select_gpio_state(struct pinctrl *pctrl, char *name)
> > +{
> > +     int ret = 0;
> > +     struct pinctrl_state *pctrl_state = NULL;
> > +
> > +     pctrl_state = pinctrl_lookup_state(pctrl, name);
> > +     if (IS_ERR(pctrl_state)) {
> > +             pr_err("gmac pinctrl_lookup_state(%s) failed! return %p\n",
> > +                                             name, pctrl_state);
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = pinctrl_select_state(pctrl, pctrl_state);
> > +     if (ret < 0)
> > +             pr_err("gmac pinctrl_select_state(%s) failed! return %d\n",
> > +                                             name, ret);
> > +
> > +     return ret;
> > +}
> > +*/
> > +static int geth_suspend(struct device *dev)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     cancel_work_sync(&priv->eth_work);
> > +
> > +     if (!ndev || !netif_running(ndev))
> > +             return 0;
> > +
> > +     priv->is_suspend = true;
> > +
> > +     spin_lock(&priv->lock);
> > +     netif_device_detach(ndev);
> > +     spin_unlock(&priv->lock);
> > +
> > +     geth_stop(ndev);
> > +/*
> > +     if (priv->phy_ext == EXT_PHY)
> > +
> > +     geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_SLEEP);
> > +*/
> > +     return 0;
> > +}
> > +
> > +static void geth_resume_work(struct work_struct *work)
> > +{
> > +     struct geth_priv *priv = container_of(work, struct geth_priv, eth_work);
> > +     struct net_device *ndev = priv->ndev;
> > +     int ret = 0;
> > +
> > +     if (!netif_running(ndev))
> > +             return;
> > +/*
> > +     if (priv->phy_ext == EXT_PHY)
> > +             geth_select_gpio_state(priv->pinctrl, PINCTRL_STATE_DEFAULT);
> > +*/
> > +     spin_lock(&priv->lock);
> > +     netif_device_attach(ndev);
> > +     spin_unlock(&priv->lock);
> > +
> > +#if IS_ENABLED(CONFIG_SUNXI_EPHY)
> > +     if (!ephy_is_enable()) {
> > +             pr_info("[geth_resume] ephy is not enable, waiting...\n");
> > +             msleep(2000);
> > +             if (!ephy_is_enable()) {
> > +                     netdev_err(ndev, "Wait for ephy resume timeout.\n");
> > +                     return;
> > +             }
> > +     }
> > +#endif
> > +
> > +     ret = geth_open(ndev);
> > +     if (!ret)
> > +             priv->is_suspend = false;
> > +}
> > +
> > +static void geth_resume(struct device *dev)
> > +{
> > +     struct net_device *ndev = dev_get_drvdata(dev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     schedule_work(&priv->eth_work);
> > +}
> > +
> > +static int geth_freeze(struct device *dev)
> > +{
> > +     return 0;
> > +}
> > +
> > +static int geth_restore(struct device *dev)
> > +{
> > +     return 0;
> > +}
> > +
> > +static const struct dev_pm_ops geth_pm_ops = {
> > +     .complete = geth_resume,
> > +     .prepare = geth_suspend,
> > +     .suspend = NULL,
> > +     .resume = NULL,
> > +     .freeze = geth_freeze,
> > +     .restore = geth_restore,
> > +};
> > +#else
> > +static const struct dev_pm_ops geth_pm_ops;
> > +#endif /* CONFIG_PM */
> > +
> > +#define sunxi_get_soc_chipid(x) {}
> > +static void geth_chip_hwaddr(u8 *addr)
> > +{
> > +#define MD5_SIZE     16
> > +#define CHIP_SIZE    16
> > +
> > +     struct crypto_ahash *tfm;
> > +     struct ahash_request *req;
> > +     struct scatterlist sg;
> > +     u8 result[MD5_SIZE];
> > +     u8 chipid[CHIP_SIZE];
> > +     int i = 0;
> > +     int ret = -1;
> > +
> > +     memset(chipid, 0, sizeof(chipid));
> > +     memset(result, 0, sizeof(result));
> > +
> > +     sunxi_get_soc_chipid((u8 *)chipid);
> > +
> > +     tfm = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
> > +     if (IS_ERR(tfm)) {
> > +             pr_err("Failed to alloc md5\n");
> > +             return;
> > +     }
> > +
> > +     req = ahash_request_alloc(tfm, GFP_KERNEL);
> > +     if (!req)
> > +             goto out;
> > +
> > +     ahash_request_set_callback(req, 0, NULL, NULL);
> > +
> > +     ret = crypto_ahash_init(req);
> > +     if (ret) {
> > +             pr_err("crypto_ahash_init() failed\n");
> > +             goto out;
> > +     }
> > +
> > +     sg_init_one(&sg, chipid, sizeof(chipid));
> > +     ahash_request_set_crypt(req, &sg, result, sizeof(chipid));
> > +     ret = crypto_ahash_update(req);
> > +     if (ret) {
> > +             pr_err("crypto_ahash_update() failed for id\n");
> > +             goto out;
> > +     }
> > +
> > +     ret = crypto_ahash_final(req);
> > +     if (ret) {
> > +             pr_err("crypto_ahash_final() failed for result\n");
> > +             goto out;
> > +     }
> > +
> > +     ahash_request_free(req);
> > +
> > +     /* Choose md5 result's [0][2][4][6][8][10] byte as mac address */
> > +     for (i = 0; i < ETH_ALEN; i++)
> > +             addr[i] = result[2 * i];
> > +     addr[0] &= 0xfe; /* clear multicast bit */
> > +     addr[0] |= 0x02; /* set local assignment bit (IEEE802) */
> > +
> > +out:
> > +     crypto_free_ahash(tfm);
> > +}
> > +
> > +static void geth_check_addr(struct net_device *ndev, unsigned char *mac)
> > +{
> > +     int i;
> > +     char *p = mac;
> > +
> > +     if (!is_valid_ether_addr(ndev->dev_addr)) {
> > +             for (i = 0; i < ETH_ALEN; i++, p++)
> > +                     ndev->dev_addr[i] = simple_strtoul(p, &p, 16);
> > +
> > +             if (!is_valid_ether_addr(ndev->dev_addr))
> > +                     geth_chip_hwaddr(ndev->dev_addr);
> > +
> > +             if (!is_valid_ether_addr(ndev->dev_addr)) {
> > +                     random_ether_addr(ndev->dev_addr);
> > +                     pr_warn("%s: Use random mac address\n", ndev->name);
> > +             }
> > +     }
> > +}
> > +
> > +static int geth_clk_enable(struct geth_priv *priv)
> > +{
> > +     int ret;
> > +     phy_interface_t phy_interface = 0;
> > +     u32 clk_value;
> > +     /*u32 efuse_value;*/
> > +/*
> > +     ret = reset_control_deassert(priv->reset);
> > +     if (ret) {
> > +             pr_err("deassert gmac rst failed!\n");
> > +             return ret;
> > +     }
> > +
> > +     ret = clk_prepare_enable(priv->geth_clk);
> > +     if (ret) {
> > +             pr_err("try to enable geth_clk failed!\n");
> > +             goto assert_reset;
> > +     }
> > +
> > +     if (((priv->phy_ext == INT_PHY) || priv->use_ephy_clk)
> > +                     && !IS_ERR_OR_NULL(priv->ephy_clk)) {
> > +             ret = clk_prepare_enable(priv->ephy_clk);
> > +             if (ret) {
> > +                     pr_err("try to enable ephy_clk failed!\n");
> > +                     goto ephy_clk_disable;
> > +             }
> > +     }
> > +*/
> > +     phy_interface = priv->phy_interface;
> > +
> > +     clk_value = readl(priv->base_phy);
> > +     if (phy_interface == PHY_INTERFACE_MODE_RGMII)
> > +             clk_value |= 0x00000004;
> > +     else
> > +             clk_value &= (~0x00000004);
> > +
> > +     clk_value &= (~0x00002003);
> > +     if (phy_interface == PHY_INTERFACE_MODE_RGMII
> > +                     || phy_interface == PHY_INTERFACE_MODE_GMII)
> > +             clk_value |= 0x00000002;
> > +     else if (phy_interface == PHY_INTERFACE_MODE_RMII)
> > +             clk_value |= 0x00002001;
> > +
> > +     /*if (priv->phy_ext == INT_PHY) {
> > +             if (0 != sunxi_efuse_read(EFUSE_OEM_NAME, &efuse_value))
> > +                     pr_err("get PHY efuse fail!\n");
> > +             else
> > +#if IS_ENABLED(CONFIG_ARCH_SUN50IW2)
> > +                     clk_value |= (((efuse_value >> 24) & 0x0F) + 3) << 28;
> > +#else
> > +                     pr_warn("miss config come from efuse!\n");
> > +#endif
> > +     }*/
> > +
> > +     /* Adjust Tx/Rx clock delay */
> > +     clk_value &= ~(0x07 << 10);
> > +     clk_value |= ((priv->tx_delay & 0x07) << 10);
> > +     clk_value &= ~(0x1F << 5);
> > +     clk_value |= ((priv->rx_delay & 0x1F) << 5);
> > +
> > +     writel(clk_value, priv->base_phy);
> > +
> > +    return 0;
> > +/*
> > +ephy_clk_disable:
> > +    clk_disable_unprepare(priv->ephy_clk);
> > +assert_reset:
> > +    reset_control_assert(priv->reset);
> > +*/
> > +    return ret;
> > +}
> > +
> > +static void geth_clk_disable(struct geth_priv *priv)
> > +{
> > +/*
> > +     if (((priv->phy_ext == INT_PHY) || priv->use_ephy_clk)
> > +                     && !IS_ERR_OR_NULL(priv->ephy_clk))
> > +             clk_disable_unprepare(priv->ephy_clk);
> > +
> > +     clk_disable_unprepare(priv->geth_clk);
> > +    reset_control_assert(priv->reset);
> > +*/
> > +}
> > +
> > +static void geth_tx_err(struct geth_priv *priv)
> > +{
> > +     netif_stop_queue(priv->ndev);
> > +
> > +     sunxi_stop_tx(priv->base);
> > +
> > +     geth_free_tx_sk(priv);
> > +     memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc));
> > +     desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx);
> > +     priv->tx_dirty = 0;
> > +     priv->tx_clean = 0;
> > +     sunxi_start_tx(priv->base, priv->dma_tx_phy);
> > +
> > +     priv->ndev->stats.tx_errors++;
> > +     netif_wake_queue(priv->ndev);
> > +}
> > +
> > +static inline void geth_schedule(struct geth_priv *priv)
> > +{
> > +     if (likely(napi_schedule_prep(&priv->napi))) {
> > +             sunxi_int_disable(priv->base);
> > +             __napi_schedule(&priv->napi);
> > +     }
> > +}
> > +
> > +static irqreturn_t geth_interrupt(int irq, void *dev_id)
> > +{
> > +     struct net_device *ndev = (struct net_device *)dev_id;
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     int status;
> > +
> > +     if (unlikely(!ndev)) {
> > +             pr_err("%s: invalid ndev pointer\n", __func__);
> > +             return IRQ_NONE;
> > +     }
> > +
> > +     status = sunxi_int_status(priv->base, (void *)(&priv->xstats));
> > +
> > +     if (likely(status == handle_tx_rx))
> > +             geth_schedule(priv);
> > +     else if (unlikely(status == tx_hard_error_bump_tc))
> > +             netdev_info(ndev, "Do nothing for bump tc\n");
> > +     else if (unlikely(status == tx_hard_error))
> > +             geth_tx_err(priv);
> > +     else
> > +             netdev_info(ndev, "Do nothing.....\n");
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int geth_open(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     int ret = 0;
> > +
> > +     ret = geth_power_on(priv);
> > +     if (ret) {
> > +             netdev_err(ndev, "Power on is failed\n");
> > +             ret = -EINVAL;
> > +     }
> > +
> > +     ret = geth_clk_enable(priv);
> > +     if (ret) {
> > +             pr_err("%s: clk enable is failed\n", __func__);
> > +             ret = -EINVAL;
> > +     }
> > +
> > +     netif_carrier_off(ndev);
> > +
> > +     ret = geth_phy_init(ndev);
> > +     if (ret)
> > +             goto err;
> > +
> > +     ret = sunxi_mac_reset((void *)priv->base, &sunxi_udelay, 10000);
> > +     if (ret) {
> > +             netdev_err(ndev, "Initialize hardware error\n");
> > +             goto desc_err;
> > +     }
> > +
> > +     sunxi_mac_init(priv->base, txmode, rxmode);
> > +     sunxi_set_umac(priv->base, ndev->dev_addr, 0);
> > +
> > +     if (!priv->is_suspend) {
> > +             ret = geth_dma_desc_init(ndev);
> > +             if (ret) {
> > +                     ret = -EINVAL;
> > +                     goto desc_err;
> > +             }
> > +     }
> > +
> > +     memset(priv->dma_tx, 0, dma_desc_tx * sizeof(struct dma_desc));
> > +     memset(priv->dma_rx, 0, dma_desc_rx * sizeof(struct dma_desc));
> > +
> > +     desc_init_chain(priv->dma_rx, (unsigned long)priv->dma_rx_phy, dma_desc_rx);
> > +     desc_init_chain(priv->dma_tx, (unsigned long)priv->dma_tx_phy, dma_desc_tx);
> > +
> > +     priv->rx_clean = 0;
> > +     priv->rx_dirty = 0;
> > +     priv->tx_clean = 0;
> > +     priv->tx_dirty = 0;
> > +     geth_rx_refill(ndev);
> > +
> > +     /* Extra statistics */
> > +     memset(&priv->xstats, 0, sizeof(struct geth_extra_stats));
> > +
> > +     if (ndev->phydev)
> > +             phy_start(ndev->phydev);
> > +
> > +     sunxi_start_rx(priv->base, (unsigned long)((struct dma_desc *)
> > +                    priv->dma_rx_phy + priv->rx_dirty));
> > +     sunxi_start_tx(priv->base, (unsigned long)((struct dma_desc *)
> > +                    priv->dma_tx_phy + priv->tx_clean));
> > +
> > +     napi_enable(&priv->napi);
> > +     netif_start_queue(ndev);
> > +
> > +     /* Enable the Rx/Tx */
> > +     sunxi_mac_enable(priv->base);
> > +
> > +     return 0;
> > +
> > +desc_err:
> > +     geth_phy_release(ndev);
> > +err:
> > +     geth_clk_disable(priv);
> > +     if (priv->is_suspend)
> > +             napi_enable(&priv->napi);
> > +
> > +     geth_power_off(priv);
> > +
> > +     return ret;
> > +}
> > +
> > +static int geth_stop(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     netif_stop_queue(ndev);
> > +     napi_disable(&priv->napi);
> > +
> > +     netif_carrier_off(ndev);
> > +
> > +     /* Release PHY resources */
> > +     geth_phy_release(ndev);
> > +
> > +     /* Disable Rx/Tx */
> > +     sunxi_mac_disable(priv->base);
> > +
> > +     geth_clk_disable(priv);
> > +     geth_power_off(priv);
> > +
> > +     netif_tx_lock_bh(ndev);
> > +     /* Release the DMA TX/RX socket buffers */
> > +     geth_free_rx_sk(priv);
> > +     geth_free_tx_sk(priv);
> > +     netif_tx_unlock_bh(ndev);
> > +
> > +     /* Ensure that hareware have been stopped */
> > +     if (!priv->is_suspend)
> > +             geth_free_dma_desc(priv);
> > +
> > +     return 0;
> > +}
> > +
> > +static void geth_tx_complete(struct geth_priv *priv)
> > +{
> > +     unsigned int entry = 0;
> > +     struct sk_buff *skb = NULL;
> > +     struct dma_desc *desc = NULL;
> > +     int tx_stat;
> > +
> > +     spin_lock(&priv->tx_lock);
> > +     while (circ_cnt(priv->tx_dirty, priv->tx_clean, dma_desc_tx) > 0) {
> > +             entry = priv->tx_clean;
> > +             desc = priv->dma_tx + entry;
> > +
> > +             /* Check if the descriptor is owned by the DMA. */
> > +             if (desc_get_own(desc))
> > +                     break;
> > +
> > +             /* Verify tx error by looking at the last segment */
> > +             if (desc_get_tx_ls(desc)) {
> > +                     tx_stat = desc_get_tx_status(desc, (void *)(&priv->xstats));
> > +
> > +                     if (likely(!tx_stat))
> > +                             priv->ndev->stats.tx_packets++;
> > +                     else
> > +                             priv->ndev->stats.tx_errors++;
> > +             }
> > +
> > +             dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> > +                              desc_buf_get_len(desc), DMA_TO_DEVICE);
> > +
> > +             skb = priv->tx_sk[entry];
> > +             priv->tx_sk[entry] = NULL;
> > +             desc_init(desc);
> > +
> > +             /* Find next dirty desc */
> > +             priv->tx_clean = circ_inc(entry, dma_desc_tx);
> > +
> > +             if (unlikely(skb == NULL))
> > +                     continue;
> > +
> > +             dev_kfree_skb(skb);
> > +     }
> > +
> > +     if (unlikely(netif_queue_stopped(priv->ndev)) &&
> > +         circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) >
> > +         TX_THRESH) {
> > +             netif_wake_queue(priv->ndev);
> > +     }
> > +     spin_unlock(&priv->tx_lock);
> > +}
> > +
> > +static netdev_tx_t geth_xmit(struct sk_buff *skb, struct net_device *ndev)
> > +{
> > +     struct geth_priv  *priv = netdev_priv(ndev);
> > +     unsigned int entry;
> > +     struct dma_desc *desc, *first;
> > +     unsigned int len, tmp_len = 0;
> > +     int i, csum_insert;
> > +     int nfrags = skb_shinfo(skb)->nr_frags;
> > +     dma_addr_t paddr;
> > +
> > +     spin_lock(&priv->tx_lock);
> > +     if (unlikely(circ_space(priv->tx_dirty, priv->tx_clean,
> > +         dma_desc_tx) < (nfrags + 1))) {
> > +             if (!netif_queue_stopped(ndev)) {
> > +                     netdev_err(ndev, "%s: BUG! Tx Ring full when queue awake\n", __func__);
> > +                     netif_stop_queue(ndev);
> > +             }
> > +             spin_unlock(&priv->tx_lock);
> > +
> > +             return NETDEV_TX_BUSY;
> > +     }
> > +
> > +     csum_insert = (skb->ip_summed == CHECKSUM_PARTIAL);
> > +     entry = priv->tx_dirty;
> > +     first = priv->dma_tx + entry;
> > +     desc = priv->dma_tx + entry;
> > +
> > +     len = skb_headlen(skb);
> > +     priv->tx_sk[entry] = skb;
> > +
> > +#ifdef PKT_DEBUG
> > +     printk("======TX PKT DATA: ============\n");
> > +     /* dump the packet */
> > +     print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
> > +                    16, 1, skb->data, 64, true);
> > +#endif
> > +
> > +     /* Every desc max size is 2K */
> > +     while (len != 0) {
> > +             desc = priv->dma_tx + entry;
> > +             tmp_len = ((len > MAX_BUF_SZ) ?  MAX_BUF_SZ : len);
> > +
> > +             paddr = dma_map_single(priv->dev, skb->data, tmp_len, DMA_TO_DEVICE);
> > +             if (dma_mapping_error(priv->dev, paddr)) {
> > +                     dev_kfree_skb(skb);
> > +                     return -EIO;
> > +             }
> > +             desc_buf_set(desc, paddr, tmp_len);
> > +             /* Don't set the first's own bit, here */
> > +             if (first != desc) {
> > +                     priv->tx_sk[entry] = NULL;
> > +                     desc_set_own(desc);
> > +             }
> > +
> > +             entry = circ_inc(entry, dma_desc_tx);
> > +             len -= tmp_len;
> > +     }
> > +
> > +     for (i = 0; i < nfrags; i++) {
> > +             const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> > +
> > +             len = skb_frag_size(frag);
> > +             desc = priv->dma_tx + entry;
> > +             paddr = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE);
> > +             if (dma_mapping_error(priv->dev, paddr)) {
> > +                     dev_kfree_skb(skb);
> > +                     return -EIO;
> > +             }
> > +
> > +             desc_buf_set(desc, paddr, len);
> > +             desc_set_own(desc);
> > +             priv->tx_sk[entry] = NULL;
> > +             entry = circ_inc(entry, dma_desc_tx);
> > +     }
> > +
> > +     ndev->stats.tx_bytes += skb->len;
> > +     priv->tx_dirty = entry;
> > +     desc_tx_close(first, desc, csum_insert);
> > +
> > +     desc_set_own(first);
> > +     spin_unlock(&priv->tx_lock);
> > +
> > +     if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) <=
> > +                     (MAX_SKB_FRAGS + 1)) {
> > +             netif_stop_queue(ndev);
> > +             if (circ_space(priv->tx_dirty, priv->tx_clean, dma_desc_tx) >
> > +                             TX_THRESH)
> > +                     netif_wake_queue(ndev);
> > +     }
> > +
> > +#ifdef DEBUG
> > +     printk("=======TX Descriptor DMA: 0x%08llx\n", priv->dma_tx_phy);
> > +     printk("Tx pointor: dirty: %d, clean: %d\n", priv->tx_dirty, priv->tx_clean);
> > +     desc_print(priv->dma_tx, dma_desc_tx);
> > +#endif
> > +     sunxi_tx_poll(priv->base);
> > +     geth_tx_complete(priv);
> > +
> > +     return NETDEV_TX_OK;
> > +}
> > +
> > +static int geth_rx(struct geth_priv *priv, int limit)
> > +{
> > +     unsigned int rxcount = 0;
> > +     unsigned int entry;
> > +     struct dma_desc *desc;
> > +     struct sk_buff *skb;
> > +     int status;
> > +     int frame_len;
> > +
> > +     while (rxcount < limit) {
> > +             entry = priv->rx_dirty;
> > +             desc = priv->dma_rx + entry;
> > +
> > +             if (desc_get_own(desc))
> > +                     break;
> > +
> > +             rxcount++;
> > +             priv->rx_dirty = circ_inc(priv->rx_dirty, dma_desc_rx);
> > +
> > +             /* Get length & status from hardware */
> > +             frame_len = desc_rx_frame_len(desc);
> > +             status = desc_get_rx_status(desc, (void *)(&priv->xstats));
> > +
> > +             netdev_dbg(priv->ndev, "Rx frame size %d, status: %d\n",
> > +                        frame_len, status);
> > +
> > +             skb = priv->rx_sk[entry];
> > +             if (unlikely(!skb)) {
> > +                     netdev_err(priv->ndev, "Skb is null\n");
> > +                     priv->ndev->stats.rx_dropped++;
> > +                     break;
> > +             }
> > +
> > +#ifdef PKT_DEBUG
> > +             printk("======RX PKT DATA: ============\n");
> > +             /* dump the packet */
> > +             print_hex_dump(KERN_DEBUG, "skb->data: ", DUMP_PREFIX_NONE,
> > +                             16, 1, skb->data, 64, true);
> > +#endif
> > +
> > +             if (status == discard_frame) {
> > +                     netdev_dbg(priv->ndev, "Get error pkt\n");
> > +                     priv->ndev->stats.rx_errors++;
> > +                     continue;
> > +             }
> > +
> > +             if (unlikely(status != llc_snap))
> > +                     frame_len -= ETH_FCS_LEN;
> > +
> > +             priv->rx_sk[entry] = NULL;
> > +
> > +             skb_put(skb, frame_len);
> > +             dma_unmap_single(priv->dev, (u32)desc_buf_get_addr(desc),
> > +                              desc_buf_get_len(desc), DMA_FROM_DEVICE);
> > +
> > +             skb->protocol = eth_type_trans(skb, priv->ndev);
> > +
> > +             skb->ip_summed = CHECKSUM_UNNECESSARY;
> > +             napi_gro_receive(&priv->napi, skb);
> > +
> > +             priv->ndev->stats.rx_packets++;
> > +             priv->ndev->stats.rx_bytes += frame_len;
> > +     }
> > +
> > +#ifdef DEBUG
> > +     if (rxcount > 0) {
> > +             printk("======RX Descriptor DMA: 0x%08llx=\n", priv->dma_rx_phy);
> > +             printk("RX pointor: dirty: %d, clean: %d\n", priv->rx_dirty, priv->rx_clean);
> > +             desc_print(priv->dma_rx, dma_desc_rx);
> > +     }
> > +#endif
> > +     geth_rx_refill(priv->ndev);
> > +
> > +     return rxcount;
> > +}
> > +
> > +static int geth_poll(struct napi_struct *napi, int budget)
> > +{
> > +     struct geth_priv *priv = container_of(napi, struct geth_priv, napi);
> > +     int work_done = 0;
> > +
> > +     geth_tx_complete(priv);
> > +     work_done = geth_rx(priv, budget);
> > +
> > +     if (work_done < budget) {
> > +             napi_complete(napi);
> > +             sunxi_int_enable(priv->base);
> > +     }
> > +
> > +     return work_done;
> > +}
> > +
> > +static int geth_change_mtu(struct net_device *ndev, int new_mtu)
> > +{
> > +     int max_mtu;
> > +
> > +     if (netif_running(ndev)) {
> > +             pr_err("%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 < 46) || (new_mtu > max_mtu)) {
> > +             pr_err("%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 geth_fix_features(struct net_device *ndev,
> > +                                        netdev_features_t features)
> > +{
> > +     return features;
> > +}
> > +
> > +static void geth_set_rx_mode(struct net_device *ndev)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     unsigned int value = 0;
> > +
> > +     pr_debug("%s: # mcasts %d, # unicast %d\n",
> > +              __func__, netdev_mc_count(ndev), netdev_uc_count(ndev));
> > +
> > +     spin_lock(&priv->lock);
> > +     if (ndev->flags & IFF_PROMISC) {
> > +             value = GETH_FRAME_FILTER_PR;
> > +     } else if ((netdev_mc_count(ndev) > HASH_TABLE_SIZE) ||
> > +                (ndev->flags & IFF_ALLMULTI)) {
> > +             value = GETH_FRAME_FILTER_PM;   /* pass all multi */
> > +             sunxi_hash_filter(priv->base, ~0UL, ~0UL);
> > +     } else if (!netdev_mc_empty(ndev)) {
> > +             u32 mc_filter[2];
> > +             struct netdev_hw_addr *ha;
> > +
> > +             /* Hash filter for multicast */
> > +             value = GETH_FRAME_FILTER_HMC;
> > +
> > +             memset(mc_filter, 0, sizeof(mc_filter));
> > +             netdev_for_each_mc_addr(ha, ndev) {
> > +                     /* The upper 6 bits of the calculated CRC are used to
> > +                      *  index the contens of the hash table
> > +                      */
> > +                     int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26;
> > +                     /* The most significant bit determines the register to
> > +                      * use (H/L) while the other 5 bits determine the bit
> > +                      * within the register.
> > +                      */
> > +                     mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
> > +             }
> > +             sunxi_hash_filter(priv->base, mc_filter[0], mc_filter[1]);
> > +     }
> > +
> > +     /* Handle multiple unicast addresses (perfect filtering)*/
> > +     if (netdev_uc_count(ndev) > 16) {
> > +             /* Switch to promiscuous mode is more than 8 addrs are required */
> > +             value |= GETH_FRAME_FILTER_PR;
> > +     } else {
> > +             int reg = 1;
> > +             struct netdev_hw_addr *ha;
> > +
> > +             netdev_for_each_uc_addr(ha, ndev) {
> > +                     sunxi_set_umac(priv->base, ha->addr, reg);
> > +                     reg++;
> > +             }
> > +     }
> > +
> > +#ifdef FRAME_FILTER_DEBUG
> > +     /* Enable Receive all mode (to debug filtering_fail errors) */
> > +     value |= GETH_FRAME_FILTER_RA;
> > +#endif
> > +     sunxi_set_filter(priv->base, value);
> > +     spin_unlock(&priv->lock);
> > +}
> > +
> > +static void geth_tx_timeout(struct net_device *ndev, unsigned int txqueue)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     geth_tx_err(priv);
> > +}
> > +
> > +static int geth_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
> > +{
> > +     if (!netif_running(ndev))
> > +             return -EINVAL;
> > +
> > +     if (!ndev->phydev)
> > +             return -EINVAL;
> > +
> > +     return phy_mii_ioctl(ndev->phydev, rq, cmd);
> > +}
> > +
> > +/* Configuration changes (passed on by ifconfig) */
> > +static int geth_config(struct net_device *ndev, struct ifmap *map)
> > +{
> > +     if (ndev->flags & IFF_UP)       /* can't act on a running interface */
> > +             return -EBUSY;
> > +
> > +     /* Don't allow changing the I/O address */
> > +     if (map->base_addr != ndev->base_addr) {
> > +             pr_warn("%s: can't change I/O address\n", ndev->name);
> > +             return -EOPNOTSUPP;
> > +     }
> > +
> > +     /* Don't allow changing the IRQ */
> > +     if (map->irq != ndev->irq) {
> > +             pr_warn("%s: can't change IRQ number %d\n", ndev->name, ndev->irq);
> > +             return -EOPNOTSUPP;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int geth_set_mac_address(struct net_device *ndev, void *p)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct sockaddr *addr = p;
> > +
> > +     if (!is_valid_ether_addr(addr->sa_data))
> > +             return -EADDRNOTAVAIL;
> > +
> > +     memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
> > +     sunxi_set_umac(priv->base, ndev->dev_addr, 0);
> > +
> > +     return 0;
> > +}
> > +
> > +int geth_set_features(struct net_device *ndev, netdev_features_t features)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     if (features & NETIF_F_LOOPBACK && netif_running(ndev))
> > +             sunxi_mac_loopback(priv->base, 1);
> > +     else
> > +             sunxi_mac_loopback(priv->base, 0);
> > +
> > +     return 0;
> > +}
> > +
> > +#if IS_ENABLED(CONFIG_NET_POLL_CONTROLLER)
> > +/* Polling receive - used by NETCONSOLE and other diagnostic tools
> > + * to allow network I/O with interrupts disabled.
> > + */
> > +static void geth_poll_controller(struct net_device *dev)
> > +{
> > +     disable_irq(dev->irq);
> > +     geth_interrupt(dev->irq, dev);
> > +     enable_irq(dev->irq);
> > +}
> > +#endif
> > +
> > +static const struct net_device_ops geth_netdev_ops = {
> > +     .ndo_init = NULL,
> > +     .ndo_open = geth_open,
> > +     .ndo_start_xmit = geth_xmit,
> > +     .ndo_stop = geth_stop,
> > +     .ndo_change_mtu = geth_change_mtu,
> > +     .ndo_fix_features = geth_fix_features,
> > +     .ndo_set_rx_mode = geth_set_rx_mode,
> > +     .ndo_tx_timeout = geth_tx_timeout,
> > +     .ndo_do_ioctl = geth_ioctl,
> > +     .ndo_set_config = geth_config,
> > +#if IS_ENABLED(CONFIG_NET_POLL_CONTROLLER)
> > +     .ndo_poll_controller = geth_poll_controller,
> > +#endif
> > +     .ndo_set_mac_address = geth_set_mac_address,
> > +     .ndo_set_features = geth_set_features,
> > +};
> > +
> > +static int geth_check_if_running(struct net_device *ndev)
> > +{
> > +     if (!netif_running(ndev))
> > +             return -EBUSY;
> > +     return 0;
> > +}
> > +
> > +static int geth_get_sset_count(struct net_device *netdev, int sset)
> > +{
> > +     int len;
> > +
> > +     switch (sset) {
> > +     case ETH_SS_STATS:
> > +             len = 0;
> > +             return len;
> > +     default:
> > +             return -EOPNOTSUPP;
> > +     }
> > +}
> > +
> > +/*static int geth_ethtool_getsettings(struct net_device *ndev,
> > +                                 struct ethtool_cmd *cmd)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phy = ndev->phydev;
> > +     int rc;
> > +
> > +     if (phy == NULL) {
> > +             netdev_err(ndev, "%s: %s: PHY is not registered\n",
> > +                    __func__, ndev->name);
> > +             return -ENODEV;
> > +     }
> > +
> > +     if (!netif_running(ndev)) {
> > +             pr_err("%s: interface is disabled: we cannot track "
> > +                    "link speed / duplex setting\n", ndev->name);
> > +             return -EBUSY;
> > +     }
> > +
> > +     cmd->transceiver = XCVR_INTERNAL;
> > +     spin_lock_irq(&priv->lock);
> > +     //rc = phy_ethtool_gset(phy, cmd);
> > +     spin_unlock_irq(&priv->lock);
> > +
> > +     return rc;
> > +}
> > +
> > +static int geth_ethtool_setsettings(struct net_device *ndev,
> > +                                 struct ethtool_cmd *cmd)
> > +{
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct phy_device *phy = ndev->phydev;
> > +     int rc;
> > +
> > +     spin_lock(&priv->lock);
> > +     rc = phy_ethtool_sset(phy, cmd);
> > +     spin_unlock(&priv->lock);
> > +
> > +     return rc;
> > +}*/
> > +
> > +static void geth_ethtool_getdrvinfo(struct net_device *ndev,
> > +                                 struct ethtool_drvinfo *info)
> > +{
> > +     strlcpy(info->driver, "sunxi_geth", sizeof(info->driver));
> > +
> > +#define DRV_MODULE_VERSION "SUNXI Gbgit driver V1.1"
> > +
> > +     strcpy(info->version, DRV_MODULE_VERSION);
> > +     info->fw_version[0] = '\0';
> > +}
> > +
> > +static const struct ethtool_ops geth_ethtool_ops = {
> > +     .begin = geth_check_if_running,
> > +     //.get_settings = geth_ethtool_getsettings,
> > +     //.set_settings = geth_ethtool_setsettings,
> > +     .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 = geth_get_sset_count,
> > +     .get_drvinfo = geth_ethtool_getdrvinfo,
> > +};
> > +
> > +/* config hardware resource */
> > +static int geth_hw_init(struct platform_device *pdev)
> > +{
> > +     struct net_device *ndev = platform_get_drvdata(pdev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +     struct device_node *np = pdev->dev.of_node;
> > +     int ret = 0;
> > +     struct resource *res;
> > +     u32 value;
> > +//   struct gpio_config cfg;
> > +//   const char *gmac_power;
> > +//   char power[20];
> > +//   int i;
> > +
> > +#if 1
> > +     priv->phy_ext = EXT_PHY;
> > +#else
> > +     priv->phy_ext = INT_PHY;
> > +#endif
> > +
> > +     /* config memery resource */
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +     if (unlikely(!res)) {
> > +             pr_err("%s: ERROR: get gmac memory failed", __func__);
> > +             return -ENODEV;
> > +     }
> > +
> > +     priv->base = devm_ioremap_resource(&pdev->dev, res);
> > +     if (!priv->base) {
> > +             pr_err("%s: ERROR: gmac memory mapping failed", __func__);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > +     if (unlikely(!res)) {
> > +             pr_err("%s: ERROR: get phy memory failed", __func__);
> > +             ret = -ENODEV;
> > +             goto mem_err;
> > +     }
> > +
> > +     priv->base_phy = devm_ioremap_resource(&pdev->dev, res);
> > +     if (unlikely(!priv->base_phy)) {
> > +             pr_err("%s: ERROR: phy memory mapping failed", __func__);
> > +             ret = -ENOMEM;
> > +             goto mem_err;
> > +     }
> > +
> > +     /* config IRQ */
> > +     ndev->irq = platform_get_irq_byname(pdev, "gmacirq");
> > +     if (ndev->irq == -ENXIO) {
> > +             pr_err("%s: ERROR: MAC IRQ not found\n", __func__);
> > +             ret = -ENXIO;
> > +             goto irq_err;
> > +     }
> > +
> > +     ret = request_irq(ndev->irq, geth_interrupt, IRQF_SHARED, dev_name(&pdev->dev), ndev);
> > +     if (unlikely(ret < 0)) {
> > +             pr_err("Could not request irq %d, error: %d\n", ndev->irq, ret);
> > +             goto irq_err;
> > +     }
> > +
> > +     /* get gmac rst handle */
> > +/*
> > +     priv->reset = devm_reset_control_get(&pdev->dev, NULL);
> > +     if (IS_ERR(priv->reset)) {
> > +             pr_err("%s: Get gmac reset control failed!\n", __func__);
> > +             return PTR_ERR(priv->reset);
> > +     }
> > +*/
> > +     /* config clock */
> > +/*
> > +     priv->geth_clk = of_clk_get_by_name(np, "gmac");
> > +     if (unlikely(!priv->geth_clk || IS_ERR(priv->geth_clk))) {
> > +             pr_err("Get gmac clock failed!\n");
> > +             ret = -EINVAL;
> > +             goto clk_err;
> > +     }
> > +
> > +     if (INT_PHY == priv->phy_ext) {
> > +             priv->ephy_clk = of_clk_get_by_name(np, "ephy");
> > +             if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) {
> > +                     pr_err("Get ephy clock failed!\n");
> > +                     ret = -EINVAL;
> > +                     goto clk_err;
> > +             }
> > +     } else {
> > +             if (!of_property_read_u32(np, "use-ephy25m", &(priv->use_ephy_clk))
> > +                             && priv->use_ephy_clk) {
> > +                     priv->ephy_clk = of_clk_get_by_name(np, "ephy");
> > +                     if (unlikely(IS_ERR_OR_NULL(priv->ephy_clk))) {
> > +                             pr_err("Get ephy clk failed!\n");
> > +                             ret = -EINVAL;
> > +                             goto clk_err;
> > +                     }
> > +             }
> > +     }
> > +*/
> > +     /* config power regulator */
> > +/*
> > +     if (EXT_PHY == priv->phy_ext) {
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     snprintf(power, 15, "gmac-power%d", i);
> > +                     ret = of_property_read_string(np, power, &gmac_power);
> > +                     if (ret) {
> > +                             priv->gmac_power[i] = NULL;
> > +                             pr_info("gmac-power%d: NULL\n", i);
> > +                             continue;
> > +                     }
> > +                     priv->gmac_power[i] = regulator_get(NULL, gmac_power);
> > +                     if (IS_ERR(priv->gmac_power[i])) {
> > +                             pr_err("gmac-power%d get error!\n", i);
> > +                             ret = -EINVAL;
> > +                             goto clk_err;
> > +                     }
> > +             }
> > +     }
> > +*/
> > +     /* config other parameters */
> > +     of_get_phy_mode(np, &(priv->phy_interface));
> > +     if (priv->phy_interface != PHY_INTERFACE_MODE_MII &&
> > +         priv->phy_interface != PHY_INTERFACE_MODE_RGMII &&
> > +         priv->phy_interface != PHY_INTERFACE_MODE_RMII) {
> > +             pr_err("Not support phy type!\n");
> > +             priv->phy_interface = PHY_INTERFACE_MODE_MII;
> > +     }
> > +
> > +     if (!of_property_read_u32(np, "tx-delay", &value))
> > +             priv->tx_delay = value;
> > +
> > +     if (!of_property_read_u32(np, "rx-delay", &value))
> > +             priv->rx_delay = value;
> > +
> > +     /* config pinctrl */
> > +/*
> > +     if (EXT_PHY == priv->phy_ext) {
> > +             priv->phyrst = of_get_named_gpio_flags(np, "phy-rst", 0, (enum of_gpio_flags *)&cfg);
> > +             priv->rst_active_low = (cfg.data == OF_GPIO_ACTIVE_LOW) ? 1 : 0;
> > +
> > +             if (gpio_is_valid(priv->phyrst)) {
> > +                     if (gpio_request(priv->phyrst, "phy-rst") < 0) {
> > +                             pr_err("gmac gpio request fail!\n");
> > +                             ret = -EINVAL;
> > +                             goto pin_err;
> > +                     }
> > +             }
> > +
> > +             priv->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> > +             if (IS_ERR_OR_NULL(priv->pinctrl)) {
> > +                     pr_err("gmac pinctrl error!\n");
> > +                     priv->pinctrl = NULL;
> > +                     ret = -EINVAL;
> > +                     goto pin_err;
> > +             }
> > +     }
> > +*/
> > +     return 0;
> > +
> > +//pin_err:
> > +/*
> > +     if (EXT_PHY == priv->phy_ext) {
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> > +                             continue;
> > +                     regulator_put(priv->gmac_power[i]);
> > +             }
> > +     }
> > +*/
> > +//clk_err:
> > +//   free_irq(ndev->irq, ndev);
> > +irq_err:
> > +     devm_iounmap(&pdev->dev, priv->base_phy);
> > +mem_err:
> > +     devm_iounmap(&pdev->dev, priv->base);
> > +
> > +     return ret;
> > +}
> > +
> > +static void geth_hw_release(struct platform_device *pdev)
> > +{
> > +     struct net_device *ndev = platform_get_drvdata(pdev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     devm_iounmap(&pdev->dev, (priv->base_phy));
> > +     devm_iounmap(&pdev->dev, priv->base);
> > +     free_irq(ndev->irq, ndev);
> > +/*
> > +     if (priv->geth_clk)
> > +             clk_put(priv->geth_clk);
> > +
> > +     if (EXT_PHY == priv->phy_ext) {
> > +             for (i = 0; i < POWER_CHAN_NUM; i++) {
> > +                     if (IS_ERR_OR_NULL(priv->gmac_power[i]))
> > +                             continue;
> > +                     regulator_put(priv->gmac_power[i]);
> > +             }
> > +
> > +             if (!IS_ERR_OR_NULL(priv->pinctrl))
> > +                     devm_pinctrl_put(priv->pinctrl);
> > +
> > +             if (gpio_is_valid(priv->phyrst))
> > +                     gpio_free(priv->phyrst);
> > +     }
> > +
> > +     if (!IS_ERR_OR_NULL(priv->ephy_clk))
> > +             clk_put(priv->ephy_clk);
> > +*/
> > +}
> > +
> > +/**
> > + * geth_probe
> > + * @pdev: platform device pointer
> > + * Description: the driver is initialized through platform_device.
> > + */
> > +static int geth_probe(struct platform_device *pdev)
> > +{
> > +     int ret = 0;
> > +     struct net_device *ndev = NULL;
> > +     struct geth_priv *priv;
> > +
> > +     pr_info("sunxi gmac driver's version: %s\n", SUNXI_GMAC_VERSION);
> > +
> > +#if IS_ENABLED(CONFIG_OF)
> > +     pdev->dev.dma_mask = &geth_dma_mask;
> > +     pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> > +#endif
> > +
> > +     ndev = alloc_etherdev(sizeof(struct geth_priv));
> > +     if (!ndev) {
> > +             dev_err(&pdev->dev, "could not allocate device.\n");
> > +             return -ENOMEM;
> > +     }
> > +     SET_NETDEV_DEV(ndev, &pdev->dev);
> > +
> > +     priv = netdev_priv(ndev);
> > +     platform_set_drvdata(pdev, ndev);
> > +
> > +     /* Must set private data to pdev, before call it */
> > +     ret = geth_hw_init(pdev);
> > +     if (0 != ret) {
> > +             pr_err("geth_hw_init fail!\n");
> > +             goto hw_err;
> > +     }
> > +
> > +     /* setup the netdevice, fill the field of netdevice */
> > +     ether_setup(ndev);
> > +     ndev->netdev_ops = &geth_netdev_ops;
> > +     netdev_set_default_ethtool_ops(ndev, &geth_ethtool_ops);
> > +     ndev->base_addr = (unsigned long)priv->base;
> > +
> > +     priv->ndev = ndev;
> > +     priv->dev = &pdev->dev;
> > +
> > +     /* TODO: support the VLAN frames */
> > +     ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | 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(watchdog);
> > +
> > +     netif_napi_add(ndev, &priv->napi, geth_poll,  BUDGET);
> > +
> > +     spin_lock_init(&priv->lock);
> > +     spin_lock_init(&priv->tx_lock);
> > +
> > +     /* The last val is mdc clock ratio */
> > +     sunxi_geth_register((void *)ndev->base_addr, HW_VERSION, 0x03);
> > +
> > +     ret = register_netdev(ndev);
> > +     if (ret) {
> > +             netif_napi_del(&priv->napi);
> > +             pr_err("Error: Register %s failed\n", ndev->name);
> > +             goto reg_err;
> > +     }
> > +
> > +     /* Before open the device, the mac address should be set */
> > +     geth_check_addr(ndev, mac_str);
> > +
> > +#ifdef CONFIG_GETH_ATTRS
> > +     geth_create_attrs(ndev);
> > +#endif
> > +     device_create_file(&pdev->dev, &dev_attr_gphy_test);
> > +     device_create_file(&pdev->dev, &dev_attr_mii_reg);
> > +     device_create_file(&pdev->dev, &dev_attr_loopback_test);
> > +     device_create_file(&pdev->dev, &dev_attr_extra_tx_stats);
> > +     device_create_file(&pdev->dev, &dev_attr_extra_rx_stats);
> > +
> > +     device_enable_async_suspend(&pdev->dev);
> > +
> > +#if IS_ENABLED(CONFIG_PM)
> > +     INIT_WORK(&priv->eth_work, geth_resume_work);
> > +#endif
> > +
> > +     return 0;
> > +
> > +reg_err:
> > +     geth_hw_release(pdev);
> > +hw_err:
> > +     platform_set_drvdata(pdev, NULL);
> > +     free_netdev(ndev);
> > +
> > +     return ret;
> > +}
> > +
> > +static int geth_remove(struct platform_device *pdev)
> > +{
> > +     struct net_device *ndev = platform_get_drvdata(pdev);
> > +     struct geth_priv *priv = netdev_priv(ndev);
> > +
> > +     device_remove_file(&pdev->dev, &dev_attr_gphy_test);
> > +     device_remove_file(&pdev->dev, &dev_attr_mii_reg);
> > +     device_remove_file(&pdev->dev, &dev_attr_loopback_test);
> > +     device_remove_file(&pdev->dev, &dev_attr_extra_tx_stats);
> > +     device_remove_file(&pdev->dev, &dev_attr_extra_rx_stats);
> > +
> > +     netif_napi_del(&priv->napi);
> > +     unregister_netdev(ndev);
> > +     geth_hw_release(pdev);
> > +     platform_set_drvdata(pdev, NULL);
> > +     free_netdev(ndev);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct of_device_id geth_of_match[] = {
> > +     {.compatible = "allwinner,sunxi-gmac",},
> > +     {},
> > +};
> > +MODULE_DEVICE_TABLE(of, geth_of_match);
> > +
> > +static struct platform_driver geth_driver = {
> > +     .probe  = geth_probe,
> > +     .remove = geth_remove,
> > +     .driver = {
> > +                .name = "sunxi-gmac",
> > +                .owner = THIS_MODULE,
> > +                .pm = &geth_pm_ops,
> > +                .of_match_table = geth_of_match,
> > +     },
> > +};
> > +module_platform_driver(geth_driver);
> > +
> > +#ifdef MODULE
> > +static int __init set_mac_addr(char *str)
> > +{
> > +     char *p = str;
> > +
> > +     if (str && strlen(str))
> > +             memcpy(mac_str, p, 18);
> > +
> > +     return 0;
> > +}
> > +__setup("mac_addr=", set_mac_addr);
> > +#endif
> > +
> > +MODULE_DESCRIPTION("Allwinner Gigabit Ethernet driver");
> > +MODULE_AUTHOR("fuzhaoke <fuzhaoke at allwinnertech.com>");
> > +MODULE_LICENSE("Dual BSD/GPL");
> > diff --git a/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> > new file mode 100644
> > index 00000000..ea7a6f15
> > --- /dev/null
> > +++ b/drivers/net/ethernet/allwinnertmp/sunxi-gmac.h
> > @@ -0,0 +1,258 @@
> > +/*
> > + * linux/drivers/net/ethernet/allwinner/sunxi_gmac.h
> > + *
> > + * Copyright © 2016-2018, fuzhaoke
> > + *           Author: fuzhaoke <fuzhaoke at allwinnertech.com>
> > + *
> > + * This file is provided under a dual BSD/GPL license.  When using or
> > + * redistributing this file, you may do so under either license.
> > + *
> > + * 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.
> > + */
> > +#ifndef __SUNXI_GETH_H__
> > +#define __SUNXI_GETH_H__
> > +
> > +#include <linux/etherdevice.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/phy.h>
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +
> > +/* GETH_FRAME_FILTER  register value */
> > +#define GETH_FRAME_FILTER_PR 0x00000001      /* Promiscuous Mode */
> > +#define GETH_FRAME_FILTER_HUC        0x00000002      /* Hash Unicast */
> > +#define GETH_FRAME_FILTER_HMC        0x00000004      /* Hash Multicast */
> > +#define GETH_FRAME_FILTER_DAIF       0x00000008      /* DA Inverse Filtering */
> > +#define GETH_FRAME_FILTER_PM 0x00000010      /* Pass all multicast */
> > +#define GETH_FRAME_FILTER_DBF        0x00000020      /* Disable Broadcast frames */
> > +#define GETH_FRAME_FILTER_SAIF       0x00000100      /* Inverse Filtering */
> > +#define GETH_FRAME_FILTER_SAF        0x00000200      /* Source Address Filter */
> > +#define GETH_FRAME_FILTER_HPF        0x00000400      /* Hash or perfect Filter */
> > +#define GETH_FRAME_FILTER_RA 0x80000000      /* Receive all mode */
> > +
> > +/* Default tx descriptor */
> > +#define TX_SINGLE_DESC0              0x80000000
> > +#define TX_SINGLE_DESC1              0x63000000
> > +
> > +/* Default rx descriptor */
> > +#define RX_SINGLE_DESC0              0x80000000
> > +#define RX_SINGLE_DESC1              0x83000000
> > +
> > +typedef union {
> > +     struct {
> > +             /* TDES0 */
> > +             unsigned int deferred:1;        /* Deferred bit (only half-duplex) */
> > +             unsigned int under_err:1;       /* Underflow error */
> > +             unsigned int ex_deferral:1;     /* Excessive deferral */
> > +             unsigned int coll_cnt:4;        /* Collision count */
> > +             unsigned int vlan_tag:1;        /* VLAN Frame */
> > +             unsigned int ex_coll:1;         /* Excessive collision */
> > +             unsigned int late_coll:1;       /* Late collision */
> > +             unsigned int no_carr:1;         /* No carrier */
> > +             unsigned int loss_carr:1;       /* Loss of collision */
> > +             unsigned int ipdat_err:1;       /* IP payload error */
> > +             unsigned int frm_flu:1;         /* Frame flushed */
> > +             unsigned int jab_timeout:1;     /* Jabber timeout */
> > +             unsigned int err_sum:1;         /* Error summary */
> > +             unsigned int iphead_err:1;      /* IP header error */
> > +             unsigned int ttss:1;            /* Transmit time stamp status */
> > +             unsigned int reserved0:13;
> > +             unsigned int own:1;             /* Own bit. CPU:0, DMA:1 */
> > +     } tx;
> > +
> > +     /* bits 5 7 0 | Frame status
> > +      * ----------------------------------------------------------
> > +      *      0 0 0 | IEEE 802.3 Type frame (length < 1536 octects)
> > +      *      1 0 0 | IPv4/6 No CSUM errorS.
> > +      *      1 0 1 | IPv4/6 CSUM PAYLOAD error
> > +      *      1 1 0 | IPv4/6 CSUM IP HR error
> > +      *      1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS
> > +      *      0 0 1 | IPv4/6 unsupported IP PAYLOAD
> > +      *      0 1 1 | COE bypassed.. no IPv4/6 frame
> > +      *      0 1 0 | Reserved.
> > +      */
> > +     struct {
> > +             /* RDES0 */
> > +             unsigned int chsum_err:1;       /* Payload checksum error */
> > +             unsigned int crc_err:1;         /* CRC error */
> > +             unsigned int dribbling:1;       /* Dribble bit error */
> > +             unsigned int mii_err:1;         /* Received error (bit3) */
> > +             unsigned int recv_wt:1;         /* Received watchdog timeout */
> > +             unsigned int frm_type:1;        /* Frame type */
> > +             unsigned int late_coll:1;       /* Late Collision */
> > +             unsigned int ipch_err:1;        /* IPv header checksum error (bit7) */
> > +             unsigned int last_desc:1;       /* Laset descriptor */
> > +             unsigned int first_desc:1;      /* First descriptor */
> > +             unsigned int vlan_tag:1;        /* VLAN Tag */
> > +             unsigned int over_err:1;        /* Overflow error (bit11) */
> > +             unsigned int len_err:1;         /* Length error */
> > +             unsigned int sou_filter:1;      /* Source address filter fail */
> > +             unsigned int desc_err:1;        /* Descriptor error */
> > +             unsigned int err_sum:1;         /* Error summary (bit15) */
> > +             unsigned int frm_len:14;        /* Frame length */
> > +             unsigned int des_filter:1;      /* Destination address filter fail */
> > +             unsigned int own:1;             /* Own bit. CPU:0, DMA:1 */
> > +     #define RX_PKT_OK               0x7FFFB77C
> > +     #define RX_LEN                  0x3FFF0000
> > +     } rx;
> > +
> > +     unsigned int all;
> > +} desc0_u;
> > +
> > +typedef union {
> > +     struct {
> > +             /* TDES1 */
> > +             unsigned int buf1_size:11;      /* Transmit buffer1 size */
> > +             unsigned int buf2_size:11;      /* Transmit buffer2 size */
> > +             unsigned int ttse:1;            /* Transmit time stamp enable */
> > +             unsigned int dis_pad:1;         /* Disable pad (bit23) */
> > +             unsigned int adr_chain:1;       /* Second address chained */
> > +             unsigned int end_ring:1;        /* Transmit end of ring */
> > +             unsigned int crc_dis:1;         /* Disable CRC */
> > +             unsigned int cic:2;             /* Checksum insertion control (bit27:28) */
> > +             unsigned int first_sg:1;        /* First Segment */
> > +             unsigned int last_seg:1;        /* Last Segment */
> > +             unsigned int interrupt:1;       /* Interrupt on completion */
> > +     } tx;
> > +
> > +     struct {
> > +             /* RDES1 */
> > +             unsigned int buf1_size:11;      /* Received buffer1 size */
> > +             unsigned int buf2_size:11;      /* Received buffer2 size */
> > +             unsigned int reserved1:2;
> > +             unsigned int adr_chain:1;       /* Second address chained */
> > +             unsigned int end_ring:1;                /* Received end of ring */
> > +             unsigned int reserved2:5;
> > +             unsigned int dis_ic:1;          /* Disable interrupt on completion */
> > +     } rx;
> > +
> > +     unsigned int all;
> > +} desc1_u;
> > +
> > +typedef struct dma_desc {
> > +     desc0_u desc0;
> > +     desc1_u desc1;
> > +     /* The address of buffers */
> > +     unsigned int    desc2;
> > +     /* Next desc's address */
> > +     unsigned int    desc3;
> > +} __attribute__((packed)) dma_desc_t;
> > +
> > +enum rx_frame_status { /* IPC status */
> > +     good_frame = 0,
> > +     discard_frame = 1,
> > +     csum_none = 2,
> > +     llc_snap = 4,
> > +};
> > +
> > +enum tx_dma_irq_status {
> > +     tx_hard_error = 1,
> > +     tx_hard_error_bump_tc = 2,
> > +     handle_tx_rx = 3,
> > +};
> > +
> > +struct geth_extra_stats {
> > +     /* Transmit errors */
> > +     unsigned long tx_underflow;
> > +     unsigned long tx_carrier;
> > +     unsigned long tx_losscarrier;
> > +     unsigned long vlan_tag;
> > +     unsigned long tx_deferred;
> > +     unsigned long tx_vlan;
> > +     unsigned long tx_jabber;
> > +     unsigned long tx_frame_flushed;
> > +     unsigned long tx_payload_error;
> > +     unsigned long tx_ip_header_error;
> > +
> > +     /* Receive errors */
> > +     unsigned long rx_desc;
> > +     unsigned long sa_filter_fail;
> > +     unsigned long overflow_error;
> > +     unsigned long ipc_csum_error;
> > +     unsigned long rx_collision;
> > +     unsigned long rx_crc;
> > +     unsigned long dribbling_bit;
> > +     unsigned long rx_length;
> > +     unsigned long rx_mii;
> > +     unsigned long rx_multicast;
> > +     unsigned long rx_gmac_overflow;
> > +     unsigned long rx_watchdog;
> > +     unsigned long da_rx_filter_fail;
> > +     unsigned long sa_rx_filter_fail;
> > +     unsigned long rx_missed_cntr;
> > +     unsigned long rx_overflow_cntr;
> > +     unsigned long rx_vlan;
> > +
> > +     /* Tx/Rx IRQ errors */
> > +     unsigned long tx_undeflow_irq;
> > +     unsigned long tx_process_stopped_irq;
> > +     unsigned long tx_jabber_irq;
> > +     unsigned long rx_overflow_irq;
> > +     unsigned long rx_buf_unav_irq;
> > +     unsigned long rx_process_stopped_irq;
> > +     unsigned long rx_watchdog_irq;
> > +     unsigned long tx_early_irq;
> > +     unsigned long fatal_bus_error_irq;
> > +
> > +     /* Extra info */
> > +     unsigned long threshold;
> > +     unsigned long tx_pkt_n;
> > +     unsigned long rx_pkt_n;
> > +     unsigned long poll_n;
> > +     unsigned long sched_timer_n;
> > +     unsigned long normal_irq_n;
> > +};
> > +
> > +int sunxi_mdio_read(void *,  int, int);
> > +int sunxi_mdio_write(void *, int, int, unsigned short);
> > +int sunxi_mdio_reset(void *);
> > +void sunxi_set_link_mode(void *iobase, int duplex, int speed);
> > +void sunxi_int_disable(void *);
> > +int sunxi_int_status(void *, struct geth_extra_stats *x);
> > +int sunxi_mac_init(void *, int txmode, int rxmode);
> > +void sunxi_set_umac(void *, unsigned char *, int);
> > +void sunxi_mac_enable(void *);
> > +void sunxi_mac_disable(void *);
> > +void sunxi_tx_poll(void *);
> > +void sunxi_int_enable(void *);
> > +void sunxi_start_rx(void *, unsigned long);
> > +void sunxi_start_tx(void *, unsigned long);
> > +void sunxi_stop_tx(void *);
> > +void sunxi_stop_rx(void *);
> > +void sunxi_hash_filter(void *iobase, unsigned long low, unsigned long high);
> > +void sunxi_set_filter(void *iobase, unsigned long flags);
> > +void sunxi_flow_ctrl(void *iobase, int duplex, int fc, int pause);
> > +void sunxi_mac_loopback(void *iobase, int enable);
> > +
> > +void desc_buf_set(struct dma_desc *p, unsigned long paddr, int size);
> > +void desc_set_own(struct dma_desc *p);
> > +void desc_init_chain(struct dma_desc *p, unsigned long paddr,  unsigned int size);
> > +void desc_tx_close(struct dma_desc *first, struct dma_desc *end, int csum_insert);
> > +void desc_init(struct dma_desc *p);
> > +int desc_get_tx_status(struct dma_desc *desc, struct geth_extra_stats *x);
> > +int desc_buf_get_len(struct dma_desc *desc);
> > +int desc_buf_get_addr(struct dma_desc *desc);
> > +int desc_get_rx_status(struct dma_desc *desc, struct geth_extra_stats *x);
> > +int desc_get_own(struct dma_desc *desc);
> > +int desc_get_tx_ls(struct dma_desc *desc);
> > +int desc_rx_frame_len(struct dma_desc *desc);
> > +
> > +int sunxi_mac_reset(void *iobase, void (*mdelay)(int), int n);
> > +int sunxi_geth_register(void *iobase, int version, unsigned int div);
> > +
> > +#if IS_ENABLED(CONFIG_SUNXI_EPHY)
> > +extern int ephy_is_enable(void);
> > +#endif
> > +
> > +#if IS_ENABLED(CONFIG_ARCH_SUN8IW3) \
> > +     || IS_ENABLED(CONFIG_ARCH_SUN9IW1) \
> > +     || IS_ENABLED(CONFIG_ARCH_SUN7I)
> > +#define HW_VERSION   0
> > +#else
> > +#define HW_VERSION   1
> > +#endif
> > +
> > +#endif
> > diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
> > index 821e85a..3c86c2a 100644
> > --- a/drivers/net/phy/realtek.c
> > +++ b/drivers/net/phy/realtek.c
> > @@ -338,7 +338,7 @@ static int rtl8211f_config_init(struct phy_device *phydev)
> >                       "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
> >                       val_txdly ? "enabled" : "disabled");
> >       }
> > -
> > +return 0;
> >       ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY,
> >                                      val_rxdly);
> >       if (ret < 0) {
>



--
Best Regards
 Guo Ren

ML: https://lore.kernel.org/linux-csky/



More information about the linux-riscv mailing list