[PATCH] net/ftgmac100: Rewrite the driver

Benjamin Herrenschmidt benh at kernel.crashing.org
Tue Mar 28 08:14:53 AEDT 2017


On Mon, 2017-03-27 at 15:52 +0200, Cédric Le Goater wrote:
> On 03/27/2017 12:28 PM, Benjamin Herrenschmidt wrote:
> > This is an almost-complete rewrite of this driver.
> > 
> > The patch overall multiplies the performance of the driver
> > on an AST2500 eval board with a gigabit link.
> > 
> > I get above 360Mbit/s with this vs. about 80Mbit/s with the current
> > driver. I've done some tests on NC-SI machines as well, I get close
> > to peak (above 90Mbit/s).
> 
> Which tests are you using ? 

nuttcp mostly.

> > It does that by, among other things, rewriting the receive and
> > transmit code, to both simplify the fast path as much as possible,
> > avoid flushing of the caches (the aspeed chips have really slow
> > little ARM cores), implementing support for fragmented sends,
> > fixing HW checksum generation (AST2500 only), etc...
> > 
> > In addition, I've added netpoll support, tx timeout recovery,
> > better handling of link speed changes, multicast filter
> > and promisc support, various ethtool config additions etc...
> 
> Nice. So I will need to update the QEMU model as it is quite 
> basic for the moment.

You will need to support fragmented sends yes ;-)

> > Finally, the code has been cleaned up and reorganized, and
> > various corner cases fixed, such as recovery in some error
> > situations.
> > 
> > Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
> > --
> > 
> > Please give it a good beating before I submit upstream.
> 
> I have used iperf to stress the AST2500 EVB. It first complains with
> :  

Can you give me a quick description of what exactly you did ?

> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x20f:
> NO_RXBUF RPKT_LOST 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> [  234.910000] ftgmac100 1e660000.ethernet eth0: [ISR] = 0x204:
> NO_RXBUF 
> 
> and then blackouts. HW reset was needed.

Ok, I'll investigate. I didn't manage to trigger that error with
nuttcp. Can you give me the precise iperf commands you used ?

Cheers,
Ben.

> C. 
> 
> > 
> > While I tried initially to do an incremental series of patch this
> > quickly became unrealistic as I rewrote more of the driver. This
> > is better reviewed as a new replacement driver for the same chip
> > instead. Since the Aspeed SoC is the only in-tree user and we are
> > maintaining this for OpenBMC, the risk is limited, I didn't feel
> > the need of submitting this as a separate driver and deprecate
> > the old one.
> > ---
> >  arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts |    1 -
> >  arch/arm/boot/dts/aspeed-g4.dtsi              |    6 +-
> >  arch/arm/boot/dts/aspeed-g5.dtsi              |    6 +-
> >  drivers/net/ethernet/faraday/ftgmac100.c      | 2141
> > ++++++++++++++-----------
> >  drivers/net/ethernet/faraday/ftgmac100.h      |  183 ++-
> >  5 files changed, 1390 insertions(+), 947 deletions(-)
> > 
> > diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> > b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> > index 6c52f1c..24e2c0f 100644
> > --- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> > +++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> > @@ -102,7 +102,6 @@
> >  	status = "okay";
> >  
> >  	use-ncsi;
> > -	no-hw-checksum;
> >  };
> >  
> >  
> > diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi
> > b/arch/arm/boot/dts/aspeed-g4.dtsi
> > index 28408a2..80c0867 100644
> > --- a/arch/arm/boot/dts/aspeed-g4.dtsi
> > +++ b/arch/arm/boot/dts/aspeed-g4.dtsi
> > @@ -42,18 +42,16 @@
> >  		};
> >  
> >  		mac0: ethernet at 1e660000 {
> > -			compatible = "faraday,ftgmac100";
> > +			compatible = "aspeed,ast2400-mac",
> > "faraday,ftgmac100";
> >  			reg = <0x1e660000 0x180>;
> >  			interrupts = <2>;
> > -			no-hw-checksum;
> >  			status = "disabled";
> >  		};
> >  
> >  		mac1: ethernet at 1e680000 {
> > -			compatible = "faraday,ftgmac100";
> > +			compatible = "aspeed,ast2400-mac",
> > "faraday,ftgmac100";
> >  			reg = <0x1e680000 0x180>;
> >  			interrupts = <3>;
> > -			no-hw-checksum;
> >  			status = "disabled";
> >  		};
> >  
> > diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi
> > b/arch/arm/boot/dts/aspeed-g5.dtsi
> > index 7c09a26..925cdf7 100644
> > --- a/arch/arm/boot/dts/aspeed-g5.dtsi
> > +++ b/arch/arm/boot/dts/aspeed-g5.dtsi
> > @@ -141,18 +141,16 @@
> >  		};
> >  
> >  		mac0: ethernet at 1e660000 {
> > -			compatible = "faraday,ftgmac100";
> > +			compatible = "aspeed,ast2500-mac",
> > "faraday,ftgmac100";
> >  			reg = <0x1e660000 0x180>;
> >  			interrupts = <2>;
> > -			no-hw-checksum;
> >  			status = "disabled";
> >  		};
> >  
> >  		mac1: ethernet at 1e680000 {
> > -			compatible = "faraday,ftgmac100";
> > +			compatible = "aspeed,ast2500-mac",
> > "faraday,ftgmac100";
> >  			reg = <0x1e680000 0x180>;
> >  			interrupts = <3>;
> > -			no-hw-checksum;
> >  			status = "disabled";
> >  		};
> >  
> > diff --git a/drivers/net/ethernet/faraday/ftgmac100.c
> > b/drivers/net/ethernet/faraday/ftgmac100.c
> > index f40fa92..a81568a 100644
> > --- a/drivers/net/ethernet/faraday/ftgmac100.c
> > +++ b/drivers/net/ethernet/faraday/ftgmac100.c
> > @@ -4,6 +4,10 @@
> >   * (C) Copyright 2009-2011 Faraday Technology
> >   * Po-Yu Chuang <ratbert at faraday-tech.com>
> >   *
> > + * Largely rewritten by
> > + *
> > + * Benjamin Herrenschmidt, copyright 2017, IBM Corp.
> > + *
> >   * This program is free software; you can redistribute it and/or
> > modify
> >   * it under the terms of the GNU General Public License as
> > published by
> >   * the Free Software Foundation; either version 2 of the License,
> > or
> > @@ -18,7 +22,6 @@
> >   * along with this program; if not, write to the Free Software
> >   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> >   */
> > -
> >  #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
> >  
> >  #include <linux/dma-mapping.h>
> > @@ -30,900 +33,1384 @@
> >  #include <linux/netdevice.h>
> >  #include <linux/phy.h>
> >  #include <linux/platform_device.h>
> > +#include <linux/crc32.h>
> > +#include <linux/delay.h>
> >  #include <net/ip.h>
> >  #include <net/ncsi.h>
> >  
> >  #include "ftgmac100.h"
> >  
> >  #define DRV_NAME	"ftgmac100"
> > -#define DRV_VERSION	"0.7"
> > +#define DRV_VERSION	"1.0"
> >  
> > -#define RX_QUEUE_ENTRIES	256	/* must be power of 2
> > */
> > -#define TX_QUEUE_ENTRIES	512	/* must be power of 2
> > */
> > +/* Arbitrary values, I am not sure the HW has limits */
> > +#define MAX_RX_QUEUE_ENTRIES	1024
> > +#define MAX_TX_QUEUE_ENTRIES	1024
> > +#define MIN_RX_QUEUE_ENTRIES	32
> > +#define MIN_TX_QUEUE_ENTRIES	32
> >  
> > -#define MAX_PKT_SIZE		1518
> > -#define RX_BUF_SIZE		PAGE_SIZE	/* must be
> > smaller than 0x3fff */
> > +/* Defaults */
> > +#define DEF_RX_QUEUE_ENTRIES	128
> > +#define DEF_TX_QUEUE_ENTRIES	128
> >  
> > -/*****************************************************************
> > *************
> > - * private data
> > -
> > *******************************************************************
> > **********/
> > -struct ftgmac100_descs {
> > -	struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
> > -	struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES];
> > -};
> > +/* We don't do jumbo frames */
> > +#define MAX_PKT_SIZE		1536
> > +#define RX_BUF_SIZE		MAX_PKT_SIZE  /* must be
> > smaller than 0x3fff */
> >  
> >  struct ftgmac100 {
> > +	/* Registers */
> >  	struct resource *res;
> >  	void __iomem *base;
> > -	int irq;
> > -
> > -	struct ftgmac100_descs *descs;
> > -	dma_addr_t descs_dma_addr;
> > -
> > -	struct page *rx_pages[RX_QUEUE_ENTRIES];
> >  
> > +	/* Rx ring */
> > +	unsigned int rx_q_entries;
> > +	struct ftgmac100_rxdes *rxdes;
> > +	dma_addr_t rxdes_dma;
> > +	struct sk_buff **rx_skbs;
> >  	unsigned int rx_pointer;
> > +
> > +	/* Tx ring */
> > +	struct ftgmac100_txdes *txdes;
> > +	dma_addr_t txdes_dma;
> > +	unsigned int tx_q_entries;
> > +	struct sk_buff **tx_skbs;
> >  	unsigned int tx_clean_pointer;
> >  	unsigned int tx_pointer;
> > -	unsigned int tx_pending;
> >  
> > -	spinlock_t tx_lock;
> > +	/* Used to signal the reset task of ring change request */
> > +	unsigned int new_rx_q_entries;
> > +	unsigned int new_tx_q_entries;
> > +
> > +	/* Scratch page to use when rx skb alloc fails */
> > +	void *rx_scratch;
> > +	dma_addr_t rx_scratch_dma;
> >  
> > -	struct net_device *netdev;
> > +	/* Component structures */
> > +	struct net_device *ndev;
> >  	struct device *dev;
> > -	struct ncsi_dev *ndev;
> > +	struct ncsi_dev *ncsidev;
> >  	struct napi_struct napi;
> > -
> > +	struct work_struct reset_task;
> >  	struct mii_bus *mii_bus;
> > -	int old_speed;
> > -	int int_mask_all;
> > +
> > +	/* Link management */
> > +	int cur_speed;
> > +	int cur_duplex;
> >  	bool use_ncsi;
> > -	bool enabled;
> > +
> > +	/* Multicast filter settings */
> > +	u32 maht0;
> > +	u32 maht1;
> > +
> > +	/* Tells the reset task to skip  */
> > +	bool stopping;
> > +	bool need_mac_restart;
> > +
> > +	/* Flow control settings */
> > +	bool tx_pause;
> > +	bool rx_pause;
> > +	bool aneg_pause;
> >  
> >  	uint32_t rxdes0_edorr_mask;
> >  	uint32_t txdes0_edotr_mask;
> >  };
> >  
> > -static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
> > -				   struct ftgmac100_rxdes *rxdes,
> > gfp_t gfp);
> > -
> > -/*****************************************************************
> > *************
> > - * internal functions (hardware register access)
> > -
> > *******************************************************************
> > **********/
> > -static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv,
> > dma_addr_t addr)
> > +static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv,
> > +				  unsigned int entry, gfp_t gfp)
> >  {
> > -	iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
> > -}
> > +	struct net_device *ndev = priv->ndev;
> > +	struct ftgmac100_rxdes *rxdes = &priv->rxdes[entry];
> > +	struct sk_buff *skb;
> > +	dma_addr_t map;
> > +	int err = 0;
> >  
> > -static void ftgmac100_set_rx_buffer_size(struct ftgmac100 *priv,
> > -		unsigned int size)
> > -{
> > -	size = FTGMAC100_RBSR_SIZE(size);
> > -	iowrite32(size, priv->base + FTGMAC100_OFFSET_RBSR);
> > -}
> > +	skb = netdev_alloc_skb_ip_align(ndev, RX_BUF_SIZE);
> > +	if (unlikely(!skb)) {
> > +		if (net_ratelimit())
> > +			netdev_err(ndev, "failed to allocate rx
> > skb\n");
> > +		err = -ENOMEM;
> > +		map = priv->rx_scratch_dma;
> > +	} else {
> > +		map = dma_map_single(priv->dev, skb->data,
> > RX_BUF_SIZE,
> > +				     DMA_FROM_DEVICE);
> > +		if (unlikely(dma_mapping_error(priv->dev, map))) {
> > +			if (net_ratelimit())
> > +				netdev_err(ndev, "failed to map rx
> > page\n");
> > +			dev_kfree_skb_any(skb);
> > +			map = priv->rx_scratch_dma;
> > +			skb = NULL;
> > +			err = -ENOMEM;
> > +		}
> > +	}
> >  
> > -static void ftgmac100_set_normal_prio_tx_ring_base(struct
> > ftgmac100 *priv,
> > -						   dma_addr_t
> > addr)
> > -{
> > -	iowrite32(addr, priv->base + FTGMAC100_OFFSET_NPTXR_BADR);
> > -}
> > +	/* Store skb */
> > +	priv->rx_skbs[entry] = skb;
> >  
> > -static void ftgmac100_txdma_normal_prio_start_polling(struct
> > ftgmac100 *priv)
> > -{
> > -	iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD);
> > +	/* Store DMA address into RX desc */
> > +	ftgmac100_rxdes_set_dma_addr(rxdes, map);
> > +
> > +	/* Ensure the above is ordered vs clearing the OWN bit */
> > +	dma_wmb();
> > +
> > +	/* Clean rxdes0 (which resets own bit) */
> > +	rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask);
> > +
> > +	return err;
> >  }
> >  
> > -static int ftgmac100_reset_hw(struct ftgmac100 *priv)
> > +static void ftgmac100_free_tx_packet(struct ftgmac100 *priv,
> > +				     unsigned int pointer,
> > +				     struct ftgmac100_txdes
> > *txdes)
> >  {
> > -	struct net_device *netdev = priv->netdev;
> > -	int i;
> > +	struct sk_buff *skb = priv->tx_skbs[pointer];
> > +	dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes);
> >  
> > -	/* NOTE: reset clears all registers */
> > -	iowrite32(FTGMAC100_MACCR_SW_RST, priv->base +
> > FTGMAC100_OFFSET_MACCR);
> > -	for (i = 0; i < 5; i++) {
> > -		unsigned int maccr;
> > +	if (ftgmac100_txdes_get_first_segment(txdes)) {
> > +		size_t len = skb_headlen(skb);
> >  
> > -		maccr = ioread32(priv->base +
> > FTGMAC100_OFFSET_MACCR);
> > -		if (!(maccr & FTGMAC100_MACCR_SW_RST))
> > -			return 0;
> > +		if (skb_shinfo(skb)->nr_frags == 0 && len <
> > ETH_ZLEN)
> > +			len = ETH_ZLEN;
> > +		dma_unmap_single(priv->dev, map, len,
> > DMA_TO_DEVICE);
> > +	} else
> > +		dma_unmap_page(priv->dev, map,
> > +			       ftgmac100_txdes_get_buffer_size(txd
> > es),
> > +			       DMA_TO_DEVICE);
> >  
> > -		udelay(1000);
> > -	}
> > -
> > -	netdev_err(netdev, "software reset failed\n");
> > -	return -EIO;
> > +	if (ftgmac100_txdes_get_last_segment(txdes))
> > +		dev_kfree_skb_any(skb);
> > +	priv->tx_skbs[pointer] = NULL;
> >  }
> >  
> > -static void ftgmac100_set_mac(struct ftgmac100 *priv, const
> > unsigned char *mac)
> > +static int ftgmac100_next_rx_pointer(struct ftgmac100 *priv, int
> > pointer)
> >  {
> > -	unsigned int maddr = mac[0] << 8 | mac[1];
> > -	unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4]
> > << 8 | mac[5];
> > -
> > -	iowrite32(maddr, priv->base + FTGMAC100_OFFSET_MAC_MADR);
> > -	iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
> > +	return (pointer + 1) & (priv->rx_q_entries - 1);
> >  }
> >  
> > -static void ftgmac100_setup_mac(struct ftgmac100 *priv)
> > +static void ftgmac100_rx_packet_error(struct ftgmac100 *priv,
> > +				      struct ftgmac100_rxdes
> > *rxdes)
> >  {
> > -	u8 mac[ETH_ALEN];
> > -	unsigned int m;
> > -	unsigned int l;
> > -	void *addr;
> > +	struct net_device *ndev = priv->ndev;
> >  
> > -	addr = device_get_mac_address(priv->dev, mac, ETH_ALEN);
> > -	if (addr) {
> > -		ether_addr_copy(priv->netdev->dev_addr, mac);
> > -		dev_info(priv->dev, "Read MAC address %pM from
> > device tree\n",
> > -			 mac);
> > -		return;
> > -	}
> > +	if (unlikely(ftgmac100_rxdes_rx_error(rxdes))) {
> > +		if (net_ratelimit())
> > +			netdev_info(ndev, "rx err\n");
> >  
> > -	m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
> > -	l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
> > +		ndev->stats.rx_errors++;
> > +	}
> >  
> > -	mac[0] = (m >> 8) & 0xff;
> > -	mac[1] = m & 0xff;
> > -	mac[2] = (l >> 24) & 0xff;
> > -	mac[3] = (l >> 16) & 0xff;
> > -	mac[4] = (l >> 8) & 0xff;
> > -	mac[5] = l & 0xff;
> > +	if (unlikely(ftgmac100_rxdes_crc_error(rxdes))) {
> > +		if (net_ratelimit())
> > +			netdev_info(ndev, "rx crc err\n");
> >  
> > -	if (is_valid_ether_addr(mac)) {
> > -		ether_addr_copy(priv->netdev->dev_addr, mac);
> > -		dev_info(priv->dev, "Read MAC address %pM from
> > chip\n", mac);
> > -	} else {
> > -		eth_hw_addr_random(priv->netdev);
> > -		dev_info(priv->dev, "Generated random MAC address
> > %pM\n",
> > -			 priv->netdev->dev_addr);
> > +		ndev->stats.rx_crc_errors++;
> >  	}
> > -}
> >  
> > -static int ftgmac100_set_mac_addr(struct net_device *dev, void *p)
> > -{
> > -	int ret;
> > +	if (unlikely(ftgmac100_rxdes_frame_too_long(rxdes))) {
> > +		if (net_ratelimit())
> > +			netdev_info(ndev, "rx frame too long\n");
> >  
> > -	ret = eth_prepare_mac_addr_change(dev, p);
> > -	if (ret < 0)
> > -		return ret;
> > +		ndev->stats.rx_length_errors++;
> > +	} else if (unlikely(ftgmac100_rxdes_runt(rxdes))) {
> > +		if (net_ratelimit())
> > +			netdev_info(ndev, "rx runt\n");
> >  
> > -	eth_commit_mac_addr_change(dev, p);
> > -	ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr);
> > +		ndev->stats.rx_length_errors++;
> > +	} else if (unlikely(ftgmac100_rxdes_odd_nibble(rxdes))) {
> > +		if (net_ratelimit())
> > +			netdev_info(ndev, "rx odd nibble\n");
> >  
> > -	return 0;
> > +		ndev->stats.rx_length_errors++;
> > +	}
> >  }
> >  
> > -static void ftgmac100_init_hw(struct ftgmac100 *priv)
> > +static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int
> > *processed)
> >  {
> > -	/* setup ring buffer base registers */
> > -	ftgmac100_set_rx_ring_base(priv,
> > -				   priv->descs_dma_addr +
> > -				   offsetof(struct
> > ftgmac100_descs, rxdes));
> > -	ftgmac100_set_normal_prio_tx_ring_base(priv,
> > -					       priv-
> > >descs_dma_addr +
> > -					       offsetof(struct
> > ftgmac100_descs, txdes));
> > +	struct net_device *ndev = priv->ndev;
> > +	struct ftgmac100_rxdes *rxdes;
> > +	struct sk_buff *skb;
> > +	unsigned int pointer, size;
> > +	dma_addr_t map;
> >  
> > -	ftgmac100_set_rx_buffer_size(priv, RX_BUF_SIZE);
> > +	/* Grab next RX descriptor */
> > +	pointer = priv->rx_pointer;
> > +	rxdes = &priv->rxdes[pointer];
> >  
> > -	iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), priv->base +
> > FTGMAC100_OFFSET_APTC);
> > +	/* Do we have a packet ? */
> > +	if (!ftgmac100_rxdes_packet_ready(rxdes))
> > +		return false;
> >  
> > -	ftgmac100_set_mac(priv, priv->netdev->dev_addr);
> > -}
> > +	/* We don't cope with fragmented RX packets */
> > +	if (unlikely(!ftgmac100_rxdes_first_segment(rxdes) ||
> > +		     !ftgmac100_rxdes_last_segment(rxdes)))
> > +		goto drop;
> >  
> > -#define MACCR_ENABLE_ALL	(FTGMAC100_MACCR_TXDMA_EN	|
> > \
> > -				 FTGMAC100_MACCR_RXDMA_EN	|
> > \
> > -				 FTGMAC100_MACCR_TXMAC_EN	|
> > \
> > -				 FTGMAC100_MACCR_RXMAC_EN	|
> > \
> > -				 FTGMAC100_MACCR_FULLDUP	|
> > \
> > -				 FTGMAC100_MACCR_CRC_APD	|
> > \
> > -				 FTGMAC100_MACCR_PHY_LINK_LEVEL |
> > \
> > -				 FTGMAC100_MACCR_RX_RUNT	|
> > \
> > -				 FTGMAC100_MACCR_RX_BROADPKT)
> > -
> > -static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed)
> > -{
> > -	int maccr = MACCR_ENABLE_ALL;
> > +	/* Any error (other than csum offload) flagged ? */
> > +	if (unlikely(ftgmac100_rxdes_any_error(rxdes))) {
> > +		ftgmac100_rx_packet_error(priv, rxdes);
> > +		goto drop;
> > +	}
> >  
> > -	switch (speed) {
> > -	default:
> > -	case 10:
> > -		break;
> > +	/* Grab the corresponding skb */
> > +	skb = priv->rx_skbs[pointer];
> > +	if (unlikely(!skb)) {
> > +		netdev_err(ndev, "Missing skb in rx ring !\n");
> > +		goto drop;
> > +	}
> >  
> > -	case 100:
> > -		maccr |= FTGMAC100_MACCR_FAST_MODE;
> > -		break;
> > +	/* Grab received size */
> > +	size = ftgmac100_rxdes_data_length(rxdes);
> > +	skb_put(skb, size);
> >  
> > -	case 1000:
> > -		maccr |= FTGMAC100_MACCR_GIGA_MODE;
> > -		break;
> > -	}
> > +	/* Tear down DMA mapping, do necessary cache management */
> > +	map = ftgmac100_rxdes_get_dma_addr(rxdes);
> >  
> > -	iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
> > -}
> > +#if defined(CONFIG_ARM) && !defined(CONFIG_ARM_DMA_USE_IOMMU)
> > +	/*
> > +	 * When we don't have an iommu, we can save cycles by not
> > +	 * invalidating the cache for the part of the packet that
> > +	 * wasn't received.
> > +	 */
> > +	dma_unmap_single(priv->dev, map, size, DMA_FROM_DEVICE);
> > +#else
> > +	dma_unmap_single(priv->dev, map, RX_BUF_SIZE,
> > DMA_FROM_DEVICE);
> > +#endif
> > +
> > +	/* Grab protocol and handle rx csum */
> > +	skb->protocol = eth_type_trans(skb, ndev);
> > +	if ((ndev->features & NETIF_F_RXCSUM) &&
> > +	    !ftgmac100_rxdes_csum_err(rxdes))
> > +		skb->ip_summed = CHECKSUM_UNNECESSARY;
> > +	else
> > +		skb->ip_summed = CHECKSUM_NONE;
> >  
> > -static void ftgmac100_stop_hw(struct ftgmac100 *priv)
> > -{
> > -	iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
> > -}
> > +	/* Some stats ... */
> > +	if (unlikely(ftgmac100_rxdes_multicast(rxdes)))
> > +		ndev->stats.multicast++;
> > +	ndev->stats.rx_packets++;
> > +	ndev->stats.rx_bytes += size;
> >  
> > -/*****************************************************************
> > *************
> > - * internal functions (receive descriptor)
> > -
> > *******************************************************************
> > **********/
> > -static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS);
> > -}
> > +	/* Resplenish rx ring */
> > +	ftgmac100_alloc_rx_buf(priv, pointer, GFP_ATOMIC);
> > +	priv->rx_pointer = ftgmac100_next_rx_pointer(priv,
> > pointer);
> >  
> > -static bool ftgmac100_rxdes_last_segment(struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_LRS);
> > -}
> > +	/* push packet to protocol stack */
> > +	if (skb->ip_summed == CHECKSUM_NONE)
> > +		netif_receive_skb(skb);
> > +	else
> > +		napi_gro_receive(&priv->napi, skb);
> >  
> > -static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY);
> > -}
> > +	(*processed)++;
> > +	return true;
> >  
> > -static void ftgmac100_rxdes_set_dma_own(const struct ftgmac100
> > *priv,
> > -		struct ftgmac100_rxdes *rxdes)
> > -{
> > -	/* clear status bits */
> > + drop:
> > +	/* Clean rxdes0 (which resets own bit) */
> >  	rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask);
> > +	priv->rx_pointer = ftgmac100_next_rx_pointer(priv, priv-
> > >rx_pointer);
> > +	ndev->stats.rx_dropped++;
> > +	return true;
> >  }
> >  
> > -static bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes
> > *rxdes)
> > +static int ftgmac100_next_tx_pointer(struct ftgmac100 *priv, int
> > pointer)
> >  {
> > -	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_RX_ERR);
> > +	return (pointer + 1) & (priv->tx_q_entries - 1);
> >  }
> >  
> > -static bool ftgmac100_rxdes_crc_error(struct ftgmac100_rxdes
> > *rxdes)
> > +static u32 ftgmac100_tx_buf_avail(struct ftgmac100 *priv)
> >  {
> > -	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_CRC_ERR);
> > +	if (priv->tx_clean_pointer <= priv->tx_pointer)
> > +		return priv->tx_clean_pointer + (priv-
> > >tx_q_entries - 1)
> > +			- priv->tx_pointer;
> > +	else
> > +		return priv->tx_clean_pointer - priv->tx_pointer -
> > 1;
> >  }
> >  
> > -static bool ftgmac100_rxdes_frame_too_long(struct ftgmac100_rxdes
> > *rxdes)
> > +static bool ftgmac100_tx_buf_cleanable(struct ftgmac100 *priv)
> >  {
> > -	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FTL);
> > +	return priv->tx_pointer != priv->tx_clean_pointer;
> >  }
> >  
> > -static bool ftgmac100_rxdes_runt(struct ftgmac100_rxdes *rxdes)
> > +static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv)
> >  {
> > -	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RUNT);
> > -}
> > +	struct net_device *ndev = priv->ndev;
> > +	struct ftgmac100_txdes *txdes;
> > +	struct sk_buff *skb;
> > +	unsigned int pointer = priv->tx_clean_pointer;
> >  
> > -static bool ftgmac100_rxdes_odd_nibble(struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_RX_ODD_NB);
> > -}
> > +	txdes = &priv->txdes[pointer];
> > +	if (ftgmac100_txdes_owned_by_dma(txdes))
> > +		return false;
> >  
> > -static unsigned int ftgmac100_rxdes_data_length(struct
> > ftgmac100_rxdes *rxdes)
> > -{
> > -	return le32_to_cpu(rxdes->rxdes0) & FTGMAC100_RXDES0_VDBC;
> > -}
> > +	if (ftgmac100_txdes_get_last_segment(txdes)) {
> > +		skb = priv->tx_skbs[pointer];
> > +		ndev->stats.tx_packets++;
> > +		ndev->stats.tx_bytes += skb->len;
> > +	}
> > +	ftgmac100_free_tx_packet(priv, priv->tx_clean_pointer,
> > txdes);
> >  
> > -static bool ftgmac100_rxdes_multicast(struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_MULTICAST);
> > -}
> > +	/* Clear except end of ring bit */
> > +	txdes->txdes0 &= cpu_to_le32(priv->txdes0_edotr_mask);
> > +	txdes->txdes1 = 0;
> >  
> > -static void ftgmac100_rxdes_set_end_of_ring(const struct ftgmac100
> > *priv,
> > -		struct ftgmac100_rxdes *rxdes)
> > -{
> > -	rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask);
> > -}
> > +	priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv,
> > pointer);
> >  
> > -static void ftgmac100_rxdes_set_dma_addr(struct ftgmac100_rxdes
> > *rxdes,
> > -					 dma_addr_t addr)
> > -{
> > -	rxdes->rxdes3 = cpu_to_le32(addr);
> > +	return true;
> >  }
> >  
> > -static dma_addr_t ftgmac100_rxdes_get_dma_addr(struct
> > ftgmac100_rxdes *rxdes)
> > +static irqreturn_t ftgmac100_interrupt(int irq __always_unused,
> > void *dev_id)
> >  {
> > -	return le32_to_cpu(rxdes->rxdes3);
> > -}
> > +	struct net_device *ndev = dev_id;
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +	unsigned int status;
> >  
> > -static bool ftgmac100_rxdes_is_tcp(struct ftgmac100_rxdes *rxdes)
> > -{
> > -	return (rxdes->rxdes1 &
> > cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) ==
> > -	       cpu_to_le32(FTGMAC100_RXDES1_PROT_TCPIP);
> > -}
> > +	/* Fetch and clear interrupt bits, process abnormal ones
> > */
> > +	status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
> > +	iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
> > +	if (unlikely(status & FTGMAC100_INT_BAD)) {
> > +		if (net_ratelimit())
> > +			netdev_warn(ndev, "[ISR] = 0x%x:
> > %s%s%s\n", status,
> > +				    status &
> > FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
> > +				    status &
> > FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "",
> > +				    status & FTGMAC100_INT_AHB_ERR
> > ? "AHB_ERR " : "");
> >  
> > -static bool ftgmac100_rxdes_is_udp(struct ftgmac100_rxdes *rxdes)
> > -{
> > -	return (rxdes->rxdes1 &
> > cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) ==
> > -	       cpu_to_le32(FTGMAC100_RXDES1_PROT_UDPIP);
> > -}
> > +		/* RX buffer unavailable */
> > +		if (status & FTGMAC100_INT_NO_RXBUF)
> > +			ndev->stats.rx_over_errors++;
> >  
> > -static bool ftgmac100_rxdes_tcpcs_err(struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	return rxdes->rxdes1 &
> > cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR);
> > -}
> > +		/* Received packet lost due to RX FIFO full */
> > +		if (status & FTGMAC100_INT_RPKT_LOST)
> > +			ndev->stats.rx_fifo_errors++;
> >  
> > -static bool ftgmac100_rxdes_udpcs_err(struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	return rxdes->rxdes1 &
> > cpu_to_le32(FTGMAC100_RXDES1_UDP_CHKSUM_ERR);
> > -}
> > +		/* AHB error -> Reset the chip */
> > +		if (status & FTGMAC100_INT_AHB_ERR) {
> > +			iowrite32(0, priv->base +
> > FTGMAC100_OFFSET_IER);
> > +			schedule_work(&priv->reset_task);
> > +			return IRQ_HANDLED;
> > +		}
> >  
> > -static bool ftgmac100_rxdes_ipcs_err(struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	return rxdes->rxdes1 &
> > cpu_to_le32(FTGMAC100_RXDES1_IP_CHKSUM_ERR);
> > -}
> > +		/*
> > +		 * We may need to restart the MAC after such
> > errors, delay
> > +		 * this until after we have freed some Rx buffers
> > though
> > +		 */
> > +		priv->need_mac_restart = true;
> > +	}
> >  
> > -static inline struct page **ftgmac100_rxdes_page_slot(struct
> > ftgmac100 *priv,
> > -		struct ftgmac100_rxdes *rxdes)
> > -{
> > -	return &priv->rx_pages[rxdes - priv->descs->rxdes];
> > -}
> >  
> > -/*
> > - * rxdes2 is not used by hardware. We use it to keep track of
> > page.
> > - * Since hardware does not touch it, we can skip
> > cpu_to_le32()/le32_to_cpu().
> > - */
> > -static void ftgmac100_rxdes_set_page(struct ftgmac100 *priv,
> > -		struct ftgmac100_rxdes *rxdes, struct page *page)
> > -{
> > -	*ftgmac100_rxdes_page_slot(priv, rxdes) = page;
> > -}
> > +	/* Only enable "bad" interrupts while NAPI is on */
> > +	iowrite32(FTGMAC100_INT_BAD, priv->base +
> > FTGMAC100_OFFSET_IER);
> >  
> > -static struct page *ftgmac100_rxdes_get_page(struct ftgmac100
> > *priv,
> > -		struct ftgmac100_rxdes *rxdes)
> > -{
> > -	return *ftgmac100_rxdes_page_slot(priv, rxdes);
> > -}
> > +	/* Schedule NAPI bh */
> > +	napi_schedule(&priv->napi);
> >  
> > -/*****************************************************************
> > *************
> > - * internal functions (receive)
> > -
> > *******************************************************************
> > **********/
> > -static int ftgmac100_next_rx_pointer(int pointer)
> > -{
> > -	return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
> > +	return IRQ_HANDLED;
> >  }
> >  
> > -static void ftgmac100_rx_pointer_advance(struct ftgmac100 *priv)
> > +static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
> > +				     struct net_device *ndev)
> >  {
> > -	priv->rx_pointer = ftgmac100_next_rx_pointer(priv-
> > >rx_pointer);
> > -}
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +	struct ftgmac100_txdes *txdes, *first;
> > +	int nfrags;
> > +	int pointer, len, i, j;
> > +	dma_addr_t map;
> >  
> > -static struct ftgmac100_rxdes *ftgmac100_current_rxdes(struct
> > ftgmac100 *priv)
> > -{
> > -	return &priv->descs->rxdes[priv->rx_pointer];
> > -}
> > +	if (unlikely(skb->len > MAX_PKT_SIZE)) {
> > +		if (net_ratelimit())
> > +			netdev_dbg(ndev, "tx packet too big\n");
> > +		goto drop;
> > +	}
> >  
> > -static struct ftgmac100_rxdes *
> > -ftgmac100_rx_locate_first_segment(struct ftgmac100 *priv)
> > -{
> > -	struct ftgmac100_rxdes *rxdes =
> > ftgmac100_current_rxdes(priv);
> > +	/* The HW doesn't pad small frames */
> > +	if (skb_padto(skb, ETH_ZLEN) < 0) {
> > +		ndev->stats.tx_dropped ++;
> > +		return NETDEV_TX_OK;
> > +	}
> >  
> > -	while (ftgmac100_rxdes_packet_ready(rxdes)) {
> > -		if (ftgmac100_rxdes_first_segment(rxdes))
> > -			return rxdes;
> > +	/* XXX Do we have a limit on #fragments ? */
> > +	nfrags = skb_shinfo(skb)->nr_frags;
> >  
> > -		ftgmac100_rxdes_set_dma_own(priv, rxdes);
> > -		ftgmac100_rx_pointer_advance(priv);
> > -		rxdes = ftgmac100_current_rxdes(priv);
> > +	/* Get header len and pad for non-fragmented packets */
> > +	len = skb_headlen(skb);
> > +	if (nfrags == 0 && len < ETH_ZLEN)
> > +		len = ETH_ZLEN;
> > +
> > +	/* Map the packet head */
> > +	map = dma_map_single(priv->dev, skb->data, len,
> > DMA_TO_DEVICE);
> > +	if (dma_mapping_error(priv->dev, map)) {
> > +		if (net_ratelimit())
> > +			netdev_err(ndev, "map tx packet head
> > failed\n");
> > +		goto drop;
> >  	}
> >  
> > -	return NULL;
> > -}
> > +	/* Grab the next free tx descriptor */
> > +	pointer = priv->tx_pointer;
> > +	txdes = first = &priv->txdes[pointer];
> >  
> > -static bool ftgmac100_rx_packet_error(struct ftgmac100 *priv,
> > -				      struct ftgmac100_rxdes
> > *rxdes)
> > -{
> > -	struct net_device *netdev = priv->netdev;
> > -	bool error = false;
> > +	/* Setup it up. We don't set the OWN bit yet. */
> > +	priv->tx_skbs[pointer] = skb;
> > +	ftgmac100_txdes_set_dma_addr(txdes, map);
> > +	ftgmac100_txdes_set_buffer_size(txdes, len);
> > +	ftgmac100_txdes_set_first_segment(txdes);
> >  
> > -	if (unlikely(ftgmac100_rxdes_rx_error(rxdes))) {
> > -		if (net_ratelimit())
> > -			netdev_info(netdev, "rx err\n");
> > +	/* Setup HW checksumming */
> > +	if (skb->ip_summed == CHECKSUM_PARTIAL) {
> > +		__be16 protocol = skb->protocol;
> >  
> > -		netdev->stats.rx_errors++;
> > -		error = true;
> > +		if (protocol == cpu_to_be16(ETH_P_IP)) {
> > +			u8 ip_proto = ip_hdr(skb)->protocol;
> > +
> > +			ftgmac100_txdes_set_ipcs(txdes);
> > +			if (ip_proto == IPPROTO_TCP)
> > +				ftgmac100_txdes_set_tcpcs(txdes);
> > +			else if (ip_proto == IPPROTO_UDP)
> > +				ftgmac100_txdes_set_udpcs(txdes);
> > +		} else if (skb_checksum_help(skb))
> > +			goto drop;
> >  	}
> >  
> > -	if (unlikely(ftgmac100_rxdes_crc_error(rxdes))) {
> > -		if (net_ratelimit())
> > -			netdev_info(netdev, "rx crc err\n");
> > +	/* Next descriptor */
> > +	pointer = ftgmac100_next_tx_pointer(priv, pointer);
> >  
> > -		netdev->stats.rx_crc_errors++;
> > -		error = true;
> > -	} else if (unlikely(ftgmac100_rxdes_ipcs_err(rxdes))) {
> > -		if (net_ratelimit())
> > -			netdev_info(netdev, "rx IP checksum
> > err\n");
> > +	/* Add the fragments */
> > +	for (i = 0; i < nfrags; i++) {
> > +		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> > +
> > +		len = frag->size;
> >  
> > -		error = true;
> > +		/* Map it */
> > +		map = skb_frag_dma_map(priv->dev, frag, 0, len,
> > DMA_TO_DEVICE);
> > +		if (dma_mapping_error(priv->dev, map))
> > +			goto dma_err;
> > +
> > +		/* Setup descriptor */
> > +		priv->tx_skbs[pointer] = skb;
> > +		txdes = &priv->txdes[pointer];
> > +		ftgmac100_txdes_set_dma_addr(txdes, map);
> > +		ftgmac100_txdes_set_buffer_size(txdes, len);
> > +		ftgmac100_txdes_set_dma_own(txdes);
> > +
> > +		/*
> > +		 * The spec is unclear, whether these need to be
> > in the
> > +		 * first descriptor only or all of them. Gor now,
> > do all
> > +		 * of them.
> > +		 */
> > +#define CSUM_MASK cpu_to_le32(FTGMAC100_TXDES1_TCP_CHKSUM | \
> > +			      FTGMAC100_TXDES1_UDP_CHKSUM | \
> > +			      FTGMAC100_TXDES1_IP_CHKSUM)
> > +		txdes->txdes1 |= first->txdes1 & CSUM_MASK;
> > +		pointer = ftgmac100_next_tx_pointer(priv,
> > pointer);
> >  	}
> >  
> > -	if (unlikely(ftgmac100_rxdes_frame_too_long(rxdes))) {
> > -		if (net_ratelimit())
> > -			netdev_info(netdev, "rx frame too
> > long\n");
> > +	/* Tag last fragment */
> > +	ftgmac100_txdes_set_last_segment(txdes);
> >  
> > -		netdev->stats.rx_length_errors++;
> > -		error = true;
> > -	} else if (unlikely(ftgmac100_rxdes_runt(rxdes))) {
> > -		if (net_ratelimit())
> > -			netdev_info(netdev, "rx runt\n");
> > +	/*
> > +	 * Set the own bit on the first descriptor, this can cause
> > the
> > +	 * HW to transmit, it needs to be ordered after all
> > previous
> > +	 * stores.
> > +	 */
> > +	dma_wmb();
> > +	ftgmac100_txdes_set_dma_own(first);
> >  
> > -		netdev->stats.rx_length_errors++;
> > -		error = true;
> > -	} else if (unlikely(ftgmac100_rxdes_odd_nibble(rxdes))) {
> > -		if (net_ratelimit())
> > -			netdev_info(netdev, "rx odd nibble\n");
> > +	/* Update next TX pointer */
> > +	priv->tx_pointer = pointer;
> >  
> > -		netdev->stats.rx_length_errors++;
> > -		error = true;
> > +	/*
> > +	 * If there isn't enough room for all the fragments of a
> > new packet
> > +	 * in the TX ring, stop the queue. The sequence below is
> > race free
> > +	 * vs. a concurrent restart in ftgmac100_poll()
> > +	 */
> > +	if (unlikely(ftgmac100_tx_buf_avail(priv) <=
> > (MAX_SKB_FRAGS + 1))) {
> > +		netif_stop_queue(ndev);
> > +		smp_mb();
> > +		if (ftgmac100_tx_buf_avail(priv) > (MAX_SKB_FRAGS
> > + 1))
> > +			netif_wake_queue(ndev);
> >  	}
> >  
> > -	return error;
> > -}
> > +	/* Poke transmitter to read the updated TX descriptors */
> > +	iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD);
> >  
> > -static void ftgmac100_rx_drop_packet(struct ftgmac100 *priv)
> > -{
> > -	struct net_device *netdev = priv->netdev;
> > -	struct ftgmac100_rxdes *rxdes =
> > ftgmac100_current_rxdes(priv);
> > -	bool done = false;
> > +	return NETDEV_TX_OK;
> >  
> > + dma_err:
> >  	if (net_ratelimit())
> > -		netdev_dbg(netdev, "drop packet %p\n", rxdes);
> > +		netdev_dbg(ndev, "map tx fragment failed\n");
> >  
> > -	do {
> > -		if (ftgmac100_rxdes_last_segment(rxdes))
> > -			done = true;
> > +	/* Free head */
> > +	pointer = priv->tx_pointer;
> > +	ftgmac100_free_tx_packet(priv, pointer, first);
> >  
> > -		ftgmac100_rxdes_set_dma_own(priv, rxdes);
> > -		ftgmac100_rx_pointer_advance(priv);
> > -		rxdes = ftgmac100_current_rxdes(priv);
> > -	} while (!done && ftgmac100_rxdes_packet_ready(rxdes));
> > +	/* Then all fragments */
> > +	for (j = 0; j < i; j++) {
> > +		pointer = ftgmac100_next_tx_pointer(priv,
> > pointer);
> > +		txdes = &priv->txdes[pointer];
> > +		ftgmac100_free_tx_packet(priv, pointer, txdes);
> > +	}
> >  
> > -	netdev->stats.rx_dropped++;
> > +	/*
> > +	 * This cannot be reached if we successfully mapped the
> > +	 * last fragment, so we know ftgmac100_free_tx_packet()
> > +	 * hasn't freed the skb yet.
> > +	 */
> > + drop:
> > +	/* Drop the packet */
> > +	dev_kfree_skb_any(skb);
> > +	ndev->stats.tx_dropped++;
> > +
> > +	return NETDEV_TX_OK;
> >  }
> >  
> > -static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int
> > *processed)
> > +static void ftgmac100_start_mac(struct ftgmac100 *priv)
> >  {
> > -	struct net_device *netdev = priv->netdev;
> > -	struct ftgmac100_rxdes *rxdes;
> > -	struct sk_buff *skb;
> > -	bool done = false;
> > +	u32 maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
> >  
> > -	rxdes = ftgmac100_rx_locate_first_segment(priv);
> > -	if (!rxdes)
> > -		return false;
> > +	priv->need_mac_restart = false;
> >  
> > -	if (unlikely(ftgmac100_rx_packet_error(priv, rxdes))) {
> > -		ftgmac100_rx_drop_packet(priv);
> > -		return true;
> > -	}
> > +	/* Keep the original GMAC and FAST bits */
> > +	maccr &= (FTGMAC100_MACCR_FAST_MODE |
> > FTGMAC100_MACCR_GIGA_MODE);
> >  
> > -	/* start processing */
> > -	skb = netdev_alloc_skb_ip_align(netdev, 128);
> > -	if (unlikely(!skb)) {
> > -		if (net_ratelimit())
> > -			netdev_err(netdev, "rx skb alloc
> > failed\n");
> > +	/* Add all the main enable bits */
> > +	maccr |= FTGMAC100_MACCR_TXDMA_EN	|
> > +		 FTGMAC100_MACCR_RXDMA_EN	|
> > +		 FTGMAC100_MACCR_TXMAC_EN	|
> > +		 FTGMAC100_MACCR_RXMAC_EN	|
> > +		 FTGMAC100_MACCR_CRC_APD	|
> > +		 FTGMAC100_MACCR_PHY_LINK_LEVEL	|
> > +		 FTGMAC100_MACCR_RX_RUNT	|
> > +		 FTGMAC100_MACCR_RX_BROADPKT;
> > +
> > +	/* Add other bits as needed */
> > +	if (priv->cur_duplex == DUPLEX_FULL)
> > +		maccr |= FTGMAC100_MACCR_FULLDUP;
> > +	if (priv->ndev->flags & IFF_PROMISC)
> > +		maccr |= FTGMAC100_MACCR_RX_ALL;
> > +	if (priv->ndev->flags & IFF_ALLMULTI)
> > +		maccr |= FTGMAC100_MACCR_RX_MULTIPKT;
> > +	else if (netdev_mc_count(priv->ndev))
> > +		maccr |= FTGMAC100_MACCR_HT_MULTI_EN;
> > +
> > +	/* Hit the HW */
> > +	iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
> > +}
> > +
> > +static void ftgmac100_stop_mac(struct ftgmac100 *priv)
> > +{
> > +	iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
> > +}
> >  
> > -		ftgmac100_rx_drop_packet(priv);
> > -		return true;
> > +static int ftgmac100_poll(struct napi_struct *napi, int budget)
> > +{
> > +	struct ftgmac100 *priv = container_of(napi, struct
> > ftgmac100, napi);
> > +	struct net_device *ndev = priv->ndev;
> > +	int work_done = 0;
> > +	bool more;
> > +
> > +	/* Handle Tx packet reclaim */
> > +	if (ftgmac100_tx_buf_cleanable(priv)) {
> > +		while(ftgmac100_tx_buf_cleanable(priv) &&
> > +		      ftgmac100_tx_complete_packet(priv))
> > +			;
> > +		/* Restart queue if needed */
> > +		smp_mb();
> > +		if (unlikely(netif_queue_stopped(ndev) &&
> > +			     ftgmac100_tx_buf_avail(priv) >
> > (MAX_SKB_FRAGS + 1))) {
> > +			struct netdev_queue *txq =
> > netdev_get_tx_queue(ndev, 0);
> > +			__netif_tx_lock(txq, smp_processor_id());
> > +			if (netif_queue_stopped(ndev) &&
> > +			    ftgmac100_tx_buf_avail(priv) >
> > (MAX_SKB_FRAGS + 1))
> > +				netif_wake_queue(ndev);
> > +			__netif_tx_unlock(txq);
> > +		}
> >  	}
> >  
> > -	if (unlikely(ftgmac100_rxdes_multicast(rxdes)))
> > -		netdev->stats.multicast++;
> > +	/* Handle RX packets */
> > +	do
> > +		more = ftgmac100_rx_packet(priv, &work_done);
> > +	while (more && work_done < budget);
> >  
> >  	/*
> > -	 * It seems that HW does checksum incorrectly with
> > fragmented packets,
> > -	 * so we are conservative here - if HW checksum error, let
> > software do
> > -	 * the checksum again.
> > +	 * The interrupt is telling us to kick the MAC back to
> > life
> > +	 * after an RX overflow
> >  	 */
> > -	if ((ftgmac100_rxdes_is_tcp(rxdes) &&
> > !ftgmac100_rxdes_tcpcs_err(rxdes)) ||
> > -	    (ftgmac100_rxdes_is_udp(rxdes) &&
> > !ftgmac100_rxdes_udpcs_err(rxdes)))
> > -		skb->ip_summed = CHECKSUM_UNNECESSARY;
> > -
> > -	do {
> > -		dma_addr_t map =
> > ftgmac100_rxdes_get_dma_addr(rxdes);
> > -		struct page *page = ftgmac100_rxdes_get_page(priv,
> > rxdes);
> > -		unsigned int size;
> > +	if (unlikely(priv->need_mac_restart))
> > +		ftgmac100_start_mac(priv);
> >  
> > -		dma_unmap_page(priv->dev, map, RX_BUF_SIZE,
> > DMA_FROM_DEVICE);
> > +	/*
> > +	 * As long as we are waiting for transmit packets to be
> > +	 * completed we keep NAPI going
> > +	 */
> > +	if (ftgmac100_tx_buf_cleanable(priv))
> > +		work_done = budget;
> >  
> > -		size = ftgmac100_rxdes_data_length(rxdes);
> > -		skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, 
> > page, 0, size);
> > +	/* Are we done ? */
> > +	if (work_done < budget) {
> > +		/* NAPI's over for now */
> > +		napi_complete(napi);
> >  
> > -		skb->len += size;
> > -		skb->data_len += size;
> > -		skb->truesize += PAGE_SIZE;
> > +		/* Enable all interrupts */
> > +		iowrite32(FTGMAC100_INT_ALL, priv->base +
> > FTGMAC100_OFFSET_IER);
> > +	}
> >  
> > -		if (ftgmac100_rxdes_last_segment(rxdes))
> > -			done = true;
> > +	return work_done;
> > +}
> >  
> > -		ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
> > +static void ftgmac100_write_mac_addr(struct ftgmac100 *priv, const
> > u8 *mac)
> > +{
> > +	unsigned int maddr = mac[0] << 8 | mac[1];
> > +	unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4]
> > << 8 | mac[5];
> >  
> > -		ftgmac100_rx_pointer_advance(priv);
> > -		rxdes = ftgmac100_current_rxdes(priv);
> > -	} while (!done);
> > +	iowrite32(maddr, priv->base + FTGMAC100_OFFSET_MAC_MADR);
> > +	iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
> > +}
> >  
> > -	/* Small frames are copied into linear part of skb to free
> > one page */
> > -	if (skb->len <= 128) {
> > -		skb->truesize -= PAGE_SIZE;
> > -		__pskb_pull_tail(skb, skb->len);
> > -	} else {
> > -		/* We pull the minimum amount into linear part */
> > -		__pskb_pull_tail(skb, ETH_HLEN);
> > -	}
> > -	skb->protocol = eth_type_trans(skb, netdev);
> > +static void ftgmac100_config_pause(struct ftgmac100 *priv)
> > +{
> > +	u32 fcr = FTGMAC100_FCR_PAUSE_TIME(16);
> >  
> > -	netdev->stats.rx_packets++;
> > -	netdev->stats.rx_bytes += skb->len;
> > +	/*
> > +	 * Throttle tx queue when receiving pause frames.
> > +	 * XXX Double check with HW vendor the HW bits.
> > +	 */
> > +	if (priv->rx_pause)
> > +		fcr |= FTGMAC100_FCR_FC_EN;
> >  
> > -	/* push packet to protocol stack */
> > -	napi_gro_receive(&priv->napi, skb);
> > +	/*
> > +	 * Enables sending pause frames when the RX queue is past
> > a
> > +	 * certain threshold.
> > +	 * XXX Double check the HW thresholds config...
> > +	 */
> > +	if (priv->tx_pause)
> > +		fcr |= FTGMAC100_FCR_FCTHR_EN;
> >  
> > -	(*processed)++;
> > -	return true;
> > +	iowrite32(fcr, priv->base + FTGMAC100_OFFSET_FCR);
> >  }
> >  
> > -/*****************************************************************
> > *************
> > - * internal functions (transmit descriptor)
> > -
> > *******************************************************************
> > **********/
> > -static void ftgmac100_txdes_reset(const struct ftgmac100 *priv,
> > struct ftgmac100_txdes *txdes)
> > +static void ftgmac100_init_hw(struct ftgmac100 *priv)
> >  {
> > -	/* clear all except end of ring bit */
> > -	txdes->txdes0 &= cpu_to_le32(priv->txdes0_edotr_mask);
> > -	txdes->txdes1 = 0;
> > -	txdes->txdes2 = 0;
> > -	txdes->txdes3 = 0;
> > -}
> > +	u32 reg, rfifo_sz, tfifo_sz;
> >  
> > -static bool ftgmac100_txdes_owned_by_dma(struct ftgmac100_txdes
> > *txdes)
> > -{
> > -	return txdes->txdes0 &
> > cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN);
> > -}
> > +	/* Clear stale interrupts */
> > +	reg = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
> > +	iowrite32(reg, priv->base + FTGMAC100_OFFSET_ISR);
> > +
> > +	/* Setup RX ring buffer base */
> > +	iowrite32(priv->rxdes_dma, priv->base +
> > FTGMAC100_OFFSET_RXR_BADR);
> > +
> > +	/* Setup TX ring buffer base */
> > +	iowrite32(priv->txdes_dma, priv->base +
> > FTGMAC100_OFFSET_NPTXR_BADR);
> > +
> > +	/* Configure RX buffer size */
> > +	iowrite32(FTGMAC100_RBSR_SIZE(RX_BUF_SIZE),
> > +		  priv->base + FTGMAC100_OFFSET_RBSR);
> > +
> > +	/* Set RX descriptor autopoll */
> > +	iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1),
> > +		  priv->base + FTGMAC100_OFFSET_APTC);
> >  
> > -static void ftgmac100_txdes_set_dma_own(struct ftgmac100_txdes
> > *txdes)
> > -{
> >  	/*
> > -	 * Make sure dma own bit will not be set before any other
> > -	 * descriptor fields.
> > +	 * Configure descriptor sizes and increase burst sizes
> > according
> > +	 * to values in Aspeed SDK. The FIFO arbitration is
> > enabled and
> > +	 * the thresholds set based on the recommended values in
> > the
> > +	 * AST2400 specification.
> >  	 */
> > -	wmb();
> > -	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN);
> > -}
> > +	iowrite32(FTGMAC100_DBLAC_RXDES_SIZE(2) |   /* 2*8 bytes
> > RX descs */
> > +		  FTGMAC100_DBLAC_TXDES_SIZE(2) |   /* 2*8 bytes
> > TX descs */
> > +		  FTGMAC100_DBLAC_RXBURST_SIZE(3) | /* 512 bytes
> > max RX bursts */
> > +		  FTGMAC100_DBLAC_TXBURST_SIZE(3) | /* 512 bytes
> > max TX bursts */
> > +		  FTGMAC100_DBLAC_RX_THR_EN |       /* Enable fifo
> > threshold arb */
> > +		  FTGMAC100_DBLAC_RXFIFO_HTHR(6) |  /* 6/8 of FIFO
> > high threshold */
> > +		  FTGMAC100_DBLAC_RXFIFO_LTHR(2),   /* 2/8 of FIFO
> > low threshold */
> > +		  priv->base + FTGMAC100_OFFSET_DBLAC);
> >  
> > -static void ftgmac100_txdes_set_end_of_ring(const struct ftgmac100
> > *priv,
> > -		struct ftgmac100_txdes *txdes)
> > -{
> > -	txdes->txdes0 |= cpu_to_le32(priv->txdes0_edotr_mask);
> > +	/*
> > +	 * Interrupt mitigation configured for 1 interrupt/packet.
> > HW interrupt
> > +	 * mitigation doesn't seem to provide any benefit with
> > NAPI so leave
> > +	 * it at that.
> > +	 */
> > +	iowrite32(FTGMAC100_ITC_RXINT_THR(1) |
> > +		  FTGMAC100_ITC_TXINT_THR(1),
> > +		  priv->base + FTGMAC100_OFFSET_ITC);
> > +
> > +	/* Configure FIFO sizes in the TPAFCR register */
> > +	reg = ioread32(priv->base + FTGMAC100_OFFSET_FEAR);
> > +	rfifo_sz = reg & 0x00000007;
> > +	tfifo_sz = (reg >> 3) & 0x00000007;
> > +	reg = ioread32(priv->base + FTGMAC100_OFFSET_TPAFCR);
> > +	reg &= ~0x3f000000;
> > +	reg |= (tfifo_sz << 27);
> > +	reg |= (rfifo_sz << 24);
> > +	iowrite32(reg, priv->base + FTGMAC100_OFFSET_TPAFCR);
> > +
> > +	/* Write MAC address */
> > +	ftgmac100_write_mac_addr(priv, priv->ndev->dev_addr);
> > +
> > +	/* Write multicast filters */
> > +	iowrite32(priv->maht0, priv->base +
> > FTGMAC100_OFFSET_MAHT0);
> > +	iowrite32(priv->maht1, priv->base +
> > FTGMAC100_OFFSET_MAHT1);
> >  }
> >  
> > -static void ftgmac100_txdes_set_first_segment(struct
> > ftgmac100_txdes *txdes)
> > +static int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr)
> >  {
> > -	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS);
> > +	struct net_device *ndev = priv->ndev;
> > +	int i;
> > +
> > +	/* NOTE: reset clears all registers */
> > +	iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
> > +	iowrite32(maccr | FTGMAC100_MACCR_SW_RST,
> > +		  priv->base + FTGMAC100_OFFSET_MACCR);
> > +	for (i = 0; i < 50; i++) {
> > +		maccr = ioread32(priv->base +
> > FTGMAC100_OFFSET_MACCR);
> > +		if (!(maccr & FTGMAC100_MACCR_SW_RST))
> > +			return 0;
> > +		udelay(100);
> > +	}
> > +
> > +	netdev_err(ndev, "Hardware reset failed\n");
> > +	return -EIO;
> >  }
> >  
> > -static void ftgmac100_txdes_set_last_segment(struct
> > ftgmac100_txdes *txdes)
> > +static int ftgmac100_reset_and_config_mac(struct ftgmac100 *priv)
> >  {
> > -	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS);
> > +	u32 maccr = 0;
> > +
> > +	switch (priv->cur_speed) {
> > +	case SPEED_10:
> > +	case 0: /* no link */
> > +		break;
> > +
> > +	case SPEED_100:
> > +		maccr |= FTGMAC100_MACCR_FAST_MODE;
> > +		break;
> > +
> > +	case SPEED_1000:
> > +		maccr |= FTGMAC100_MACCR_GIGA_MODE;
> > +		break;
> > +	default:
> > +		netdev_err(priv->ndev, "Unknown speed %d !\n",
> > priv->cur_speed);
> > +		break;
> > +	}
> > +
> > +	/* (Re)initialize the queue pointers */
> > +	priv->rx_pointer = 0;
> > +	priv->tx_clean_pointer = 0;
> > +	priv->tx_pointer = 0;
> > +
> > +	/* The doc says reset twice with 10us interval */
> > +	if (ftgmac100_reset_mac(priv, maccr))
> > +		return -EIO;
> > +	udelay(10);
> > +	return ftgmac100_reset_mac(priv, maccr);
> >  }
> >  
> > -static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes
> > *txdes,
> > -					    unsigned int len)
> > +static void ftgmac100_calc_mc_hash(struct ftgmac100 *priv)
> >  {
> > -	txdes->txdes0 |=
> > cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len));
> > +	struct netdev_hw_addr *ha;
> > +
> > +	priv->maht1 = 0;
> > +	priv->maht0 = 0;
> > +	netdev_for_each_mc_addr(ha, priv->ndev) {
> > +		u32 crc_val = ether_crc_le(ETH_ALEN, ha->addr);
> > +		crc_val = (~(crc_val >> 2)) & 0x3f;
> > +		if (crc_val >= 32)
> > +			priv->maht1 |= 1ul << (crc_val - 32);
> > +		else
> > +			priv->maht0 |= 1ul << (crc_val);
> > +	}
> >  }
> >  
> > -static void ftgmac100_txdes_set_txint(struct ftgmac100_txdes
> > *txdes)
> > +static void ftgmac100_set_rx_mode(struct net_device *ndev)
> >  {
> > -	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TXIC);
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +
> > +	/* If we get passed some MC addresses, setup the hash
> > filter */
> > +	if (netdev_mc_count(ndev)) {
> > +		ftgmac100_calc_mc_hash(priv);
> > +		iowrite32(priv->maht0, priv->base +
> > FTGMAC100_OFFSET_MAHT0);
> > +		iowrite32(priv->maht1, priv->base +
> > FTGMAC100_OFFSET_MAHT1);
> > +	}
> > +
> > +	/* Reconfigure MACCR */
> > +	ftgmac100_start_mac(priv);
> >  }
> >  
> > -static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes
> > *txdes)
> > +static int ftgmac100_set_mac_addr(struct net_device *ndev, void
> > *p)
> >  {
> > -	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TCP_CHKSUM);
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +	int ret;
> > +
> > +	ret = eth_prepare_mac_addr_change(ndev, p);
> > +	if (ret < 0)
> > +		return ret;
> > +	ftgmac100_write_mac_addr(priv, p);
> > +	eth_commit_mac_addr_change(ndev, p);
> > +	return 0;
> >  }
> >  
> > -static void ftgmac100_txdes_set_udpcs(struct ftgmac100_txdes
> > *txdes)
> > +static int ftgmac100_do_ioctl(struct net_device *ndev, struct
> > ifreq *ifr,
> > +			      int cmd)
> >  {
> > -	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_UDP_CHKSUM);
> > +	if (!ndev->phydev)
> > +		return -ENXIO;
> > +
> > +	return phy_mii_ioctl(ndev->phydev, ifr, cmd);
> >  }
> >  
> > -static void ftgmac100_txdes_set_ipcs(struct ftgmac100_txdes
> > *txdes)
> > +static void ftgmac100_get_drvinfo(struct net_device *ndev,
> > +				  struct ethtool_drvinfo *info)
> >  {
> > -	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_IP_CHKSUM);
> > +	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
> > +	strlcpy(info->version, DRV_VERSION, sizeof(info-
> > >version));
> > +	strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info-
> > >bus_info));
> >  }
> >  
> > -static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes
> > *txdes,
> > -					 dma_addr_t addr)
> > +static int ftgmac100_nway_reset(struct net_device *ndev)
> >  {
> > -	txdes->txdes3 = cpu_to_le32(addr);
> > +	if (!ndev->phydev)
> > +		return -ENXIO;
> > +	return phy_start_aneg(ndev->phydev);
> >  }
> >  
> > -static dma_addr_t ftgmac100_txdes_get_dma_addr(struct
> > ftgmac100_txdes *txdes)
> > +static void ftgmac100_get_ringparam(struct net_device *ndev,
> > +				    struct ethtool_ringparam
> > *ering)
> >  {
> > -	return le32_to_cpu(txdes->txdes3);
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +
> > +	memset(ering, 0, sizeof(*ering));
> > +	ering->rx_max_pending = MAX_RX_QUEUE_ENTRIES;
> > +	ering->tx_max_pending = MAX_TX_QUEUE_ENTRIES;
> > +	ering->rx_pending = priv->rx_q_entries;
> > +	ering->tx_pending = priv->tx_q_entries;
> >  }
> >  
> > -/*
> > - * txdes2 is not used by hardware. We use it to keep track of
> > socket buffer.
> > - * Since hardware does not touch it, we can skip
> > cpu_to_le32()/le32_to_cpu().
> > - */
> > -static void ftgmac100_txdes_set_skb(struct ftgmac100_txdes *txdes,
> > -				    struct sk_buff *skb)
> > +static int ftgmac100_set_ringparam(struct net_device *ndev,
> > +				   struct ethtool_ringparam
> > *ering)
> >  {
> > -	txdes->txdes2 = (unsigned int)skb;
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +
> > +	if (ering->rx_pending > MAX_RX_QUEUE_ENTRIES ||
> > +	    ering->tx_pending > MAX_TX_QUEUE_ENTRIES ||
> > +	    ering->rx_pending < MIN_RX_QUEUE_ENTRIES ||
> > +	    ering->tx_pending < MIN_TX_QUEUE_ENTRIES ||
> > +	    !is_power_of_2(ering->rx_pending) ||
> > +	    !is_power_of_2(ering->tx_pending))
> > +		return -EINVAL;
> > +
> > +	priv->new_rx_q_entries = ering->rx_pending;
> > +	priv->new_tx_q_entries = ering->tx_pending;
> > +	if (netif_running(ndev))
> > +		schedule_work(&priv->reset_task);
> > +
> > +	return 0;
> >  }
> >  
> > -static struct sk_buff *ftgmac100_txdes_get_skb(struct
> > ftgmac100_txdes *txdes)
> > +static void ftgmac100_get_pauseparam(struct net_device *ndev,
> > +				     struct ethtool_pauseparam
> > *pause)
> >  {
> > -	return (struct sk_buff *)txdes->txdes2;
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +
> > +	pause->autoneg = priv->aneg_pause;
> > +	pause->tx_pause = priv->tx_pause;
> > +	pause->rx_pause = priv->rx_pause;
> >  }
> >  
> > -/*****************************************************************
> > *************
> > - * internal functions (transmit)
> > -
> > *******************************************************************
> > **********/
> > -static int ftgmac100_next_tx_pointer(int pointer)
> > +static int ftgmac100_set_pauseparam(struct net_device *ndev,
> > +				    struct ethtool_pauseparam
> > *pause)
> >  {
> > -	return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +	struct phy_device *phydev = ndev->phydev;
> > +
> > +	priv->aneg_pause = pause->autoneg;
> > +	priv->tx_pause = pause->tx_pause;
> > +	priv->rx_pause = pause->rx_pause;
> > +
> > +	if (phydev) {
> > +		phydev->advertising &= ~ADVERTISED_Pause;
> > +		phydev->advertising &= ~ADVERTISED_Asym_Pause;
> > +
> > +		if (pause->rx_pause) {
> > +			phydev->advertising |= ADVERTISED_Pause;
> > +			phydev->advertising |=
> > ADVERTISED_Asym_Pause;
> > +		}
> > +
> > +		if (pause->tx_pause)
> > +			phydev->advertising ^=
> > ADVERTISED_Asym_Pause;
> > +	}
> > +	if (netif_running(ndev)) {
> > +		if (phydev && priv->aneg_pause)
> > +			phy_start_aneg(phydev);
> > +		else
> > +			ftgmac100_config_pause(priv);
> > +	}
> > +
> > +	return 0;
> >  }
> >  
> > -static void ftgmac100_tx_pointer_advance(struct ftgmac100 *priv)
> > +static const struct ethtool_ops ftgmac100_ethtool_ops = {
> > +	.get_drvinfo		= ftgmac100_get_drvinfo,
> > +	.get_link		= ethtool_op_get_link,
> > +	.get_link_ksettings	=
> > phy_ethtool_get_link_ksettings,
> > +	.set_link_ksettings	=
> > phy_ethtool_set_link_ksettings,
> > +	.nway_reset		= ftgmac100_nway_reset,
> > +	.get_ringparam		= ftgmac100_get_ringparam,
> > +	.set_ringparam		= ftgmac100_set_ringparam,
> > +	.get_pauseparam		= ftgmac100_get_pauseparam,
> > +	.set_pauseparam		= ftgmac100_set_pauseparam,
> > +};
> > +
> > +static void ftgmac100_free_tx_buffers(struct ftgmac100 *priv)
> >  {
> > -	priv->tx_pointer = ftgmac100_next_tx_pointer(priv-
> > >tx_pointer);
> > +	int i;
> > +
> > +	/* Free all tx buffers */
> > +	for (i = 0; i < priv->tx_q_entries; i++) {
> > +		struct ftgmac100_txdes *txdes = &priv->txdes[i];
> > +
> > +		if (!priv->tx_skbs[i])
> > +			continue;
> > +		ftgmac100_free_tx_packet(priv, i, txdes);
> > +	}
> >  }
> >  
> > -static void ftgmac100_tx_clean_pointer_advance(struct ftgmac100
> > *priv)
> > +static void ftgmac100_free_rx_buffers(struct ftgmac100 *priv)
> >  {
> > -	priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv-
> > >tx_clean_pointer);
> > +	int i;
> > +
> > +	/* Free all RX buffers */
> > +	for (i = 0; i < priv->rx_q_entries; i++) {
> > +		struct ftgmac100_rxdes *rxdes = &priv->rxdes[i];
> > +		struct sk_buff *skb = priv->rx_skbs[i];
> > +		dma_addr_t map =
> > ftgmac100_rxdes_get_dma_addr(rxdes);
> > +
> > +		if (!skb)
> > +			continue;
> > +
> > +		priv->rx_skbs[i] = NULL;
> > +		dma_unmap_page(priv->dev, map, RX_BUF_SIZE,
> > DMA_FROM_DEVICE);
> > +		dev_kfree_skb_any(skb);
> > +	}
> >  }
> >  
> > -static struct ftgmac100_txdes *ftgmac100_current_txdes(struct
> > ftgmac100 *priv)
> > +static void ftgmac100_free_descriptors(struct ftgmac100 *priv)
> >  {
> > -	return &priv->descs->txdes[priv->tx_pointer];
> > +	/* Free skb arrays */
> > +	if (priv->rx_skbs)
> > +		kfree(priv->rx_skbs);
> > +	if (priv->tx_skbs)
> > +		kfree(priv->tx_skbs);
> > +
> > +	/* Free descriptor arrays */
> > +	if (priv->rxdes)
> > +		dma_free_coherent(priv->dev, MAX_RX_QUEUE_ENTRIES
> > *
> > +				  sizeof(struct ftgmac100_rxdes),
> > +				  priv->rxdes, priv->rxdes_dma);
> > +	priv->rxdes = NULL;
> > +	if (priv->txdes)
> > +		dma_free_coherent(priv->dev, MAX_TX_QUEUE_ENTRIES
> > *
> > +				  sizeof(struct ftgmac100_txdes),
> > +				  priv->txdes, priv->txdes_dma);
> > +	priv->txdes = NULL;
> > +
> > +	/* Free scratch packet buffer */
> > +	if (priv->rx_scratch)
> > +		dma_free_coherent(priv->dev, RX_BUF_SIZE,
> > +				  priv->rx_scratch, priv-
> > >rx_scratch_dma);
> >  }
> >  
> > -static struct ftgmac100_txdes *
> > -ftgmac100_current_clean_txdes(struct ftgmac100 *priv)
> > +static void ftgmac100_init_descriptors(struct ftgmac100 *priv)
> >  {
> > -	return &priv->descs->txdes[priv->tx_clean_pointer];
> > +	int i;
> > +
> > +	/* Update entries counts */
> > +	priv->rx_q_entries = priv->new_rx_q_entries;
> > +	priv->tx_q_entries = priv->new_tx_q_entries;
> > +
> > +	/*
> > +	 * Clean all rx and tx descriptors and set the end-of-ring
> > +	 * marker on the last entry. For the RX descriptor,
> > populate
> > +	 * all entries with a DMA address pointing to the scratch
> > +	 * page.
> > +	 */
> > +	for (i = 0; i < priv->rx_q_entries; i++) {
> > +		priv->rxdes[i].rxdes0 = 0;
> > +		ftgmac100_rxdes_set_dma_addr(&priv->rxdes[i],
> > priv->rx_scratch_dma);
> > +	}
> > +	priv->rxdes[i - 1].rxdes0 = cpu_to_le32(priv-
> > >rxdes0_edorr_mask);
> > +	for (i = 0; i < priv->tx_q_entries; i++)
> > +		priv->txdes[i].txdes0 = 0;
> > +	priv->txdes[i - 1].txdes0 = cpu_to_le32(priv-
> > >txdes0_edotr_mask);
> >  }
> >  
> > -static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv)
> > +static int ftgmac100_alloc_descriptors(struct ftgmac100 *priv)
> >  {
> > -	struct net_device *netdev = priv->netdev;
> > -	struct ftgmac100_txdes *txdes;
> > -	struct sk_buff *skb;
> > -	dma_addr_t map;
> > +	/* Allocate skb arrays */
> > +	priv->rx_skbs = kzalloc(MAX_RX_QUEUE_ENTRIES * sizeof(void
> > *), GFP_KERNEL);
> > +	if (!priv->rx_skbs)
> > +		return -ENOMEM;
> > +	priv->tx_skbs = kzalloc(MAX_TX_QUEUE_ENTRIES * sizeof(void
> > *), GFP_KERNEL);
> > +	if (!priv->tx_skbs)
> > +		return -ENOMEM;
> >  
> > -	if (priv->tx_pending == 0)
> > -		return false;
> > +	/* Allocate descriptor arrays */
> > +	priv->rxdes = dma_zalloc_coherent(priv->dev,
> > +					  MAX_RX_QUEUE_ENTRIES *
> > +					  sizeof(struct
> > ftgmac100_rxdes),
> > +					  &priv->rxdes_dma,
> > GFP_KERNEL);
> > +	if (!priv->rxdes)
> > +		return -ENOMEM
> > +;	priv->txdes = dma_zalloc_coherent(priv->dev,
> > +					  MAX_TX_QUEUE_ENTRIES *
> > +					  sizeof(struct
> > ftgmac100_txdes),
> > +					  &priv->txdes_dma,
> > GFP_KERNEL);
> > +	if (!priv->txdes)
> > +		return -ENOMEM;
> >  
> > -	txdes = ftgmac100_current_clean_txdes(priv);
> > +	/* Allocate scratch packet buffer */
> > +	priv->rx_scratch = dma_alloc_coherent(priv->dev,
> > +					      RX_BUF_SIZE,
> > +					      &priv-
> > >rx_scratch_dma,
> > +					      GFP_KERNEL);
> > +	if (!priv->rx_scratch)
> > +		return -ENOMEM;
> >  
> > -	if (ftgmac100_txdes_owned_by_dma(txdes))
> > -		return false;
> > +	return 0;
> > +}
> >  
> > -	skb = ftgmac100_txdes_get_skb(txdes);
> > -	map = ftgmac100_txdes_get_dma_addr(txdes);
> > +static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv)
> > +{
> > +	int i;
> >  
> > -	netdev->stats.tx_packets++;
> > -	netdev->stats.tx_bytes += skb->len;
> > +	/* Populate RX ring */
> > +	for (i = 0; i < priv->rx_q_entries; i++) {
> > +		/*
> > +		 * Give up on error, the entries have been pre-
> > populated
> > +		 * with the address of the scratch page
> > +		 */
> > +		if (ftgmac100_alloc_rx_buf(priv, i, GFP_KERNEL))
> > +			return ENOMEM;;
> > +	}
> > +	return 0;
> > +}
> >  
> > -	dma_unmap_single(priv->dev, map, skb_headlen(skb),
> > DMA_TO_DEVICE);
> > +static void ftgmac100_init_all(struct ftgmac100 *priv)
> > +{
> > +	if (!netif_running(priv->ndev))
> > +		return;
> >  
> > -	dev_kfree_skb(skb);
> > +	/* Re-init descriptors (adjust queue sizes) */
> > +	ftgmac100_init_descriptors(priv);
> >  
> > -	ftgmac100_txdes_reset(priv, txdes);
> > +	/* Realloc rx descriptors */
> > +	ftgmac100_alloc_rx_buffers(priv);
> >  
> > -	ftgmac100_tx_clean_pointer_advance(priv);
> > +	/* Reinit and restart HW */
> > +	ftgmac100_init_hw(priv);
> > +	ftgmac100_config_pause(priv);
> > +	ftgmac100_start_mac(priv);
> >  
> > -	spin_lock(&priv->tx_lock);
> > -	priv->tx_pending--;
> > -	spin_unlock(&priv->tx_lock);
> > -	netif_wake_queue(netdev);
> > +	/* Re-enable the device */
> > +	napi_enable(&priv->napi);
> > +	netif_start_queue(priv->ndev);
> >  
> > -	return true;
> > +	/* Enable all interrupts */
> > +	iowrite32(FTGMAC100_INT_ALL, priv->base +
> > FTGMAC100_OFFSET_IER);
> >  }
> >  
> > -static void ftgmac100_tx_complete(struct ftgmac100 *priv)
> > +static int ftgmac100_open(struct net_device *ndev)
> >  {
> > -	while (ftgmac100_tx_complete_packet(priv))
> > -		;
> > -}
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +	int err;
> >  
> > -static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff
> > *skb,
> > -			  dma_addr_t map)
> > -{
> > -	struct net_device *netdev = priv->netdev;
> > -	struct ftgmac100_txdes *txdes;
> > -	unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb-
> > >len;
> > +	/* Clear stale stopping flag */
> > +	priv->stopping = false;
> >  
> > -	txdes = ftgmac100_current_txdes(priv);
> > -	ftgmac100_tx_pointer_advance(priv);
> > +	/* Allocate ring buffers and populate Rx ring */
> > +	err = ftgmac100_alloc_descriptors(priv);
> > +	if (err) {
> > +		netdev_err(ndev, "failed to allocate
> > descriptors\n");
> > +		goto err_alloc;
> > +	}
> >  
> > -	/* setup TX descriptor */
> > -	ftgmac100_txdes_set_skb(txdes, skb);
> > -	ftgmac100_txdes_set_dma_addr(txdes, map);
> > -	ftgmac100_txdes_set_buffer_size(txdes, len);
> > +	/*
> > +	 * When using NC-SI we force the speed to 100Mbit/s full
> > duplex,
> > +	 *
> > +	 * Otherwise we leave it set to 0 (no link), the link
> > +	 * message from the PHY layer will handle setting it up to
> > +	 * something else if needed.
> > +	 */
> > +	if (priv->use_ncsi) {
> > +		priv->cur_duplex = DUPLEX_FULL;
> > +		priv->cur_speed = SPEED_100;
> > +	} else {
> > +		priv->cur_duplex = 0;
> > +		priv->cur_speed = 0;
> > +	}
> >  
> > -	ftgmac100_txdes_set_first_segment(txdes);
> > -	ftgmac100_txdes_set_last_segment(txdes);
> > -	ftgmac100_txdes_set_txint(txdes);
> > -	if (skb->ip_summed == CHECKSUM_PARTIAL) {
> > -		__be16 protocol = skb->protocol;
> > +	/* Reset the hardware */
> > +	err = ftgmac100_reset_and_config_mac(priv);
> > +	if (err)
> > +		goto err_hw;
> >  
> > -		if (protocol == cpu_to_be16(ETH_P_IP)) {
> > -			u8 ip_proto = ip_hdr(skb)->protocol;
> > +	/* Initialize NAPI */
> > +	netif_napi_add(ndev, &priv->napi, ftgmac100_poll,
> > NAPI_POLL_WEIGHT);
> > +
> > +	/* Disable all interrupts */
> > +	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> > +
> > +	/* Grab our interrupt */
> > +	err = request_irq(ndev->irq, ftgmac100_interrupt, 0,
> > +			  ndev->name, ndev);
> > +	if (err) {
> > +		netdev_err(ndev, "failed to request irq %d\n",
> > ndev->irq);
> > +		goto err_irq;
> > +	}
> > +
> > +	/* Start thing up */
> > +	ftgmac100_init_all(priv);
> > +	if (ndev->phydev) {
> > +		/* If we have a PHY, start polling */
> > +		phy_start(ndev->phydev);
> > +	} else if (priv->use_ncsi) {
> > +		/* If using NC-SI, set our carrier on and start
> > the stack */
> > +		netif_carrier_on(ndev);
> > +
> > +		/* Start the NCSI device */
> > +		err = ncsi_start_dev(priv->ncsidev);
> > +		if (err)
> > +			goto err_ncsi;
> > +	}
> > +
> > +	return 0;
> > +
> > + err_ncsi:
> > +	napi_disable(&priv->napi);
> > +	netif_stop_queue(ndev);
> > +	free_irq(ndev->irq, ndev);
> > + err_irq:
> > +	netif_napi_del(&priv->napi);
> > + err_hw:
> > + err_alloc:
> > +	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> > +	ftgmac100_free_tx_buffers(priv);
> > +	ftgmac100_free_rx_buffers(priv);
> > +	ftgmac100_free_descriptors(priv);
> > +	return err;
> > +}
> >  
> > -			ftgmac100_txdes_set_ipcs(txdes);
> > -			if (ip_proto == IPPROTO_TCP)
> > -				ftgmac100_txdes_set_tcpcs(txdes);
> > -			else if (ip_proto == IPPROTO_UDP)
> > -				ftgmac100_txdes_set_udpcs(txdes);
> > -		}
> > -	}
> > +static int ftgmac100_stop(struct net_device *ndev)
> > +{
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> >  
> > -	spin_lock(&priv->tx_lock);
> > -	priv->tx_pending++;
> > -	if (priv->tx_pending == TX_QUEUE_ENTRIES)
> > -		netif_stop_queue(netdev);
> > +	/* Block reset task */
> > +	priv->stopping = true;
> >  
> > -	/* start transmit */
> > -	ftgmac100_txdes_set_dma_own(txdes);
> > -	spin_unlock(&priv->tx_lock);
> > +	/* Kill any pending one */
> > +	cancel_work_sync(&priv->reset_task);
> >  
> > -	ftgmac100_txdma_normal_prio_start_polling(priv);
> > +	/* Disable all interrupts */
> > +	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> >  
> > -	return NETDEV_TX_OK;
> > -}
> > +	/* Stop the PHY or NCSI */
> > +	if (ndev->phydev)
> > +		phy_stop(ndev->phydev);
> > +	else if (priv->use_ncsi)
> > +		ncsi_stop_dev(priv->ncsidev);
> >  
> > -/*****************************************************************
> > *************
> > - * internal functions (buffer)
> > -
> > *******************************************************************
> > **********/
> > -static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
> > -				   struct ftgmac100_rxdes *rxdes,
> > gfp_t gfp)
> > -{
> > -	struct net_device *netdev = priv->netdev;
> > -	struct page *page;
> > -	dma_addr_t map;
> > +	/* Stop the network stack */
> > +	netif_stop_queue(ndev);
> > +	napi_disable(&priv->napi);
> > +	netif_napi_del(&priv->napi);
> >  
> > -	page = alloc_page(gfp);
> > -	if (!page) {
> > -		if (net_ratelimit())
> > -			netdev_err(netdev, "failed to allocate rx
> > page\n");
> > -		return -ENOMEM;
> > -	}
> > +	/* Stop the HW */
> > +	ftgmac100_stop_mac(priv);
> >  
> > -	map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE,
> > DMA_FROM_DEVICE);
> > -	if (unlikely(dma_mapping_error(priv->dev, map))) {
> > -		if (net_ratelimit())
> > -			netdev_err(netdev, "failed to map rx
> > page\n");
> > -		__free_page(page);
> > -		return -ENOMEM;
> > -	}
> > +	/* No more IRQ for us */
> > +	free_irq(ndev->irq, ndev);
> > +
> > +	/* Free everything */
> > +	ftgmac100_free_tx_buffers(priv);
> > +	ftgmac100_free_rx_buffers(priv);
> > +	ftgmac100_free_descriptors(priv);
> >  
> > -	ftgmac100_rxdes_set_page(priv, rxdes, page);
> > -	ftgmac100_rxdes_set_dma_addr(rxdes, map);
> > -	ftgmac100_rxdes_set_dma_own(priv, rxdes);
> >  	return 0;
> >  }
> >  
> > -static void ftgmac100_free_buffers(struct ftgmac100 *priv)
> > +static void ftgmac100_reset_task(struct work_struct *work)
> >  {
> > -	int i;
> > +	struct ftgmac100 *priv = container_of(work, struct
> > ftgmac100, reset_task);
> > +	struct net_device *ndev = priv->ndev;
> > +	int err;
> >  
> > -	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
> > -		struct ftgmac100_rxdes *rxdes = &priv->descs-
> > >rxdes[i];
> > -		struct page *page = ftgmac100_rxdes_get_page(priv,
> > rxdes);
> > -		dma_addr_t map =
> > ftgmac100_rxdes_get_dma_addr(rxdes);
> > +	/* Adapter is going down */
> > +	if (priv->stopping)
> > +		return;
> >  
> > -		if (!page)
> > -			continue;
> > +	netdev_dbg(ndev, "Resetting NIC...\n");
> >  
> > -		dma_unmap_page(priv->dev, map, RX_BUF_SIZE,
> > DMA_FROM_DEVICE);
> > -		__free_page(page);
> > -	}
> > +	/* Block PHY polling */
> > +	if (ndev->phydev)
> > +		mutex_lock(&ndev->phydev->lock);
> >  
> > -	for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
> > -		struct ftgmac100_txdes *txdes = &priv->descs-
> > >txdes[i];
> > -		struct sk_buff *skb =
> > ftgmac100_txdes_get_skb(txdes);
> > -		dma_addr_t map =
> > ftgmac100_txdes_get_dma_addr(txdes);
> > +	rtnl_lock();
> >  
> > -		if (!skb)
> > -			continue;
> > +	/* Check if link state changed again */
> > +	if (priv->cur_speed == 0)
> > +		goto bail;
> > +
> > +	/* Stop the network stack */
> > +	netif_trans_update(ndev);
> > +	napi_disable(&priv->napi);
> > +	netif_tx_disable(ndev);
> >  
> > -		dma_unmap_single(priv->dev, map, skb_headlen(skb),
> > DMA_TO_DEVICE);
> > -		kfree_skb(skb);
> > +	/* Stop and reset the MAC */
> > +	ftgmac100_stop_mac(priv);
> > +	err = ftgmac100_reset_and_config_mac(priv);
> > +	if (err) {
> > +		/* Not much we can do ... it might come back... */
> > +		netdev_err(ndev, "attempting to continue...\n");
> >  	}
> >  
> > -	dma_free_coherent(priv->dev, sizeof(struct
> > ftgmac100_descs),
> > -			  priv->descs, priv->descs_dma_addr);
> > +	/* Free all rx and tx buffers */
> > +	ftgmac100_free_tx_buffers(priv);
> > +	ftgmac100_free_rx_buffers(priv);
> > +
> > +	/* Setup everything and restart chip */
> > +	ftgmac100_init_all(priv);
> > +
> > +	netdev_dbg(ndev, "Reset done !\n");
> > + bail:
> > +	rtnl_unlock();
> > +
> > +	/* Unblock PHY polling */
> > +	if (ndev->phydev)
> > +		mutex_unlock(&ndev->phydev->lock);
> >  }
> >  
> > -static int ftgmac100_alloc_buffers(struct ftgmac100 *priv)
> > +static void ftgmac100_tx_timeout(struct net_device *ndev)
> >  {
> > -	int i;
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> >  
> > -	priv->descs = dma_zalloc_coherent(priv->dev,
> > -					  sizeof(struct
> > ftgmac100_descs),
> > -					  &priv->descs_dma_addr,
> > GFP_KERNEL);
> > -	if (!priv->descs)
> > -		return -ENOMEM;
> > -
> > -	/* initialize RX ring */
> > -	ftgmac100_rxdes_set_end_of_ring(priv,
> > -			&priv->descs->rxdes[RX_QUEUE_ENTRIES -
> > 1]);
> > +	/* Disable all interrupts */
> > +	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> >  
> > -	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
> > -		struct ftgmac100_rxdes *rxdes = &priv->descs-
> > >rxdes[i];
> > +	/* Do the reset outside of interrupt context */
> > +	schedule_work(&priv->reset_task);
> > +}
> >  
> > -		if (ftgmac100_alloc_rx_page(priv, rxdes,
> > GFP_KERNEL))
> > -			goto err;
> > -	}
> > +#ifdef CONFIG_NET_POLL_CONTROLLER
> > +static void ftgmac100_netpoll(struct net_device *ndev)
> > +{
> > +	disable_irq(dev->irq);
> > +	ftgmac100_interrupt(dev->irq, ndev);
> > +	enable_irq(dev->irq);
> > +}
> > +#endif
> >  
> > -	/* initialize TX ring */
> > -	ftgmac100_txdes_set_end_of_ring(priv,
> > -			&priv->descs->txdes[TX_QUEUE_ENTRIES -
> > 1]);
> > -	return 0;
> > +static const struct net_device_ops ftgmac100_netdev_ops = {
> > +	.ndo_open		= ftgmac100_open,
> > +	.ndo_stop		= ftgmac100_stop,
> > +	.ndo_start_xmit		=
> > ftgmac100_hard_start_xmit,
> > +	.ndo_set_mac_address	= ftgmac100_set_mac_addr,
> > +        .ndo_set_rx_mode	= ftgmac100_set_rx_mode,
> > +	.ndo_validate_addr	= eth_validate_addr,
> > +	.ndo_do_ioctl		= ftgmac100_do_ioctl,
> > +	.ndo_tx_timeout		= ftgmac100_tx_timeout,
> > +#ifdef CONFIG_NET_POLL_CONTROLLER
> > +	.ndo_poll_controller	= ftgmac100_poll_controller,
> > +#endif
> > +};
> >  
> > -err:
> > -	ftgmac100_free_buffers(priv);
> > -	return -ENOMEM;
> > +static const char *ftgmac100_fctrl_string(struct ftgmac100 *priv)
> > +{
> > +	if (priv->tx_pause && priv->rx_pause)
> > +		return "rx/tx";
> > +	else if (priv->rx_pause)
> > +		return "rx";
> > +	else if (priv->tx_pause)
> > +		return "tx";
> > +	else
> > +		return "no";
> >  }
> >  
> > -/*****************************************************************
> > *************
> > - * internal functions (mdio)
> > -
> > *******************************************************************
> > **********/
> > -static void ftgmac100_adjust_link(struct net_device *netdev)
> > +static void ftgmac100_adjust_link(struct net_device *ndev)
> >  {
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > -	struct phy_device *phydev = netdev->phydev;
> > -	int ier;
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> > +	struct phy_device *phydev = ndev->phydev;
> > +	bool tx_pause, rx_pause;
> > +
> > +	/* Link is down */
> > +	if (!phydev->link) {
> > +		if (priv->cur_speed)
> > +			netdev_info(ndev, "Link down\n");
> > +		priv->cur_speed = 0;
> >  
> > -	if (phydev->speed == priv->old_speed)
> > +		/*
> > +		 * We just stop the MAC, we'll reset the adapter
> > +		 * if/when the link comes back up
> > +		 */
> > +		ftgmac100_stop_mac(priv);
> >  		return;
> > +	}
> > +
> > +	/* Grab pause settings from PHY if configured to do so */
> > +	if (priv->aneg_pause) {
> > +		rx_pause = tx_pause = phydev->pause;
> > +		if (phydev->asym_pause)
> > +			tx_pause = !rx_pause;
> > +	} else {
> > +		rx_pause = priv->rx_pause;
> > +		tx_pause = priv->tx_pause;
> > +	}
> >  
> > -	priv->old_speed = phydev->speed;
> > +	/* Link hasn't changed, do nothing */
> > +	if (phydev->speed == priv->cur_speed &&
> > +	    phydev->duplex == priv->cur_duplex &&
> > +	    rx_pause == priv->rx_pause &&
> > +	    tx_pause == priv->tx_pause)
> > +		return;
> >  
> > -	ier = ioread32(priv->base + FTGMAC100_OFFSET_IER);
> > +	priv->cur_speed = phydev->speed;
> > +	priv->cur_duplex = phydev->duplex;
> > +	priv->rx_pause = rx_pause;
> > +	priv->tx_pause = tx_pause;
> >  
> > -	/* disable all interrupts */
> > -	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> > +	netdev_info(ndev, "Link up at %d Mbit/s %s duplex %s flow
> > ctrl\n",
> > +		    priv->cur_speed,
> > +		    phydev->duplex == DUPLEX_FULL ? "full" :
> > "half",
> > +		    ftgmac100_fctrl_string(priv));
> >  
> > -	netif_stop_queue(netdev);
> > -	ftgmac100_stop_hw(priv);
> >  
> > -	netif_start_queue(netdev);
> > -	ftgmac100_init_hw(priv);
> > -	ftgmac100_start_hw(priv, phydev->speed);
> > +	/* Disable all interrupts */
> > +	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> >  
> > -	/* re-enable interrupts */
> > -	iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER);
> > +	/* Reset the adapter asynchronously */
> > +	schedule_work(&priv->reset_task);
> >  }
> >  
> >  static int ftgmac100_mii_probe(struct ftgmac100 *priv)
> >  {
> > -	struct net_device *netdev = priv->netdev;
> > +	struct net_device *ndev = priv->ndev;
> >  	struct phy_device *phydev;
> >  
> >  	phydev = phy_find_first(priv->mii_bus);
> >  	if (!phydev) {
> > -		netdev_info(netdev, "%s: no PHY found\n", netdev-
> > >name);
> > +		netdev_info(ndev, "%s: no PHY found\n", ndev-
> > >name);
> >  		return -ENODEV;
> >  	}
> >  
> > -	phydev = phy_connect(netdev, phydev_name(phydev),
> > +	phydev = phy_connect(ndev, phydev_name(phydev),
> >  			     &ftgmac100_adjust_link,
> > PHY_INTERFACE_MODE_GMII);
> >  
> >  	if (IS_ERR(phydev)) {
> > -		netdev_err(netdev, "%s: Could not attach to
> > PHY\n", netdev->name);
> > +		netdev_err(ndev, "%s: Could not attach to PHY\n",
> > ndev->name);
> >  		return PTR_ERR(phydev);
> >  	}
> >  
> >  	return 0;
> >  }
> >  
> > -/*****************************************************************
> > *************
> > - * struct mii_bus functions
> > -
> > *******************************************************************
> > **********/
> > -static int ftgmac100_mdiobus_read(struct mii_bus *bus, int
> > phy_addr, int regnum)
> > +static int ftgmac100_mii_read(struct mii_bus *bus, int phy_addr,
> > int regnum)
> >  {
> > -	struct net_device *netdev = bus->priv;
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > +	struct net_device *ndev = bus->priv;
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> >  	unsigned int phycr;
> >  	int i;
> >  
> > @@ -951,15 +1438,15 @@ static int ftgmac100_mdiobus_read(struct
> > mii_bus *bus, int phy_addr, int regnum)
> >  		udelay(100);
> >  	}
> >  
> > -	netdev_err(netdev, "mdio read timed out\n");
> > +	netdev_err(ndev, "mdio read timed out\n");
> >  	return -EIO;
> >  }
> >  
> > -static int ftgmac100_mdiobus_write(struct mii_bus *bus, int
> > phy_addr,
> > -				   int regnum, u16 value)
> > +static int ftgmac100_mii_write(struct mii_bus *bus, int phy_addr,
> > +			       int regnum, u16 value)
> >  {
> > -	struct net_device *netdev = bus->priv;
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > +	struct net_device *ndev = bus->priv;
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> >  	unsigned int phycr;
> >  	int data;
> >  	int i;
> > @@ -987,268 +1474,13 @@ static int ftgmac100_mdiobus_write(struct
> > mii_bus *bus, int phy_addr,
> >  		udelay(100);
> >  	}
> >  
> > -	netdev_err(netdev, "mdio write timed out\n");
> > +	netdev_err(ndev, "mdio write timed out\n");
> >  	return -EIO;
> >  }
> >  
> > -/*****************************************************************
> > *************
> > - * struct ethtool_ops functions
> > -
> > *******************************************************************
> > **********/
> > -static void ftgmac100_get_drvinfo(struct net_device *netdev,
> > -				  struct ethtool_drvinfo *info)
> > -{
> > -	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
> > -	strlcpy(info->version, DRV_VERSION, sizeof(info-
> > >version));
> > -	strlcpy(info->bus_info, dev_name(&netdev->dev),
> > sizeof(info->bus_info));
> > -}
> > -
> > -static const struct ethtool_ops ftgmac100_ethtool_ops = {
> > -	.get_drvinfo		= ftgmac100_get_drvinfo,
> > -	.get_link		= ethtool_op_get_link,
> > -	.get_link_ksettings	=
> > phy_ethtool_get_link_ksettings,
> > -	.set_link_ksettings	=
> > phy_ethtool_set_link_ksettings,
> > -};
> > -
> > -/*****************************************************************
> > *************
> > - * interrupt handler
> > -
> > *******************************************************************
> > **********/
> > -static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
> > -{
> > -	struct net_device *netdev = dev_id;
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > -
> > -	/* When running in NCSI mode, the interface should be
> > ready for
> > -	 * receiving or transmitting NCSI packets before it's
> > opened.
> > -	 */
> > -	if (likely(priv->use_ncsi || netif_running(netdev))) {
> > -		/* Disable interrupts for polling */
> > -		iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> > -		napi_schedule(&priv->napi);
> > -	}
> > -
> > -	return IRQ_HANDLED;
> > -}
> > -
> > -/*****************************************************************
> > *************
> > - * struct napi_struct functions
> > -
> > *******************************************************************
> > **********/
> > -static int ftgmac100_poll(struct napi_struct *napi, int budget)
> > -{
> > -	struct ftgmac100 *priv = container_of(napi, struct
> > ftgmac100, napi);
> > -	struct net_device *netdev = priv->netdev;
> > -	unsigned int status;
> > -	bool completed = true;
> > -	int rx = 0;
> > -
> > -	status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
> > -	iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
> > -
> > -	if (status & (FTGMAC100_INT_RPKT_BUF |
> > FTGMAC100_INT_NO_RXBUF)) {
> > -		/*
> > -		 * FTGMAC100_INT_RPKT_BUF:
> > -		 *	RX DMA has received packets into RX
> > buffer successfully
> > -		 *
> > -		 * FTGMAC100_INT_NO_RXBUF:
> > -		 *	RX buffer unavailable
> > -		 */
> > -		bool retry;
> > -
> > -		do {
> > -			retry = ftgmac100_rx_packet(priv, &rx);
> > -		} while (retry && rx < budget);
> > -
> > -		if (retry && rx == budget)
> > -			completed = false;
> > -	}
> > -
> > -	if (status & (FTGMAC100_INT_XPKT_ETH |
> > FTGMAC100_INT_XPKT_LOST)) {
> > -		/*
> > -		 * FTGMAC100_INT_XPKT_ETH:
> > -		 *	packet transmitted to ethernet
> > successfully
> > -		 *
> > -		 * FTGMAC100_INT_XPKT_LOST:
> > -		 *	packet transmitted to ethernet lost due
> > to late
> > -		 *	collision or excessive collision
> > -		 */
> > -		ftgmac100_tx_complete(priv);
> > -	}
> > -
> > -	if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF
> > |
> > -			FTGMAC100_INT_RPKT_LOST |
> > FTGMAC100_INT_AHB_ERR)) {
> > -		if (net_ratelimit())
> > -			netdev_info(netdev, "[ISR] = 0x%x:
> > %s%s%s\n", status,
> > -				    status &
> > FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
> > -				    status &
> > FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "",
> > -				    status & FTGMAC100_INT_AHB_ERR
> > ? "AHB_ERR " : "");
> > -
> > -		if (status & FTGMAC100_INT_NO_RXBUF) {
> > -			/* RX buffer unavailable */
> > -			netdev->stats.rx_over_errors++;
> > -		}
> > -
> > -		if (status & FTGMAC100_INT_RPKT_LOST) {
> > -			/* received packet lost due to RX FIFO
> > full */
> > -			netdev->stats.rx_fifo_errors++;
> > -		}
> > -	}
> > -
> > -	if (completed) {
> > -		napi_complete(napi);
> > -
> > -		/* enable all interrupts */
> > -		iowrite32(priv->int_mask_all,
> > -			  priv->base + FTGMAC100_OFFSET_IER);
> > -	}
> > -
> > -	return rx;
> > -}
> > -
> > -/*****************************************************************
> > *************
> > - * struct net_device_ops functions
> > -
> > *******************************************************************
> > **********/
> > -static int ftgmac100_open(struct net_device *netdev)
> > -{
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > -	unsigned int status;
> > -	int err;
> > -
> > -	err = ftgmac100_alloc_buffers(priv);
> > -	if (err) {
> > -		netdev_err(netdev, "failed to allocate
> > buffers\n");
> > -		goto err_alloc;
> > -	}
> > -
> > -	err = request_irq(priv->irq, ftgmac100_interrupt, 0,
> > netdev->name, netdev);
> > -	if (err) {
> > -		netdev_err(netdev, "failed to request irq %d\n",
> > priv->irq);
> > -		goto err_irq;
> > -	}
> > -
> > -	priv->rx_pointer = 0;
> > -	priv->tx_clean_pointer = 0;
> > -	priv->tx_pointer = 0;
> > -	priv->tx_pending = 0;
> > -
> > -	err = ftgmac100_reset_hw(priv);
> > -	if (err)
> > -		goto err_hw;
> > -
> > -	ftgmac100_init_hw(priv);
> > -	ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10);
> > -
> > -	/* Clear stale interrupts */
> > -	status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
> > -	iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
> > -
> > -	if (netdev->phydev)
> > -		phy_start(netdev->phydev);
> > -	else if (priv->use_ncsi)
> > -		netif_carrier_on(netdev);
> > -
> > -	napi_enable(&priv->napi);
> > -	netif_start_queue(netdev);
> > -
> > -	/* enable all interrupts */
> > -	iowrite32(priv->int_mask_all, priv->base +
> > FTGMAC100_OFFSET_IER);
> > -
> > -	/* Start the NCSI device */
> > -	if (priv->use_ncsi) {
> > -		err = ncsi_start_dev(priv->ndev);
> > -		if (err)
> > -			goto err_ncsi;
> > -	}
> > -
> > -	priv->enabled = true;
> > -
> > -	return 0;
> > -
> > -err_ncsi:
> > -	napi_disable(&priv->napi);
> > -	netif_stop_queue(netdev);
> > -	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> > -err_hw:
> > -	free_irq(priv->irq, netdev);
> > -err_irq:
> > -	ftgmac100_free_buffers(priv);
> > -err_alloc:
> > -	return err;
> > -}
> > -
> > -static int ftgmac100_stop(struct net_device *netdev)
> > -{
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > -
> > -	if (!priv->enabled)
> > -		return 0;
> > -
> > -	/* disable all interrupts */
> > -	priv->enabled = false;
> > -	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
> > -
> > -	netif_stop_queue(netdev);
> > -	napi_disable(&priv->napi);
> > -	if (netdev->phydev)
> > -		phy_stop(netdev->phydev);
> > -	else if (priv->use_ncsi)
> > -		ncsi_stop_dev(priv->ndev);
> > -
> > -	ftgmac100_stop_hw(priv);
> > -	free_irq(priv->irq, netdev);
> > -	ftgmac100_free_buffers(priv);
> > -
> > -	return 0;
> > -}
> > -
> > -static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
> > -				     struct net_device *netdev)
> > -{
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > -	dma_addr_t map;
> > -
> > -	if (unlikely(skb->len > MAX_PKT_SIZE)) {
> > -		if (net_ratelimit())
> > -			netdev_dbg(netdev, "tx packet too big\n");
> > -
> > -		netdev->stats.tx_dropped++;
> > -		kfree_skb(skb);
> > -		return NETDEV_TX_OK;
> > -	}
> > -
> > -	map = dma_map_single(priv->dev, skb->data,
> > skb_headlen(skb), DMA_TO_DEVICE);
> > -	if (unlikely(dma_mapping_error(priv->dev, map))) {
> > -		/* drop packet */
> > -		if (net_ratelimit())
> > -			netdev_err(netdev, "map socket buffer
> > failed\n");
> > -
> > -		netdev->stats.tx_dropped++;
> > -		kfree_skb(skb);
> > -		return NETDEV_TX_OK;
> > -	}
> > -
> > -	return ftgmac100_xmit(priv, skb, map);
> > -}
> > -
> > -/* optional */
> > -static int ftgmac100_do_ioctl(struct net_device *netdev, struct
> > ifreq *ifr, int cmd)
> > -{
> > -	if (!netdev->phydev)
> > -		return -ENXIO;
> > -
> > -	return phy_mii_ioctl(netdev->phydev, ifr, cmd);
> > -}
> > -
> > -static const struct net_device_ops ftgmac100_netdev_ops = {
> > -	.ndo_open		= ftgmac100_open,
> > -	.ndo_stop		= ftgmac100_stop,
> > -	.ndo_start_xmit		=
> > ftgmac100_hard_start_xmit,
> > -	.ndo_set_mac_address	= ftgmac100_set_mac_addr,
> > -	.ndo_validate_addr	= eth_validate_addr,
> > -	.ndo_do_ioctl		= ftgmac100_do_ioctl,
> > -};
> > -
> > -static int ftgmac100_setup_mdio(struct net_device *netdev)
> > +static int ftgmac100_setup_mdio(struct net_device *ndev)
> >  {
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> >  	struct platform_device *pdev = to_platform_device(priv-
> > >dev);
> >  	int i, err = 0;
> >  	u32 reg;
> > @@ -1258,8 +1490,9 @@ static int ftgmac100_setup_mdio(struct
> > net_device *netdev)
> >  	if (!priv->mii_bus)
> >  		return -EIO;
> >  
> > -	if (of_machine_is_compatible("aspeed,ast2400") ||
> > -	    of_machine_is_compatible("aspeed,ast2500")) {
> > +	if (pdev->dev.of_node &&
> > +	    (of_device_is_compatible(pdev->dev.of_node,
> > "aspeed,ast2400-mac") ||
> > +	     of_device_is_compatible(pdev->dev.of_node,
> > "aspeed,ast2500-mac"))) {
> >  		/* This driver supports the old MDIO interface */
> >  		reg = ioread32(priv->base +
> > FTGMAC100_OFFSET_REVR);
> >  		reg &= ~FTGMAC100_REVR_NEW_MDIO_INTERFACE;
> > @@ -1269,9 +1502,9 @@ static int ftgmac100_setup_mdio(struct
> > net_device *netdev)
> >  	priv->mii_bus->name = "ftgmac100_mdio";
> >  	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d",
> >  		 pdev->name, pdev->id);
> > -	priv->mii_bus->priv = priv->netdev;
> > -	priv->mii_bus->read = ftgmac100_mdiobus_read;
> > -	priv->mii_bus->write = ftgmac100_mdiobus_write;
> > +	priv->mii_bus->priv = priv->ndev;
> > +	priv->mii_bus->read = ftgmac100_mii_read;
> > +	priv->mii_bus->write = ftgmac100_mii_write;
> >  
> >  	for (i = 0; i < PHY_MAX_ADDR; i++)
> >  		priv->mii_bus->irq[i] = PHY_POLL;
> > @@ -1297,14 +1530,14 @@ err_register_mdiobus:
> >  	return err;
> >  }
> >  
> > -static void ftgmac100_destroy_mdio(struct net_device *netdev)
> > +static void ftgmac100_destroy_mdio(struct net_device *ndev)
> >  {
> > -	struct ftgmac100 *priv = netdev_priv(netdev);
> > +	struct ftgmac100 *priv = netdev_priv(ndev);
> >  
> > -	if (!netdev->phydev)
> > +	if (!ndev->phydev)
> >  		return;
> >  
> > -	phy_disconnect(netdev->phydev);
> > +	phy_disconnect(ndev->phydev);
> >  	mdiobus_unregister(priv->mii_bus);
> >  	mdiobus_free(priv->mii_bus);
> >  }
> > @@ -1318,16 +1551,48 @@ static void ftgmac100_ncsi_handler(struct
> > ncsi_dev *nd)
> >  		    nd->link_up ? "up" : "down");
> >  }
> >  
> > -/*****************************************************************
> > *************
> > - * struct platform_driver functions
> > -
> > *******************************************************************
> > **********/
> > +static void ftgmac100_initial_mac(struct ftgmac100 *priv)
> > +{
> > +	u8 mac[ETH_ALEN];
> > +	unsigned int m;
> > +	unsigned int l;
> > +	void *addr;
> > +
> > +	addr = device_get_mac_address(priv->dev, mac, ETH_ALEN);
> > +	if (addr) {
> > +		ether_addr_copy(priv->ndev->dev_addr, mac);
> > +		dev_info(priv->dev, "Read MAC address %pM from
> > device tree\n",
> > +			 mac);
> > +		return;
> > +	}
> > +
> > +	m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
> > +	l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
> > +
> > +	mac[0] = (m >> 8) & 0xff;
> > +	mac[1] = m & 0xff;
> > +	mac[2] = (l >> 24) & 0xff;
> > +	mac[3] = (l >> 16) & 0xff;
> > +	mac[4] = (l >> 8) & 0xff;
> > +	mac[5] = l & 0xff;
> > +
> > +	if (is_valid_ether_addr(mac)) {
> > +		ether_addr_copy(priv->ndev->dev_addr, mac);
> > +		dev_info(priv->dev, "Read MAC address %pM from
> > chip\n", mac);
> > +	} else {
> > +		eth_hw_addr_random(priv->ndev);
> > +		dev_info(priv->dev, "Generated random MAC address
> > %pM\n",
> > +			 priv->ndev->dev_addr);
> > +	}
> > +}
> > +
> >  static int ftgmac100_probe(struct platform_device *pdev)
> >  {
> >  	struct resource *res;
> > -	int irq;
> > -	struct net_device *netdev;
> > +	struct net_device *ndev;
> >  	struct ftgmac100 *priv;
> > -	int err = 0;
> > +	struct device_node *np;
> > +	int irq, err = 0;
> >  
> >  	if (!pdev)
> >  		return -ENODEV;
> > @@ -1341,26 +1606,31 @@ static int ftgmac100_probe(struct
> > platform_device *pdev)
> >  		return irq;
> >  
> >  	/* setup net_device */
> > -	netdev = alloc_etherdev(sizeof(*priv));
> > -	if (!netdev) {
> > +	ndev = alloc_etherdev(sizeof(*priv));
> > +	if (!ndev) {
> >  		err = -ENOMEM;
> >  		goto err_alloc_etherdev;
> >  	}
> >  
> > -	SET_NETDEV_DEV(netdev, &pdev->dev);
> > +	SET_NETDEV_DEV(ndev, &pdev->dev);
> >  
> > -	netdev->ethtool_ops = &ftgmac100_ethtool_ops;
> > -	netdev->netdev_ops = &ftgmac100_netdev_ops;
> > +	ndev->ethtool_ops = &ftgmac100_ethtool_ops;
> > +	ndev->netdev_ops = &ftgmac100_netdev_ops;
> > +	ndev->irq = irq;
> >  
> > -	platform_set_drvdata(pdev, netdev);
> > +	platform_set_drvdata(pdev, ndev);
> >  
> >  	/* setup private data */
> > -	priv = netdev_priv(netdev);
> > -	priv->netdev = netdev;
> > +	priv = netdev_priv(ndev);
> > +	priv->ndev = ndev;
> >  	priv->dev = &pdev->dev;
> > +	priv->maht1 = 0;
> > +	priv->maht0 = 0;
> > +	INIT_WORK(&priv->reset_task, ftgmac100_reset_task);
> >  
> > -	if (of_machine_is_compatible("aspeed,ast2400") ||
> > -	    of_machine_is_compatible("aspeed,ast2500")) {
> > +	np = pdev->dev.of_node;
> > +	if (np && (of_device_is_compatible(np, "aspeed,ast2400-
> > mac") ||
> > +		   of_device_is_compatible(np, "aspeed,ast2500-
> > mac"))) {
> >  		priv->rxdes0_edorr_mask = BIT(30);
> >  		priv->txdes0_edotr_mask = BIT(30);
> >  	} else {
> > @@ -1368,11 +1638,6 @@ static int ftgmac100_probe(struct
> > platform_device *pdev)
> >  		priv->txdes0_edotr_mask = BIT(15);
> >  	}
> >  
> > -	spin_lock_init(&priv->tx_lock);
> > -
> > -	/* initialize NAPI */
> > -	netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
> > -
> >  	/* map io memory */
> >  	priv->res = request_mem_region(res->start,
> > resource_size(res),
> >  				       dev_name(&pdev->dev));
> > @@ -1389,19 +1654,15 @@ static int ftgmac100_probe(struct
> > platform_device *pdev)
> >  		goto err_ioremap;
> >  	}
> >  
> > -	priv->irq = irq;
> > +	/* Enable pause */
> > +	priv->tx_pause = true;
> > +	priv->rx_pause = true;
> > +	priv->aneg_pause = true;
> >  
> >  	/* MAC address from chip or random one */
> > -	ftgmac100_setup_mac(priv);
> > -
> > -	priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST |
> > -			      FTGMAC100_INT_XPKT_ETH |
> > -			      FTGMAC100_INT_XPKT_LOST |
> > -			      FTGMAC100_INT_AHB_ERR |
> > -			      FTGMAC100_INT_RPKT_BUF |
> > -			      FTGMAC100_INT_NO_RXBUF);
> > -	if (pdev->dev.of_node &&
> > -	    of_get_property(pdev->dev.of_node, "use-ncsi", NULL))
> > {
> > +	ftgmac100_initial_mac(priv);
> > +
> > +	if (np && of_get_property(np, "use-ncsi", NULL)) {
> >  		if (!IS_ENABLED(CONFIG_NET_NCSI)) {
> >  			dev_err(&pdev->dev, "NCSI stack not
> > enabled\n");
> >  			goto err_ncsi_dev;
> > @@ -1409,67 +1670,81 @@ static int ftgmac100_probe(struct
> > platform_device *pdev)
> >  
> >  		dev_info(&pdev->dev, "Using NCSI interface\n");
> >  		priv->use_ncsi = true;
> > -		priv->ndev = ncsi_register_dev(netdev,
> > ftgmac100_ncsi_handler);
> > +		priv->ncsidev = ncsi_register_dev(ndev,
> > ftgmac100_ncsi_handler);
> >  		if (!priv->ndev)
> >  			goto err_ncsi_dev;
> >  	} else {
> >  		priv->use_ncsi = false;
> > -		err = ftgmac100_setup_mdio(netdev);
> > +		err = ftgmac100_setup_mdio(ndev);
> >  		if (err)
> >  			goto err_setup_mdio;
> >  	}
> >  
> > -	/* We have to disable on-chip IP checksum functionality
> > -	 * when NCSI is enabled on the interface. It doesn't work
> > -	 * in that case.
> > -	 */
> > -	netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
> > -	if (priv->use_ncsi &&
> > -	    of_get_property(pdev->dev.of_node, "no-hw-checksum",
> > NULL))
> > -		netdev->features &= ~NETIF_F_IP_CSUM;
> > +	/* Default ring sizes */
> > +	priv->rx_q_entries = priv->new_rx_q_entries =
> > DEF_RX_QUEUE_ENTRIES;
> > +	priv->tx_q_entries = priv->new_tx_q_entries =
> > DEF_TX_QUEUE_ENTRIES;
> >  
> > +	/* Setup feature set */
> > +	ndev->hw_features = NETIF_F_RXCSUM | NETIF_F_GRO |
> > NETIF_F_SG;
> > +	if (np && of_device_is_compatible(np, "aspeed,ast2500-
> > mac"))
> > +		ndev->hw_features |= NETIF_F_IP_CSUM;
> > +	if (np && of_get_property(np, "no-hw-checksum", NULL))
> > +		ndev->hw_features &= ~(NETIF_F_IP_CSUM |
> > NETIF_F_RXCSUM);
> > +	ndev->features |= ndev->hw_features;
> >  
> >  	/* register network device */
> > -	err = register_netdev(netdev);
> > +	err = register_netdev(ndev);
> >  	if (err) {
> >  		dev_err(&pdev->dev, "Failed to register
> > netdev\n");
> >  		goto err_register_netdev;
> >  	}
> >  
> > -	netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq,
> > priv->base);
> > +	netdev_info(ndev, "irq %d, mapped at %p\n", ndev->irq,
> > priv->base);
> >  
> >  	return 0;
> >  
> >  err_ncsi_dev:
> >  err_register_netdev:
> > -	ftgmac100_destroy_mdio(netdev);
> > +	ftgmac100_destroy_mdio(ndev);
> >  err_setup_mdio:
> >  	iounmap(priv->base);
> >  err_ioremap:
> >  	release_resource(priv->res);
> >  err_req_mem:
> > -	netif_napi_del(&priv->napi);
> > -	free_netdev(netdev);
> > +	free_netdev(ndev);
> >  err_alloc_etherdev:
> >  	return err;
> >  }
> >  
> >  static int __exit ftgmac100_remove(struct platform_device *pdev)
> >  {
> > -	struct net_device *netdev;
> > +	struct net_device *ndev;
> >  	struct ftgmac100 *priv;
> >  
> > -	netdev = platform_get_drvdata(pdev);
> > -	priv = netdev_priv(netdev);
> > +	ndev = platform_get_drvdata(pdev);
> > +	priv = netdev_priv(ndev);
> > +
> > +	/* Preent the reset task from kicking early on */
> > +	priv->stopping = true;
> >  
> > -	unregister_netdev(netdev);
> > -	ftgmac100_destroy_mdio(netdev);
> > +	/*
> > +	 * Close & unregister the netdevice, at this points
> > +	 * the interrupt will be disabled
> > +	 */
> > +	unregister_netdev(ndev);
> > +
> > +	/*
> > +	 * There's a small chance the reset task will have been
> > re-queued,
> > +	 * make sure it's gone before we free the structure
> > +	 */
> > +	/* Kill any pending one */
> > +	cancel_work_sync(&priv->reset_task);
> > +
> > +	ftgmac100_destroy_mdio(ndev);
> >  
> >  	iounmap(priv->base);
> >  	release_resource(priv->res);
> > -
> > -	netif_napi_del(&priv->napi);
> > -	free_netdev(netdev);
> > +	free_netdev(ndev);
> >  	return 0;
> >  }
> >  
> > diff --git a/drivers/net/ethernet/faraday/ftgmac100.h
> > b/drivers/net/ethernet/faraday/ftgmac100.h
> > index a7ce0ac..dd41763 100644
> > --- a/drivers/net/ethernet/faraday/ftgmac100.h
> > +++ b/drivers/net/ethernet/faraday/ftgmac100.h
> > @@ -86,6 +86,19 @@
> >  #define FTGMAC100_INT_PHYSTS_CHG	(1 << 9)
> >  #define FTGMAC100_INT_NO_HPTXBUF	(1 << 10)
> >  
> > +/* These are all the interrupts we care about */
> > +#define FTGMAC100_INT_ALL (FTGMAC100_INT_RPKT_LOST |	\
> > +			   FTGMAC100_INT_XPKT_ETH |	\
> > +			   FTGMAC100_INT_XPKT_LOST |	\
> > +			   FTGMAC100_INT_AHB_ERR |	\
> > +			   FTGMAC100_INT_RPKT_BUF |	\
> > +			   FTGMAC100_INT_NO_RXBUF)
> > +
> > +/* These are the interrupts we care about in NAPI mode */
> > +#define FTGMAC100_INT_BAD (FTGMAC100_INT_RPKT_LOST |	\
> > +			   FTGMAC100_INT_AHB_ERR |	\
> > +			   FTGMAC100_INT_NO_RXBUF)
> > +
> >  /*
> >   * Interrupt timer control register
> >   */
> > @@ -185,6 +198,13 @@
> >  #define FTGMAC100_PHYDATA_MIIRDATA(phydata)	(((phydata) >>
> > 16) & 0xffff)
> >  
> >  /*
> > + * Flow control register
> > + */
> > +#define FTGMAC100_FCR_FC_EN		(1 << 0)
> > +#define FTGMAC100_FCR_FCTHR_EN		(1 << 2)
> > +#define FTGMAC100_FCR_PAUSE_TIME(x)	(((x) & 0xffff) << 16)
> > +
> > +/*
> >   * Transmit descriptor, aligned to 16 bytes
> >   */
> >  struct ftgmac100_txdes {
> > @@ -209,6 +229,79 @@ struct ftgmac100_txdes {
> >  #define FTGMAC100_TXDES1_TX2FIC		(1 << 30)
> >  #define FTGMAC100_TXDES1_TXIC		(1 << 31)
> >  
> > +static inline bool ftgmac100_txdes_owned_by_dma(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	return txdes->txdes0 &
> > cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN);
> > +}
> > +
> > +static inline void ftgmac100_txdes_set_dma_own(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN);
> > +}
> > +
> > +static inline void ftgmac100_txdes_set_first_segment(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS);
> > +}
> > +
> > +static inline bool ftgmac100_txdes_get_first_segment(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	return (txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_FTS))
> > != 0;
> > +}
> > +
> > +static inline void ftgmac100_txdes_set_last_segment(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS);
> > +}
> > +
> > +static inline bool ftgmac100_txdes_get_last_segment(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	return (txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_LTS))
> > != 0;
> > +}
> > +
> > +static inline void ftgmac100_txdes_set_buffer_size(struct
> > ftgmac100_txdes *txdes,
> > +					    unsigned int len)
> > +{
> > +	txdes->txdes0 |=
> > cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len));
> > +}
> > +
> > +static inline unsigned int ftgmac100_txdes_get_buffer_size(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	return FTGMAC100_TXDES0_TXBUF_SIZE(cpu_to_le32(txdes-
> > >txdes0));
> > +}
> > +
> > +
> > +static inline void ftgmac100_txdes_set_txint(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TXIC);
> > +}
> > +
> > +static inline void ftgmac100_txdes_set_tcpcs(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TCP_CHKSUM);
> > +}
> > +
> > +static inline void ftgmac100_txdes_set_udpcs(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_UDP_CHKSUM);
> > +}
> > +
> > +static inline void ftgmac100_txdes_set_ipcs(struct ftgmac100_txdes
> > *txdes)
> > +{
> > +	txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_IP_CHKSUM);
> > +}
> > +
> > +static inline void ftgmac100_txdes_set_dma_addr(struct
> > ftgmac100_txdes *txdes,
> > +						dma_addr_t addr)
> > +{
> > +	txdes->txdes3 = cpu_to_le32(addr);
> > +}
> > +
> > +static inline dma_addr_t ftgmac100_txdes_get_dma_addr(struct
> > ftgmac100_txdes *txdes)
> > +{
> > +	return le32_to_cpu(txdes->txdes3);
> > +}
> > +
> >  /*
> >   * Receive descriptor, aligned to 16 bytes
> >   */
> > @@ -235,11 +328,11 @@ struct ftgmac100_rxdes {
> >  #define FTGMAC100_RXDES0_RXPKT_RDY	(1 << 31)
> >  
> >  #define FTGMAC100_RXDES1_VLANTAG_CI	0xffff
> > -#define FTGMAC100_RXDES1_PROT_MASK	(0x3 << 20)
> > -#define FTGMAC100_RXDES1_PROT_NONIP	(0x0 << 20)
> > -#define FTGMAC100_RXDES1_PROT_IP	(0x1 << 20)
> > -#define FTGMAC100_RXDES1_PROT_TCPIP	(0x2 << 20)
> > -#define FTGMAC100_RXDES1_PROT_UDPIP	(0x3 << 20)
> > +#define FTGMAC100_RXDES1_PROT(x)	(((x) >> 20) & 3)
> > +#define   FTGMAC100_PROT_NONIP	0
> > +#define   FTGMAC100_PROT_IP	1
> > +#define   FTGMAC100_PROT_TCPIP	2
> > +#define   FTGMAC100_PROT_UDPIP	3
> >  #define FTGMAC100_RXDES1_LLC		(1 << 22)
> >  #define FTGMAC100_RXDES1_DF		(1 << 23)
> >  #define FTGMAC100_RXDES1_VLANTAG_AVAIL	(1 << 24)
> > @@ -247,4 +340,84 @@ struct ftgmac100_rxdes {
> >  #define FTGMAC100_RXDES1_UDP_CHKSUM_ERR	(1 << 26)
> >  #define FTGMAC100_RXDES1_IP_CHKSUM_ERR	(1 << 27)
> >  
> > +static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes
> > *rxdes)
> > +{
> > +	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS);
> > +}
> > +
> > +static bool ftgmac100_rxdes_last_segment(struct ftgmac100_rxdes
> > *rxdes)
> > +{
> > +	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_LRS);
> > +}
> > +
> > +static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes
> > *rxdes)
> > +{
> > +	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY);
> > +}
> > +
> > +#define RXDES0_ANY_ERROR		  \
> > +	FTGMAC100_RXDES0_RX_ERR		| \
> > +	FTGMAC100_RXDES0_CRC_ERR	| \
> > +	FTGMAC100_RXDES0_FTL		| \
> > +	FTGMAC100_RXDES0_RUNT		| \
> > +	FTGMAC100_RXDES0_RX_ODD_NB
> > +
> > +static inline bool ftgmac100_rxdes_any_error(struct
> > ftgmac100_rxdes *rxdes)
> > +{
> > +	return rxdes->rxdes0 & cpu_to_le32(RXDES0_ANY_ERROR);
> > +}
> > +
> > +static inline bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes
> > *rxdes)
> > +{
> > +	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_RX_ERR);
> > +}
> > +
> > +static inline bool ftgmac100_rxdes_crc_error(struct
> > ftgmac100_rxdes *rxdes)
> > +{
> > +	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_CRC_ERR);
> > +}
> > +
> > +static inline bool ftgmac100_rxdes_frame_too_long(struct
> > ftgmac100_rxdes *rxdes)
> > +{
> > +	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FTL);
> > +}
> > +
> > +static inline bool ftgmac100_rxdes_runt(struct ftgmac100_rxdes
> > *rxdes)
> > +{
> > +	return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RUNT);
> > +}
> > +
> > +static inline bool ftgmac100_rxdes_odd_nibble(struct
> > ftgmac100_rxdes *rxdes)
> > +{
> > +	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_RX_ODD_NB);
> > +}
> > +
> > +static inline unsigned int ftgmac100_rxdes_data_length(struct
> > ftgmac100_rxdes *rxdes)
> > +{
> > +	return le32_to_cpu(rxdes->rxdes0) & FTGMAC100_RXDES0_VDBC;
> > +}
> > +
> > +static inline bool ftgmac100_rxdes_multicast(struct
> > ftgmac100_rxdes *rxdes)
> > +{
> > +	return rxdes->rxdes0 &
> > cpu_to_le32(FTGMAC100_RXDES0_MULTICAST);
> > +}
> > +
> > +static inline void ftgmac100_rxdes_set_dma_addr(struct
> > ftgmac100_rxdes *rxdes,
> > +					 dma_addr_t addr)
> > +{
> > +	rxdes->rxdes3 = cpu_to_le32(addr);
> > +}
> > +
> > +static inline dma_addr_t ftgmac100_rxdes_get_dma_addr(struct
> > ftgmac100_rxdes *rxdes)
> > +{
> > +	return le32_to_cpu(rxdes->rxdes3);
> > +}
> > +
> > +static inline bool ftgmac100_rxdes_csum_err(struct ftgmac100_rxdes
> > *rxdes)
> > +{
> > +	return !!(rxdes->rxdes1 &
> > cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR |
> > +					      FTGMAC100_RXDES1_UDP
> > _CHKSUM_ERR |
> > +					      FTGMAC100_RXDES1_IP_
> > CHKSUM_ERR));
> > +}
> > +
> >  #endif /* __FTGMAC100_H */
> > 


More information about the openbmc mailing list