[PATCH] gianfar: Add support enhanced TSEC features on the MPC 8548

Jeff,

Just incase this got lost in the recent netdev mailing list transition
here is a nicer version of Andy's patch for gianfar.

- kumar

* TCP/IP/UDP checksumming and verification
* VLAN tag insertion/extraction
* Larger multicast hash-table
* Padding to align IP headers

Also added:
* msg lvl support
* Some whitespace cleanup

Signed-off-by: Andy Fleming <afleming@freescale.com>
Signed-off-by: Kumar Gala <kumar.gala@freescale.com>
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index b43b2b1..6518334 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -1,4 +1,4 @@
-/* 
+/*
  * drivers/net/gianfar.c
  *
  * Gianfar Ethernet Driver
@@ -22,10 +22,9 @@
  *  B-V +1.62
  *
  *  Theory of operation
- *  This driver is designed for the Triple-speed Ethernet
- *  controllers on the Freescale 8540/8560 integrated processors,
- *  as well as the Fast Ethernet Controller on the 8540.  
- *  
+ *  This driver is designed for the non-CPM ethernet controllers
+ *  on the 85xx and 83xx family of integrated processors
+ *
  *  The driver is initialized through platform_device.  Structures which
  *  define the configuration needed by the board are defined in a
  *  board structure in arch/ppc/platforms (though I do not
@@ -39,12 +38,12 @@
  *
  *  The Gianfar Ethernet Controller uses a ring of buffer
  *  descriptors.  The beginning is indicated by a register
- *  pointing to the physical address of the start of the ring. 
- *  The end is determined by a "wrap" bit being set in the 
+ *  pointing to the physical address of the start of the ring.
+ *  The end is determined by a "wrap" bit being set in the
  *  last descriptor of the ring.
  *
  *  When a packet is received, the RXF bit in the
- *  IEVENT register is set, triggering an interrupt when the 
+ *  IEVENT register is set, triggering an interrupt when the
  *  corresponding bit in the IMASK register is also set (if
  *  interrupt coalescing is active, then the interrupt may not
  *  happen immediately, but will wait until either a set number
@@ -52,7 +51,7 @@
  *  interrupt handler will signal there is work to be done, and
  *  exit.  Without NAPI, the packet(s) will be handled
  *  immediately.  Both methods will start at the last known empty
- *  descriptor, and process every subsequent descriptor until there 
+ *  descriptor, and process every subsequent descriptor until there
  *  are none left with data (NAPI will stop after a set number of
  *  packets to give time to other tasks, but will eventually
  *  process all the packets).  The data arrives inside a
@@ -83,9 +82,13 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/if_vlan.h>
 #include <linux/spinlock.h>
 #include <linux/mm.h>
 #include <linux/device.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -123,7 +126,7 @@
 static int gfar_change_mtu(struct net_device *dev, int new_mtu);
 static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs);
 static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs);
-irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
 static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void gfar_phy_change(void *data);
@@ -139,9 +142,12 @@
 #ifdef CONFIG_GFAR_NAPI
 static int gfar_poll(struct net_device *dev, int *budget);
 #endif
-static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
+int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
 static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
 static void gfar_phy_startup_timer(unsigned long data);
+static void gfar_vlan_rx_register(struct net_device *netdev,
+		                struct vlan_group *grp);
+static void gfar_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
 
 extern struct ethtool_ops gfar_ethtool_ops;
 
@@ -149,6 +155,13 @@
 MODULE_DESCRIPTION("Gianfar Ethernet Driver");
 MODULE_LICENSE("GPL");
 
+int gfar_uses_fcb(struct gfar_private *priv)
+{
+	if (priv->vlan_enable || priv->rx_csum_enable)
+		return 1;
+	else
+		return 0;
+}
 static int gfar_probe(struct device *device)
 {
 	u32 tempval;
@@ -159,7 +172,6 @@
 	struct resource *r;
 	int idx;
 	int err = 0;
-	int dev_ethtool_ops = 0;
 
 	einfo = (struct gianfar_platform_data *) pdev->dev.platform_data;
 
@@ -265,15 +277,69 @@
 	dev->mtu = 1500;
 	dev->set_multicast_list = gfar_set_multi;
 
-	/* Index into the array of possible ethtool
-	 * ops to catch all 4 possibilities */
-	if((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) == 0)
-		dev_ethtool_ops += 1;
+	dev->ethtool_ops = &gfar_ethtool_ops;
 
-	if((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE) == 0)
-		dev_ethtool_ops += 2;
+	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
+		priv->rx_csum_enable = 1;
+		dev->features |= NETIF_F_IP_CSUM;
+	} else
+		priv->rx_csum_enable = 0;
 
-	dev->ethtool_ops = gfar_op_array[dev_ethtool_ops];
+	priv->vlgrp = NULL;
+
+	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
+		dev->vlan_rx_register = gfar_vlan_rx_register;
+		dev->vlan_rx_kill_vid = gfar_vlan_rx_kill_vid;
+
+		dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+
+		priv->vlan_enable = 1;
+	}
+
+	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
+		priv->extended_hash = 1;
+		priv->hash_width = 9;
+
+		priv->hash_regs[0] = &priv->regs->igaddr0;
+		priv->hash_regs[1] = &priv->regs->igaddr1;
+		priv->hash_regs[2] = &priv->regs->igaddr2;
+		priv->hash_regs[3] = &priv->regs->igaddr3;
+		priv->hash_regs[4] = &priv->regs->igaddr4;
+		priv->hash_regs[5] = &priv->regs->igaddr5;
+		priv->hash_regs[6] = &priv->regs->igaddr6;
+		priv->hash_regs[7] = &priv->regs->igaddr7;
+		priv->hash_regs[8] = &priv->regs->gaddr0;
+		priv->hash_regs[9] = &priv->regs->gaddr1;
+		priv->hash_regs[10] = &priv->regs->gaddr2;
+		priv->hash_regs[11] = &priv->regs->gaddr3;
+		priv->hash_regs[12] = &priv->regs->gaddr4;
+		priv->hash_regs[13] = &priv->regs->gaddr5;
+		priv->hash_regs[14] = &priv->regs->gaddr6;
+		priv->hash_regs[15] = &priv->regs->gaddr7;
+
+	} else {
+		priv->extended_hash = 0;
+		priv->hash_width = 8;
+
+		priv->hash_regs[0] = &priv->regs->gaddr0;
+                priv->hash_regs[1] = &priv->regs->gaddr1;
+		priv->hash_regs[2] = &priv->regs->gaddr2;
+		priv->hash_regs[3] = &priv->regs->gaddr3;
+		priv->hash_regs[4] = &priv->regs->gaddr4;
+		priv->hash_regs[5] = &priv->regs->gaddr5;
+		priv->hash_regs[6] = &priv->regs->gaddr6;
+		priv->hash_regs[7] = &priv->regs->gaddr7;
+	}
+
+	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_PADDING)
+		priv->padding = DEFAULT_PADDING;
+	else
+		priv->padding = 0;
+
+	dev->hard_header_len += priv->padding;
+
+	if (dev->features & NETIF_F_IP_CSUM)
+		dev->hard_header_len += GMAC_FCB_LEN;
 
 	priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
 #ifdef CONFIG_GFAR_BUFSTASH
@@ -289,6 +355,9 @@
 	priv->rxcount = DEFAULT_RXCOUNT;
 	priv->rxtime = DEFAULT_RXTIME;
 
+	/* Enable most messages by default */
+	priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
+
 	err = register_netdev(dev);
 
 	if (err) {
@@ -360,8 +429,9 @@
 			GFP_KERNEL);
 
 	if(NULL == mii_info) {
-		printk(KERN_ERR "%s: Could not allocate mii_info\n", 
-				dev->name);
+		if (netif_msg_ifup(priv))
+			printk(KERN_ERR "%s: Could not allocate mii_info\n",
+					dev->name);
 		return -ENOMEM;
 	}
 
@@ -410,7 +480,8 @@
 	curphy = get_phy_info(priv->mii_info);
 
 	if (curphy == NULL) {
-		printk(KERN_ERR "%s: No PHY found\n", dev->name);
+		if (netif_msg_ifup(priv))
+			printk(KERN_ERR "%s: No PHY found\n", dev->name);
 		err = -1;
 		goto no_phy;
 	}
@@ -421,7 +492,7 @@
 	if(curphy->init) {
 		err = curphy->init(priv->mii_info);
 
-		if (err) 
+		if (err)
 			goto phy_init_fail;
 	}
 
@@ -446,14 +517,14 @@
 	gfar_write(&priv->regs->imask, IMASK_INIT_CLEAR);
 
 	/* Init hash registers to zero */
-	gfar_write(&priv->regs->iaddr0, 0);
-	gfar_write(&priv->regs->iaddr1, 0);
-	gfar_write(&priv->regs->iaddr2, 0);
-	gfar_write(&priv->regs->iaddr3, 0);
-	gfar_write(&priv->regs->iaddr4, 0);
-	gfar_write(&priv->regs->iaddr5, 0);
-	gfar_write(&priv->regs->iaddr6, 0);
-	gfar_write(&priv->regs->iaddr7, 0);
+	gfar_write(&priv->regs->igaddr0, 0);
+	gfar_write(&priv->regs->igaddr1, 0);
+	gfar_write(&priv->regs->igaddr2, 0);
+	gfar_write(&priv->regs->igaddr3, 0);
+	gfar_write(&priv->regs->igaddr4, 0);
+	gfar_write(&priv->regs->igaddr5, 0);
+	gfar_write(&priv->regs->igaddr6, 0);
+	gfar_write(&priv->regs->igaddr7, 0);
 
 	gfar_write(&priv->regs->gaddr0, 0);
 	gfar_write(&priv->regs->gaddr1, 0);
@@ -464,9 +535,6 @@
 	gfar_write(&priv->regs->gaddr6, 0);
 	gfar_write(&priv->regs->gaddr7, 0);
 
-	/* Zero out rctrl */
-	gfar_write(&priv->regs->rctrl, 0x00000000);
-
 	/* Zero out the rmon mib registers if it has them */
 	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
 		memset((void *) &(priv->regs->rmon), 0,
@@ -497,20 +565,14 @@
 	gfar_write(&priv->regs->tbipa, TBIPA_VALUE);
 }
 
-void stop_gfar(struct net_device *dev)
+
+/* Halt the receive and transmit queues */
+void gfar_halt(struct net_device *dev)
 {
 	struct gfar_private *priv = netdev_priv(dev);
 	struct gfar *regs = priv->regs;
-	unsigned long flags;
 	u32 tempval;
 
-	/* Lock it down */
-	spin_lock_irqsave(&priv->lock, flags);
-
-	/* Tell the kernel the link is down */
-	priv->mii_info->link = 0;
-	adjust_link(dev);
-
 	/* Mask all interrupts */
 	gfar_write(&regs->imask, IMASK_INIT_CLEAR);
 
@@ -533,13 +595,29 @@
 	tempval = gfar_read(&regs->maccfg1);
 	tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN);
 	gfar_write(&regs->maccfg1, tempval);
+}
+
+void stop_gfar(struct net_device *dev)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	struct gfar *regs = priv->regs;
+	unsigned long flags;
+
+	/* Lock it down */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* Tell the kernel the link is down */
+	priv->mii_info->link = 0;
+	adjust_link(dev);
+
+	gfar_halt(dev);
 
 	if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) {
 		/* Clear any pending interrupts */
 		mii_clear_phy_interrupt(priv->mii_info);
 
 		/* Disable PHY Interrupts */
-		mii_configure_phy_interrupt(priv->mii_info, 
+		mii_configure_phy_interrupt(priv->mii_info,
 				MII_INTERRUPT_DISABLED);
 	}
 
@@ -566,7 +644,7 @@
 			sizeof(struct txbd8)*priv->tx_ring_size
 			+ sizeof(struct rxbd8)*priv->rx_ring_size,
 			priv->tx_bd_base,
-			gfar_read(&regs->tbase));
+			gfar_read(&regs->tbase0));
 }
 
 /* If there are any tx skbs or rx skbs still around, free them.
@@ -620,6 +698,34 @@
 	}
 }
 
+void gfar_start(struct net_device *dev)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	struct gfar *regs = priv->regs;
+	u32 tempval;
+
+	/* Enable Rx and Tx in MACCFG1 */
+	tempval = gfar_read(&regs->maccfg1);
+	tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
+	gfar_write(&regs->maccfg1, tempval);
+
+	/* Initialize DMACTRL to have WWR and WOP */
+	tempval = gfar_read(&priv->regs->dmactrl);
+	tempval |= DMACTRL_INIT_SETTINGS;
+	gfar_write(&priv->regs->dmactrl, tempval);
+
+	/* Clear THLT, so that the DMA starts polling now */
+	gfar_write(&regs->tstat, TSTAT_CLEAR_THALT);
+
+	/* Make sure we aren't stopped */
+	tempval = gfar_read(&priv->regs->dmactrl);
+	tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
+	gfar_write(&priv->regs->dmactrl, tempval);
+
+	/* Unmask the interrupts we look for */
+	gfar_write(&regs->imask, IMASK_DEFAULT);
+}
+
 /* Bring the controller up and running */
 int startup_gfar(struct net_device *dev)
 {
@@ -630,33 +736,34 @@
 	int i;
 	struct gfar_private *priv = netdev_priv(dev);
 	struct gfar *regs = priv->regs;
-	u32 tempval;
 	int err = 0;
+	u32 rctrl = 0;
 
 	gfar_write(&regs->imask, IMASK_INIT_CLEAR);
 
 	/* Allocate memory for the buffer descriptors */
-	vaddr = (unsigned long) dma_alloc_coherent(NULL, 
+	vaddr = (unsigned long) dma_alloc_coherent(NULL,
 			sizeof (struct txbd8) * priv->tx_ring_size +
 			sizeof (struct rxbd8) * priv->rx_ring_size,
 			&addr, GFP_KERNEL);
 
 	if (vaddr == 0) {
-		printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
-		       dev->name);
+		if (netif_msg_ifup(priv))
+			printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
+					dev->name);
 		return -ENOMEM;
 	}
 
 	priv->tx_bd_base = (struct txbd8 *) vaddr;
 
 	/* enet DMA only understands physical addresses */
-	gfar_write(&regs->tbase, addr);
+	gfar_write(&regs->tbase0, addr);
 
 	/* Start the rx descriptor ring where the tx ring leaves off */
 	addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
 	vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size;
 	priv->rx_bd_base = (struct rxbd8 *) vaddr;
-	gfar_write(&regs->rbase, addr);
+	gfar_write(&regs->rbase0, addr);
 
 	/* Setup the skbuff rings */
 	priv->tx_skbuff =
@@ -664,8 +771,9 @@
 					priv->tx_ring_size, GFP_KERNEL);
 
 	if (priv->tx_skbuff == NULL) {
-		printk(KERN_ERR "%s: Could not allocate tx_skbuff\n",
-		       dev->name);
+		if (netif_msg_ifup(priv))
+			printk(KERN_ERR "%s: Could not allocate tx_skbuff\n",
+					dev->name);
 		err = -ENOMEM;
 		goto tx_skb_fail;
 	}
@@ -678,8 +786,9 @@
 					priv->rx_ring_size, GFP_KERNEL);
 
 	if (priv->rx_skbuff == NULL) {
-		printk(KERN_ERR "%s: Could not allocate rx_skbuff\n",
-		       dev->name);
+		if (netif_msg_ifup(priv))
+			printk(KERN_ERR "%s: Could not allocate rx_skbuff\n",
+					dev->name);
 		err = -ENOMEM;
 		goto rx_skb_fail;
 	}
@@ -726,12 +835,13 @@
 	/* If the device has multiple interrupts, register for
 	 * them.  Otherwise, only register for the one */
 	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
-		/* Install our interrupt handlers for Error, 
+		/* Install our interrupt handlers for Error,
 		 * Transmit, and Receive */
 		if (request_irq(priv->interruptError, gfar_error,
 				0, "enet_error", dev) < 0) {
-			printk(KERN_ERR "%s: Can't get IRQ %d\n",
-			       dev->name, priv->interruptError);
+			if (netif_msg_intr(priv))
+				printk(KERN_ERR "%s: Can't get IRQ %d\n",
+					dev->name, priv->interruptError);
 
 			err = -1;
 			goto err_irq_fail;
@@ -739,8 +849,9 @@
 
 		if (request_irq(priv->interruptTransmit, gfar_transmit,
 				0, "enet_tx", dev) < 0) {
-			printk(KERN_ERR "%s: Can't get IRQ %d\n",
-			       dev->name, priv->interruptTransmit);
+			if (netif_msg_intr(priv))
+				printk(KERN_ERR "%s: Can't get IRQ %d\n",
+					dev->name, priv->interruptTransmit);
 
 			err = -1;
 
@@ -749,8 +860,9 @@
 
 		if (request_irq(priv->interruptReceive, gfar_receive,
 				0, "enet_rx", dev) < 0) {
-			printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n",
-			       dev->name, priv->interruptReceive);
+			if (netif_msg_intr(priv))
+				printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n",
+						dev->name, priv->interruptReceive);
 
 			err = -1;
 			goto rx_irq_fail;
@@ -758,8 +870,9 @@
 	} else {
 		if (request_irq(priv->interruptTransmit, gfar_interrupt,
 				0, "gfar_interrupt", dev) < 0) {
-			printk(KERN_ERR "%s: Can't get IRQ %d\n",
-			       dev->name, priv->interruptError);
+			if (netif_msg_intr(priv))
+				printk(KERN_ERR "%s: Can't get IRQ %d\n",
+					dev->name, priv->interruptError);
 
 			err = -1;
 			goto err_irq_fail;
@@ -787,28 +900,22 @@
 	else
 		gfar_write(&regs->rxic, 0);
 
-	init_waitqueue_head(&priv->rxcleanupq);
+	if (priv->rx_csum_enable)
+		rctrl |= RCTRL_CHECKSUMMING;
 
-	/* Enable Rx and Tx in MACCFG1 */
-	tempval = gfar_read(&regs->maccfg1);
-	tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
-	gfar_write(&regs->maccfg1, tempval);
+	if (priv->extended_hash)
+		rctrl |= RCTRL_EXTHASH;
 
-	/* Initialize DMACTRL to have WWR and WOP */
-	tempval = gfar_read(&priv->regs->dmactrl);
-	tempval |= DMACTRL_INIT_SETTINGS;
-	gfar_write(&priv->regs->dmactrl, tempval);
+	if (priv->vlan_enable)
+		rctrl |= RCTRL_VLAN;
 
-	/* Clear THLT, so that the DMA starts polling now */
-	gfar_write(&regs->tstat, TSTAT_CLEAR_THALT);
+	/* Init rctrl based on our settings */
+	gfar_write(&priv->regs->rctrl, rctrl);
 
-	/* Make sure we aren't stopped */
-	tempval = gfar_read(&priv->regs->dmactrl);
-	tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
-	gfar_write(&priv->regs->dmactrl, tempval);
+	if (dev->features & NETIF_F_IP_CSUM)
+		gfar_write(&priv->regs->tctrl, TCTRL_INIT_CSUM);
 
-	/* Unmask the interrupts we look for */
-	gfar_write(&regs->imask, IMASK_DEFAULT);
+	gfar_start(dev);
 
 	return 0;
 
@@ -824,7 +931,7 @@
 			sizeof(struct txbd8)*priv->tx_ring_size
 			+ sizeof(struct rxbd8)*priv->rx_ring_size,
 			priv->tx_bd_base,
-			gfar_read(&regs->tbase));
+			gfar_read(&regs->tbase0));
 
 	if (priv->mii_info->phyinfo->close)
 		priv->mii_info->phyinfo->close(priv->mii_info);
@@ -857,11 +964,62 @@
 	return err;
 }
 
+static struct txfcb *gfar_add_fcb(struct sk_buff *skb, struct txbd8 *bdp)
+{
+	struct txfcb *fcb = (struct txfcb *)skb_push (skb, GMAC_FCB_LEN);
+
+	memset(fcb, 0, GMAC_FCB_LEN);
+
+	/* Flag the bd so the controller looks for the FCB */
+	bdp->status |= TXBD_TOE;
+
+	return fcb;
+}
+
+static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb)
+{
+	int len;
+
+	/* If we're here, it's a IP packet with a TCP or UDP
+	 * payload.  We set it to checksum, using a pseudo-header
+	 * we provide
+	 */
+	fcb->ip = 1;
+	fcb->tup = 1;
+	fcb->ctu = 1;
+	fcb->nph = 1;
+
+	/* Notify the controller what the protocol is */
+	if (skb->nh.iph->protocol == IPPROTO_UDP)
+		fcb->udp = 1;
+
+	/* l3os is the distance between the start of the
+	 * frame (skb->data) and the start of the IP hdr.
+	 * l4os is the distance between the start of the
+	 * l3 hdr and the l4 hdr */
+	fcb->l3os = (u16)(skb->nh.raw - skb->data - GMAC_FCB_LEN);
+	fcb->l4os = (u16)(skb->h.raw - skb->nh.raw);
+
+	len = skb->nh.iph->tot_len - fcb->l4os;
+
+	/* Provide the pseudoheader csum */
+	fcb->phcs = ~csum_tcpudp_magic(skb->nh.iph->saddr,
+			skb->nh.iph->daddr, len,
+			skb->nh.iph->protocol, 0);
+}
+
+void gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb)
+{
+	fcb->vln = 1;
+	fcb->vlctl = vlan_tx_tag_get(skb);
+}
+
 /* This is called by the kernel when a frame is ready for transmission. */
 /* It is pointed to by the dev->hard_start_xmit function pointer */
 static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct gfar_private *priv = netdev_priv(dev);
+	struct txfcb *fcb = NULL;
 	struct txbd8 *txbdp;
 
 	/* Update transmit stats */
@@ -876,9 +1034,24 @@
 	/* Clear all but the WRAP status flags */
 	txbdp->status &= TXBD_WRAP;
 
+	/* Set up checksumming */
+	if ((dev->features & NETIF_F_IP_CSUM) 
+			&& (CHECKSUM_HW == skb->ip_summed)) {
+		fcb = gfar_add_fcb(skb, txbdp);
+		gfar_tx_checksum(skb, fcb);
+	}
+
+	if (priv->vlan_enable &&
+			unlikely(priv->vlgrp && vlan_tx_tag_present(skb))) {
+		if (NULL == fcb)
+			fcb = gfar_add_fcb(skb, txbdp);
+
+		gfar_tx_vlan(skb, fcb);
+	}
+
 	/* Set buffer length and pointer */
 	txbdp->length = skb->len;
-	txbdp->bufPtr = dma_map_single(NULL, skb->data, 
+	txbdp->bufPtr = dma_map_single(NULL, skb->data,
 			skb->len, DMA_TO_DEVICE);
 
 	/* Save the skb pointer so we can free it later */
@@ -972,15 +1145,78 @@
 }
 
 
+/* Enables and disables VLAN insertion/extraction */
+static void gfar_vlan_rx_register(struct net_device *dev,
+		struct vlan_group *grp)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	unsigned long flags;
+	u32 tempval;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	priv->vlgrp = grp;
+
+	if (grp) {
+		/* Enable VLAN tag insertion */
+		tempval = gfar_read(&priv->regs->tctrl);
+		tempval |= TCTRL_VLINS;
+
+		gfar_write(&priv->regs->tctrl, tempval);
+		
+		/* Enable VLAN tag extraction */
+		tempval = gfar_read(&priv->regs->rctrl);
+		tempval |= RCTRL_VLEX;
+		gfar_write(&priv->regs->rctrl, tempval);
+	} else {
+		/* Disable VLAN tag insertion */
+		tempval = gfar_read(&priv->regs->tctrl);
+		tempval &= ~TCTRL_VLINS;
+		gfar_write(&priv->regs->tctrl, tempval);
+
+		/* Disable VLAN tag extraction */
+		tempval = gfar_read(&priv->regs->rctrl);
+		tempval &= ~RCTRL_VLEX;
+		gfar_write(&priv->regs->rctrl, tempval);
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+
+static void gfar_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (priv->vlgrp)
+		priv->vlgrp->vlan_devices[vid] = NULL;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+
 static int gfar_change_mtu(struct net_device *dev, int new_mtu)
 {
 	int tempsize, tempval;
 	struct gfar_private *priv = netdev_priv(dev);
 	int oldsize = priv->rx_buffer_size;
-	int frame_size = new_mtu + 18;
+	int frame_size = new_mtu + ETH_HLEN;
+
+	if (priv->vlan_enable)
+		frame_size += VLAN_ETH_HLEN;
+
+	if (gfar_uses_fcb(priv))
+		frame_size += GMAC_FCB_LEN;
+
+	frame_size += priv->padding;
 
 	if ((frame_size < 64) || (frame_size > JUMBO_FRAME_SIZE)) {
-		printk(KERN_ERR "%s: Invalid MTU setting\n", dev->name);
+		if (netif_msg_drv(priv))
+			printk(KERN_ERR "%s: Invalid MTU setting\n",
+					dev->name);
 		return -EINVAL;
 	}
 
@@ -1120,7 +1356,7 @@
 	skb->dev = dev;
 
 	bdp->bufPtr = dma_map_single(NULL, skb->data,
-			priv->rx_buffer_size + RXBUF_ALIGNMENT, 
+			priv->rx_buffer_size + RXBUF_ALIGNMENT,
 			DMA_FROM_DEVICE);
 
 	bdp->length = 0;
@@ -1190,11 +1426,10 @@
 
 		__netif_rx_schedule(dev);
 	} else {
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: receive called twice (%x)[%x]\n",
-		       dev->name, gfar_read(&priv->regs->ievent),
-		       gfar_read(&priv->regs->imask));
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_DEBUG "%s: receive called twice (%x)[%x]\n",
+				dev->name, gfar_read(&priv->regs->ievent),
+				gfar_read(&priv->regs->imask));
 	}
 #else
 
@@ -1209,15 +1444,43 @@
 	else
 		gfar_write(&priv->regs->rxic, 0);
 
-	/* Just in case we need to wake the ring param changer */
-	priv->rxclean = 1;
-
 	spin_unlock(&priv->lock);
 #endif
 
 	return IRQ_HANDLED;
 }
 
+static inline int gfar_rx_vlan(struct sk_buff *skb,
+		struct vlan_group *vlgrp, unsigned short vlctl)
+{
+#ifdef CONFIG_GFAR_NAPI
+	return vlan_hwaccel_receive_skb(skb, vlgrp, vlctl);
+#else
+	return vlan_hwaccel_rx(skb, vlgrp, vlctl);
+#endif
+}
+
+static inline void gfar_rx_checksum(struct sk_buff *skb, struct rxfcb *fcb)
+{
+	/* If valid headers were found, and valid sums
+	 * were verified, then we tell the kernel that no
+	 * checksumming is necessary.  Otherwise, it is */
+	if (fcb->cip && !fcb->eip && fcb->ctu && !fcb->etu)
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	else
+		skb->ip_summed = CHECKSUM_NONE;
+}
+
+
+static inline struct rxfcb *gfar_get_fcb(struct sk_buff *skb)
+{
+	struct rxfcb *fcb = (struct rxfcb *)skb->data;
+
+	/* Remove the FCB from the skb */
+	skb_pull(skb, GMAC_FCB_LEN);
+
+	return fcb;
+}
 
 /* gfar_process_frame() -- handle one incoming packet if skb
  * isn't NULL.  */
@@ -1225,35 +1488,51 @@
 		int length)
 {
 	struct gfar_private *priv = netdev_priv(dev);
+	struct rxfcb *fcb = NULL;
 
 	if (skb == NULL) {
-#ifdef BRIEF_GFAR_ERRORS
-		printk(KERN_WARNING "%s: Missing skb!!.\n",
-				dev->name);
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_WARNING "%s: Missing skb!!.\n", dev->name);
 		priv->stats.rx_dropped++;
 		priv->extra_stats.rx_skbmissing++;
 	} else {
+		int ret;
+
 		/* Prep the skb for the packet */
 		skb_put(skb, length);
 
+		/* Grab the FCB if there is one */
+		if (gfar_uses_fcb(priv))
+			fcb = gfar_get_fcb(skb);
+
+		/* Remove the padded bytes, if there are any */
+		if (priv->padding)
+			skb_pull(skb, priv->padding);
+
+		if (priv->rx_csum_enable)
+			gfar_rx_checksum(skb, fcb);
+
 		/* Tell the skb what kind of packet this is */
 		skb->protocol = eth_type_trans(skb, dev);
 
 		/* Send the packet up the stack */
-		if (RECEIVE(skb) == NET_RX_DROP) {
+		if (unlikely(priv->vlgrp && fcb->vln))
+			ret = gfar_rx_vlan(skb, priv->vlgrp, fcb->vlctl);
+		else
+			ret = RECEIVE(skb);
+
+		if (NET_RX_DROP == ret)
 			priv->extra_stats.kernel_dropped++;
-		}
 	}
 
 	return 0;
 }
 
 /* gfar_clean_rx_ring() -- Processes each frame in the rx ring
- *   until the budget/quota has been reached. Returns the number 
+ *   until the budget/quota has been reached. Returns the number
  *   of frames handled
  */
-static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
+int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
 {
 	struct rxbd8 *bdp;
 	struct sk_buff *skb;
@@ -1355,9 +1634,6 @@
 				   mk_ic_value(priv->rxcount, priv->rxtime));
 		else
 			gfar_write(&priv->regs->rxic, 0);
-
-		/* Signal to the ring size changer that it's safe to go */
-		priv->rxclean = 1;
 	}
 
 	return (rx_work_limit < 0) ? 1 : 0;
@@ -1393,10 +1669,8 @@
 		if (events & IEVENT_CRL)
 			priv->stats.tx_aborted_errors++;
 		if (events & IEVENT_XFUN) {
-#ifdef VERBOSE_GFAR_ERRORS
-			printk(KERN_WARNING "%s: tx underrun. dropped packet\n",
-			       dev->name);
-#endif
+			if (netif_msg_tx_err(priv))
+				printk(KERN_WARNING "%s: tx underrun. dropped packet\n", dev->name);
 			priv->stats.tx_dropped++;
 			priv->extra_stats.tx_underrun++;
 
@@ -1415,36 +1689,30 @@
 		gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
 #endif
 
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", dev->name,
-		       gfar_read(&priv->regs->rstat));
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n",
+					dev->name,
+					gfar_read(&priv->regs->rstat));
 	}
 	if (events & IEVENT_BABR) {
 		priv->stats.rx_errors++;
 		priv->extra_stats.rx_babr++;
 
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: babbling error\n", dev->name);
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_DEBUG "%s: babbling error\n", dev->name);
 	}
 	if (events & IEVENT_EBERR) {
 		priv->extra_stats.eberr++;
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: EBERR\n", dev->name);
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_DEBUG "%s: EBERR\n", dev->name);
 	}
-	if (events & IEVENT_RXC) {
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: control frame\n", dev->name);
-#endif
-	}
+	if ((events & IEVENT_RXC) && (netif_msg_rx_err(priv)))
+			printk(KERN_DEBUG "%s: control frame\n", dev->name);
 
 	if (events & IEVENT_BABT) {
 		priv->extra_stats.tx_babt++;
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: babt error\n", dev->name);
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_DEBUG "%s: babt error\n", dev->name);
 	}
 
 	return IRQ_HANDLED;
@@ -1510,7 +1778,7 @@
  * If, after GFAR_AN_TIMEOUT seconds, it has not
  * finished, we switch to forced.
  * Either way, once the process has completed, we either
- * request the interrupt, or switch the timer over to 
+ * request the interrupt, or switch the timer over to
  * using gfar_phy_timer to check status */
 static void gfar_phy_startup_timer(unsigned long data)
 {
@@ -1535,8 +1803,9 @@
 
 		/* Forcing failed!  Give up */
 		if(result) {
-			printk(KERN_ERR "%s: Forcing failed!\n",
-					mii_info->dev->name);
+			if (netif_msg_link(priv))
+				printk(KERN_ERR "%s: Forcing failed!\n",
+						mii_info->dev->name);
 			return;
 		}
 	}
@@ -1546,16 +1815,17 @@
 
 	/* Grab the PHY interrupt, if necessary/possible */
 	if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) {
-		if (request_irq(priv->einfo->interruptPHY, 
+		if (request_irq(priv->einfo->interruptPHY,
 					phy_interrupt,
-					SA_SHIRQ, 
-					"phy_interrupt", 
+					SA_SHIRQ,
+					"phy_interrupt",
 					mii_info->dev) < 0) {
-			printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
-					mii_info->dev->name,
+			if (netif_msg_intr(priv))
+				printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+						mii_info->dev->name,
 					priv->einfo->interruptPHY);
 		} else {
-			mii_configure_phy_interrupt(priv->mii_info, 
+			mii_configure_phy_interrupt(priv->mii_info,
 					MII_INTERRUPT_ENABLED);
 			return;
 		}
@@ -1592,15 +1862,17 @@
 				tempval &= ~(MACCFG2_FULL_DUPLEX);
 				gfar_write(&regs->maccfg2, tempval);
 
-				printk(KERN_INFO "%s: Half Duplex\n",
-				       dev->name);
+				if (netif_msg_link(priv))
+					printk(KERN_INFO "%s: Half Duplex\n",
+							dev->name);
 			} else {
 				tempval = gfar_read(&regs->maccfg2);
 				tempval |= MACCFG2_FULL_DUPLEX;
 				gfar_write(&regs->maccfg2, tempval);
 
-				printk(KERN_INFO "%s: Full Duplex\n",
-				       dev->name);
+				if (netif_msg_link(priv))
+					printk(KERN_INFO "%s: Full Duplex\n",
+							dev->name);
 			}
 
 			priv->oldduplex = mii_info->duplex;
@@ -1622,27 +1894,32 @@
 				gfar_write(&regs->maccfg2, tempval);
 				break;
 			default:
-				printk(KERN_WARNING
-				       "%s: Ack!  Speed (%d) is not 10/100/1000!\n",
-				       dev->name, mii_info->speed);
+				if (netif_msg_link(priv))
+					printk(KERN_WARNING
+							"%s: Ack!  Speed (%d) is not 10/100/1000!\n",
+							dev->name, mii_info->speed);
 				break;
 			}
 
-			printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
-			       mii_info->speed);
+			if (netif_msg_link(priv))
+				printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
+						mii_info->speed);
 
 			priv->oldspeed = mii_info->speed;
 		}
 
 		if (!priv->oldlink) {
-			printk(KERN_INFO "%s: Link is up\n", dev->name);
+			if (netif_msg_link(priv))
+				printk(KERN_INFO "%s: Link is up\n", dev->name);
 			priv->oldlink = 1;
 			netif_carrier_on(dev);
 			netif_schedule(dev);
 		}
 	} else {
 		if (priv->oldlink) {
-			printk(KERN_INFO "%s: Link is down\n", dev->name);
+			if (netif_msg_link(priv))
+				printk(KERN_INFO "%s: Link is down\n",
+						dev->name);
 			priv->oldlink = 0;
 			priv->oldspeed = 0;
 			priv->oldduplex = -1;
@@ -1664,8 +1941,9 @@
 	u32 tempval;
 
 	if(dev->flags & IFF_PROMISC) {
-		printk(KERN_INFO "%s: Entering promiscuous mode.\n",
-				dev->name);
+		if (netif_msg_drv(priv))
+			printk(KERN_INFO "%s: Entering promiscuous mode.\n",
+					dev->name);
 		/* Set RCTRL to PROM */
 		tempval = gfar_read(&regs->rctrl);
 		tempval |= RCTRL_PROM;
@@ -1679,6 +1957,14 @@
 	
 	if(dev->flags & IFF_ALLMULTI) {
 		/* Set the hash to rx all multicast frames */
+		gfar_write(&regs->igaddr0, 0xffffffff);
+		gfar_write(&regs->igaddr1, 0xffffffff);
+		gfar_write(&regs->igaddr2, 0xffffffff);
+		gfar_write(&regs->igaddr3, 0xffffffff);
+		gfar_write(&regs->igaddr4, 0xffffffff);
+		gfar_write(&regs->igaddr5, 0xffffffff);
+		gfar_write(&regs->igaddr6, 0xffffffff);
+		gfar_write(&regs->igaddr7, 0xffffffff);
 		gfar_write(&regs->gaddr0, 0xffffffff);
 		gfar_write(&regs->gaddr1, 0xffffffff);
 		gfar_write(&regs->gaddr2, 0xffffffff);
@@ -1689,6 +1975,14 @@
 		gfar_write(&regs->gaddr7, 0xffffffff);
 	} else {
 		/* zero out the hash */
+		gfar_write(&regs->igaddr0, 0x0);
+		gfar_write(&regs->igaddr1, 0x0);
+		gfar_write(&regs->igaddr2, 0x0);
+		gfar_write(&regs->igaddr3, 0x0);
+		gfar_write(&regs->igaddr4, 0x0);
+		gfar_write(&regs->igaddr5, 0x0);
+		gfar_write(&regs->igaddr6, 0x0);
+		gfar_write(&regs->igaddr7, 0x0);
 		gfar_write(&regs->gaddr0, 0x0);
 		gfar_write(&regs->gaddr1, 0x0);
 		gfar_write(&regs->gaddr2, 0x0);
@@ -1727,16 +2021,15 @@
 {
 	u32 tempval;
 	struct gfar_private *priv = netdev_priv(dev);
-	struct gfar *regs = priv->regs;
-	u32 *hash = &regs->gaddr0;
 	u32 result = ether_crc(MAC_ADDR_LEN, addr);
-	u8 whichreg = ((result >> 29) & 0x7);
-	u8 whichbit = ((result >> 24) & 0x1f);
+	int width = priv->hash_width;
+	u8 whichbit = (result >> (32 - width)) & 0x1f;
+	u8 whichreg = result >> (32 - width + 5);
 	u32 value = (1 << (31-whichbit));
 
-	tempval = gfar_read(&hash[whichreg]);
+	tempval = gfar_read(priv->hash_regs[whichreg]);
 	tempval |= value;
-	gfar_write(&hash[whichreg], tempval);
+	gfar_write(priv->hash_regs[whichreg], tempval);
 
 	return;
 }
@@ -1754,10 +2047,9 @@
 	gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK);
 
 	/* Hmm... */
-#if defined (BRIEF_GFAR_ERRORS) || defined (VERBOSE_GFAR_ERRORS)
-	printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n",
-	       dev->name, events, gfar_read(&priv->regs->imask));
-#endif
+	if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))
+		printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n",
+				dev->name, events, gfar_read(&priv->regs->imask));
 
 	/* Update the error counters */
 	if (events & IEVENT_TXE) {
@@ -1768,19 +2060,17 @@
 		if (events & IEVENT_CRL)
 			priv->stats.tx_aborted_errors++;
 		if (events & IEVENT_XFUN) {
-#ifdef VERBOSE_GFAR_ERRORS
-			printk(KERN_DEBUG "%s: underrun.  packet dropped.\n",
-			       dev->name);
-#endif
+			if (netif_msg_tx_err(priv))
+				printk(KERN_DEBUG "%s: underrun.  packet dropped.\n",
+						dev->name);
 			priv->stats.tx_dropped++;
 			priv->extra_stats.tx_underrun++;
 
 			/* Reactivate the Tx Queues */
 			gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
 		}
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
-#endif
+		if (netif_msg_tx_err(priv))
+			printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
 	}
 	if (events & IEVENT_BSY) {
 		priv->stats.rx_errors++;
@@ -1793,35 +2083,31 @@
 		gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
 #endif
 
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", dev->name,
-		       gfar_read(&priv->regs->rstat));
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n",
+					dev->name,
+					gfar_read(&priv->regs->rstat));
 	}
 	if (events & IEVENT_BABR) {
 		priv->stats.rx_errors++;
 		priv->extra_stats.rx_babr++;
 
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: babbling error\n", dev->name);
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_DEBUG "%s: babbling error\n", dev->name);
 	}
 	if (events & IEVENT_EBERR) {
 		priv->extra_stats.eberr++;
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: EBERR\n", dev->name);
-#endif
+		if (netif_msg_rx_err(priv))
+			printk(KERN_DEBUG "%s: EBERR\n", dev->name);
 	}
-	if (events & IEVENT_RXC)
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: control frame\n", dev->name);
-#endif
+	if ((events & IEVENT_RXC) && netif_msg_rx_status(priv))
+		if (netif_msg_rx_status(priv))
+			printk(KERN_DEBUG "%s: control frame\n", dev->name);
 
 	if (events & IEVENT_BABT) {
 		priv->extra_stats.tx_babt++;
-#ifdef VERBOSE_GFAR_ERRORS
-		printk(KERN_DEBUG "%s: babt error\n", dev->name);
-#endif
+		if (netif_msg_tx_err(priv))
+			printk(KERN_DEBUG "%s: babt error\n", dev->name);
 	}
 	return IRQ_HANDLED;
 }
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index c2f783a..28af087 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -1,4 +1,4 @@
-/* 
+/*
  * drivers/net/gianfar.h
  *
  * Gianfar Ethernet Driver
@@ -53,6 +53,12 @@
 /* The maximum number of packets to be handled in one call of gfar_poll */
 #define GFAR_DEV_WEIGHT 64
 
+/* Length for FCB */
+#define GMAC_FCB_LEN 8
+
+/* Default padding amount */
+#define DEFAULT_PADDING 2
+
 /* Number of bytes to align the rx bufs to */
 #define RXBUF_ALIGNMENT 64
 
@@ -91,7 +97,7 @@
 #define JUMBO_FRAME_SIZE 9600
 
 /* Latency of interface clock in nanoseconds */
-/* Interface clock latency , in this case, means the 
+/* Interface clock latency , in this case, means the
  * time described by a value of 1 in the interrupt
  * coalescing registers' time fields.  Since those fields
  * refer to the time it takes for 64 clocks to pass, the
@@ -166,9 +172,28 @@
 				mk_ic_icft(count) | \
 				mk_ic_ictt(time))
 
+#define RCTRL_PAL_MASK		0x001f0000
+#define RCTRL_VLEX		0x00002000
+#define RCTRL_FILREN		0x00001000
+#define RCTRL_GHTX		0x00000400
+#define RCTRL_IPCSEN		0x00000200
+#define RCTRL_TUCSEN		0x00000100
+#define RCTRL_PRSDEP_MASK	0x000000c0
+#define RCTRL_PRSDEP_INIT	0x000000c0
 #define RCTRL_PROM		0x00000008
+#define RCTRL_CHECKSUMMING	(RCTRL_IPCSEN \
+		| RCTRL_TUCSEN | RCTRL_PRSDEP_INIT)
+#define RCTRL_EXTHASH		(RCTRL_GHTX)
+#define RCTRL_VLAN		(RCTRL_PRSDEP_INIT)
+
+
 #define RSTAT_CLEAR_RHALT       0x00800000
 
+#define TCTRL_IPCSEN		0x00004000
+#define TCTRL_TUCSEN		0x00002000
+#define TCTRL_VLINS		0x00001000
+#define TCTRL_INIT_CSUM		(TCTRL_TUCSEN | TCTRL_IPCSEN)
+
 #define IEVENT_INIT_CLEAR	0xffffffff
 #define IEVENT_BABR		0x80000000
 #define IEVENT_RXC		0x40000000
@@ -187,12 +212,16 @@
 #define IEVENT_RXB0		0x00008000
 #define IEVENT_GRSC		0x00000100
 #define IEVENT_RXF0		0x00000080
+#define IEVENT_FIR		0x00000008
+#define IEVENT_FIQ		0x00000004
+#define IEVENT_DPE		0x00000002
+#define IEVENT_PERR		0x00000001
 #define IEVENT_RX_MASK          (IEVENT_RXB0 | IEVENT_RXF0)
 #define IEVENT_TX_MASK          (IEVENT_TXB | IEVENT_TXF)
 #define IEVENT_ERR_MASK         \
 (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \
  IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \
- | IEVENT_CRL | IEVENT_XFUN)
+ | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR)
 
 #define IMASK_INIT_CLEAR	0x00000000
 #define IMASK_BABR              0x80000000
@@ -212,10 +241,15 @@
 #define IMASK_RXB0              0x00008000
 #define IMASK_GTSC              0x00000100
 #define IMASK_RXFEN0		0x00000080
+#define IMASK_FIR		0x00000008
+#define IMASK_FIQ		0x00000004
+#define IMASK_DPE		0x00000002
+#define IMASK_PERR		0x00000001
 #define IMASK_RX_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY)
 #define IMASK_DEFAULT  (IMASK_TXEEN | IMASK_TXFEN | IMASK_TXBEN | \
 		IMASK_RXFEN0 | IMASK_BSY | IMASK_EBERR | IMASK_BABR | \
-		IMASK_XFUN | IMASK_RXC | IMASK_BABT)
+		IMASK_XFUN | IMASK_RXC | IMASK_BABT | IMASK_DPE \
+		| IMASK_PERR)
 
 
 /* Attribute fields */
@@ -254,6 +288,18 @@
 #define TXBD_RETRYLIMIT		0x0040
 #define	TXBD_RETRYCOUNTMASK	0x003c
 #define TXBD_UNDERRUN		0x0002
+#define TXBD_TOE		0x0002
+
+/* Tx FCB param bits */
+#define TXFCB_VLN		0x80
+#define TXFCB_IP		0x40
+#define TXFCB_IP6		0x20
+#define TXFCB_TUP		0x10
+#define TXFCB_UDP		0x08
+#define TXFCB_CIP		0x04
+#define TXFCB_CTU		0x02
+#define TXFCB_NPH		0x01
+#define TXFCB_DEFAULT 		(TXFCB_IP|TXFCB_TUP|TXFCB_CTU|TXFCB_NPH)
 
 /* RxBD status field bits */
 #define RXBD_EMPTY		0x8000
@@ -273,6 +319,18 @@
 #define RXBD_TRUNCATED		0x0001
 #define RXBD_STATS		0x01ff
 
+/* Rx FCB status field bits */
+#define RXFCB_VLN		0x8000
+#define RXFCB_IP		0x4000
+#define RXFCB_IP6		0x2000
+#define RXFCB_TUP		0x1000
+#define RXFCB_CIP		0x0800
+#define RXFCB_CTU		0x0400
+#define RXFCB_EIP		0x0200
+#define RXFCB_ETU		0x0100
+#define RXFCB_PERR_MASK		0x000c
+#define RXFCB_PERR_BADL3	0x0008
+
 struct txbd8
 {
 	u16	status;	/* Status Fields */
@@ -280,6 +338,22 @@
 	u32	bufPtr;	/* Buffer Pointer */
 };
 
+struct txfcb {
+	u8	vln:1,
+		ip:1,
+		ip6:1,
+		tup:1,
+		udp:1,
+		cip:1,
+		ctu:1,
+		nph:1;
+	u8	reserved;
+	u8	l4os;	/* Level 4 Header Offset */
+	u8	l3os; 	/* Level 3 Header Offset */
+	u16	phcs;	/* Pseudo-header Checksum */
+	u16	vlctl;	/* VLAN control word */
+};
+
 struct rxbd8
 {
 	u16	status;	/* Status Fields */
@@ -287,6 +361,21 @@
 	u32	bufPtr;	/* Buffer Pointer */
 };
 
+struct rxfcb {
+	u16	vln:1,
+		ip:1,
+		ip6:1,
+		tup:1,
+		cip:1,
+		ctu:1,
+		eip:1,
+		etu:1;
+	u8	rq;	/* Receive Queue index */
+	u8	pro;	/* Layer 4 Protocol */
+	u16	reserved;
+	u16	vlctl;	/* VLAN control word */
+};
+
 struct rmon_mib
 {
 	u32	tr64;	/* 0x.680 - Transmit and Receive 64-byte Frame Counter */
@@ -371,90 +460,191 @@
 
 
 struct gfar {
-	u8	res1[16];
-	u32	ievent;			/* 0x.010 - Interrupt Event Register */
-	u32	imask;			/* 0x.014 - Interrupt Mask Register */
-	u32	edis;			/* 0x.018 - Error Disabled Register */
+	u32	tsec_id;	/* 0x.000 - Controller ID register */
+	u8	res1[12];
+	u32	ievent;		/* 0x.010 - Interrupt Event Register */
+	u32	imask;		/* 0x.014 - Interrupt Mask Register */
+	u32	edis;		/* 0x.018 - Error Disabled Register */
 	u8	res2[4];
-	u32	ecntrl;			/* 0x.020 - Ethernet Control Register */
-	u32	minflr;			/* 0x.024 - Minimum Frame Length Register */
-	u32	ptv;			/* 0x.028 - Pause Time Value Register */
-	u32	dmactrl;		/* 0x.02c - DMA Control Register */
-	u32	tbipa;			/* 0x.030 - TBI PHY Address Register */
+	u32	ecntrl;		/* 0x.020 - Ethernet Control Register */
+	u32	minflr;		/* 0x.024 - Minimum Frame Length Register */
+	u32	ptv;		/* 0x.028 - Pause Time Value Register */
+	u32	dmactrl;	/* 0x.02c - DMA Control Register */
+	u32	tbipa;		/* 0x.030 - TBI PHY Address Register */
 	u8	res3[88];
-	u32	fifo_tx_thr;		/* 0x.08c - FIFO transmit threshold register */
+	u32	fifo_tx_thr;	/* 0x.08c - FIFO transmit threshold register */
 	u8	res4[8];
-	u32	fifo_tx_starve;		/* 0x.098 - FIFO transmit starve register */
+	u32	fifo_tx_starve;	/* 0x.098 - FIFO transmit starve register */
 	u32	fifo_tx_starve_shutoff;	/* 0x.09c - FIFO transmit starve shutoff register */
-	u8	res5[96];
-	u32	tctrl;			/* 0x.100 - Transmit Control Register */
-	u32	tstat;			/* 0x.104 - Transmit Status Register */
-	u8	res6[4];
-	u32	tbdlen;			/* 0x.10c - Transmit Buffer Descriptor Data Length Register */
-	u32	txic;			/* 0x.110 - Transmit Interrupt Coalescing Configuration Register */
-	u8	res7[16];
-	u32	ctbptr;			/* 0x.124 - Current Transmit Buffer Descriptor Pointer Register */
-	u8	res8[92];
-	u32	tbptr;			/* 0x.184 - Transmit Buffer Descriptor Pointer Low Register */
-	u8	res9[124];
-	u32	tbase;			/* 0x.204 - Transmit Descriptor Base Address Register */
-	u8	res10[168];
-	u32	ostbd;			/* 0x.2b0 - Out-of-Sequence Transmit Buffer Descriptor Register */
-	u32	ostbdp;			/* 0x.2b4 - Out-of-Sequence Transmit Data Buffer Pointer Register */
-	u8	res11[72];
-	u32	rctrl;			/* 0x.300 - Receive Control Register */
-	u32	rstat;			/* 0x.304 - Receive Status Register */
-	u8	res12[4];
-	u32	rbdlen;			/* 0x.30c - RxBD Data Length Register */
-	u32	rxic;			/* 0x.310 - Receive Interrupt Coalescing Configuration Register */
-	u8	res13[16];
-	u32	crbptr;			/* 0x.324 - Current Receive Buffer Descriptor Pointer */
-	u8	res14[24];
-	u32	mrblr;			/* 0x.340 - Maximum Receive Buffer Length Register */
-	u8	res15[64];
-	u32	rbptr;			/* 0x.384 - Receive Buffer Descriptor Pointer */
-	u8	res16[124];
-	u32	rbase;			/* 0x.404 - Receive Descriptor Base Address */
-	u8	res17[248];
-	u32	maccfg1;		/* 0x.500 - MAC Configuration 1 Register */
-	u32	maccfg2;		/* 0x.504 - MAC Configuration 2 Register */
-	u32	ipgifg;			/* 0x.508 - Inter Packet Gap/Inter Frame Gap Register */
-	u32	hafdup;			/* 0x.50c - Half Duplex Register */
-	u32	maxfrm;			/* 0x.510 - Maximum Frame Length Register */
+	u8	res5[4];
+	u32	fifo_rx_pause;	/* 0x.0a4 - FIFO receive pause threshold register */
+	u32	fifo_rx_alarm;	/* 0x.0a8 - FIFO receive alarm threshold register */
+	u8	res6[84];
+	u32	tctrl;		/* 0x.100 - Transmit Control Register */
+	u32	tstat;		/* 0x.104 - Transmit Status Register */
+	u32	dfvlan;		/* 0x.108 - Default VLAN Control word */
+	u32	tbdlen;		/* 0x.10c - Transmit Buffer Descriptor Data Length Register */
+	u32	txic;		/* 0x.110 - Transmit Interrupt Coalescing Configuration Register */
+	u32	tqueue;		/* 0x.114 - Transmit queue control register */
+	u8	res7[40];
+	u32	tr03wt;		/* 0x.140 - TxBD Rings 0-3 round-robin weightings */
+	u32	tr47wt;		/* 0x.144 - TxBD Rings 4-7 round-robin weightings */
+	u8	res8[52];
+	u32	tbdbph;		/* 0x.17c - Tx data buffer pointer high */
+	u8	res9a[4];
+	u32	tbptr0;		/* 0x.184 - TxBD Pointer for ring 0 */
+	u8	res9b[4];
+	u32	tbptr1;		/* 0x.18c - TxBD Pointer for ring 1 */
+	u8	res9c[4];
+	u32	tbptr2;		/* 0x.194 - TxBD Pointer for ring 2 */
+	u8	res9d[4];
+	u32	tbptr3;		/* 0x.19c - TxBD Pointer for ring 3 */
+	u8	res9e[4];
+	u32	tbptr4;		/* 0x.1a4 - TxBD Pointer for ring 4 */
+	u8	res9f[4];
+	u32	tbptr5;		/* 0x.1ac - TxBD Pointer for ring 5 */
+	u8	res9g[4];
+	u32	tbptr6;		/* 0x.1b4 - TxBD Pointer for ring 6 */
+	u8	res9h[4];
+	u32	tbptr7;		/* 0x.1bc - TxBD Pointer for ring 7 */
+	u8	res9[64];
+	u32	tbaseh;		/* 0x.200 - TxBD base address high */
+	u32	tbase0;		/* 0x.204 - TxBD Base Address of ring 0 */
+	u8	res10a[4];
+	u32	tbase1;		/* 0x.20c - TxBD Base Address of ring 1 */
+	u8	res10b[4];
+	u32	tbase2;		/* 0x.214 - TxBD Base Address of ring 2 */
+	u8	res10c[4];
+	u32	tbase3;		/* 0x.21c - TxBD Base Address of ring 3 */
+	u8	res10d[4];
+	u32	tbase4;		/* 0x.224 - TxBD Base Address of ring 4 */
+	u8	res10e[4];
+	u32	tbase5;		/* 0x.22c - TxBD Base Address of ring 5 */
+	u8	res10f[4];
+	u32	tbase6;		/* 0x.234 - TxBD Base Address of ring 6 */
+	u8	res10g[4];
+	u32	tbase7;		/* 0x.23c - TxBD Base Address of ring 7 */
+	u8	res10[192];
+	u32	rctrl;		/* 0x.300 - Receive Control Register */
+	u32	rstat;		/* 0x.304 - Receive Status Register */
+	u8	res12[8];
+	u32	rxic;		/* 0x.310 - Receive Interrupt Coalescing Configuration Register */
+	u32	rqueue;		/* 0x.314 - Receive queue control register */
+	u8	res13[24];
+	u32	rbifx;		/* 0x.330 - Receive bit field extract control register */
+	u32	rqfar;		/* 0x.334 - Receive queue filing table address register */
+	u32	rqfcr;		/* 0x.338 - Receive queue filing table control register */
+	u32	rqfpr;		/* 0x.33c - Receive queue filing table property register */
+	u32	mrblr;		/* 0x.340 - Maximum Receive Buffer Length Register */
+	u8	res14[56];
+	u32	rbdbph;		/* 0x.37c - Rx data buffer pointer high */
+	u8	res15a[4];
+	u32	rbptr0;		/* 0x.384 - RxBD pointer for ring 0 */
+	u8	res15b[4];
+	u32	rbptr1;		/* 0x.38c - RxBD pointer for ring 1 */
+	u8	res15c[4];
+	u32	rbptr2;		/* 0x.394 - RxBD pointer for ring 2 */
+	u8	res15d[4];
+	u32	rbptr3;		/* 0x.39c - RxBD pointer for ring 3 */
+	u8	res15e[4];
+	u32	rbptr4;		/* 0x.3a4 - RxBD pointer for ring 4 */
+	u8	res15f[4];
+	u32	rbptr5;		/* 0x.3ac - RxBD pointer for ring 5 */
+	u8	res15g[4];
+	u32	rbptr6;		/* 0x.3b4 - RxBD pointer for ring 6 */
+	u8	res15h[4];
+	u32	rbptr7;		/* 0x.3bc - RxBD pointer for ring 7 */
+	u8	res16[64];
+	u32	rbaseh;		/* 0x.400 - RxBD base address high */
+	u32	rbase0;		/* 0x.404 - RxBD base address of ring 0 */
+	u8	res17a[4];
+	u32	rbase1;		/* 0x.40c - RxBD base address of ring 1 */
+	u8	res17b[4];
+	u32	rbase2;		/* 0x.414 - RxBD base address of ring 2 */
+	u8	res17c[4];
+	u32	rbase3;		/* 0x.41c - RxBD base address of ring 3 */
+	u8	res17d[4];
+	u32	rbase4;		/* 0x.424 - RxBD base address of ring 4 */
+	u8	res17e[4];
+	u32	rbase5;		/* 0x.42c - RxBD base address of ring 5 */
+	u8	res17f[4];
+	u32	rbase6;		/* 0x.434 - RxBD base address of ring 6 */
+	u8	res17g[4];
+	u32	rbase7;		/* 0x.43c - RxBD base address of ring 7 */
+	u8	res17[192];
+	u32	maccfg1;	/* 0x.500 - MAC Configuration 1 Register */
+	u32	maccfg2;	/* 0x.504 - MAC Configuration 2 Register */
+	u32	ipgifg;		/* 0x.508 - Inter Packet Gap/Inter Frame Gap Register */
+	u32	hafdup;		/* 0x.50c - Half Duplex Register */
+	u32	maxfrm;		/* 0x.510 - Maximum Frame Length Register */
 	u8	res18[12];
-	u32	miimcfg;		/* 0x.520 - MII Management Configuration Register */
-	u32	miimcom;		/* 0x.524 - MII Management Command Register */
-	u32	miimadd;		/* 0x.528 - MII Management Address Register */
-	u32	miimcon;		/* 0x.52c - MII Management Control Register */
-	u32	miimstat;		/* 0x.530 - MII Management Status Register */
-	u32	miimind;		/* 0x.534 - MII Management Indicator Register */
+	u32	miimcfg;	/* 0x.520 - MII Management Configuration Register */
+	u32	miimcom;	/* 0x.524 - MII Management Command Register */
+	u32	miimadd;	/* 0x.528 - MII Management Address Register */
+	u32	miimcon;	/* 0x.52c - MII Management Control Register */
+	u32	miimstat;	/* 0x.530 - MII Management Status Register */
+	u32	miimind;	/* 0x.534 - MII Management Indicator Register */
 	u8	res19[4];
-	u32	ifstat;			/* 0x.53c - Interface Status Register */
-	u32	macstnaddr1;		/* 0x.540 - Station Address Part 1 Register */
-	u32	macstnaddr2;		/* 0x.544 - Station Address Part 2 Register */
-	u8	res20[312];
-	struct rmon_mib	rmon;
-	u8	res21[192];
-	u32	iaddr0;			/* 0x.800 - Indivdual address register 0 */
-	u32	iaddr1;			/* 0x.804 - Indivdual address register 1 */
-	u32	iaddr2;			/* 0x.808 - Indivdual address register 2 */
-	u32	iaddr3;			/* 0x.80c - Indivdual address register 3 */
-	u32	iaddr4;			/* 0x.810 - Indivdual address register 4 */
-	u32	iaddr5;			/* 0x.814 - Indivdual address register 5 */
-	u32	iaddr6;			/* 0x.818 - Indivdual address register 6 */
-	u32	iaddr7;			/* 0x.81c - Indivdual address register 7 */
+	u32	ifstat;		/* 0x.53c - Interface Status Register */
+	u32	macstnaddr1;	/* 0x.540 - Station Address Part 1 Register */
+	u32	macstnaddr2;	/* 0x.544 - Station Address Part 2 Register */
+	u32	mac01addr1;	/* 0x.548 - MAC exact match address 1, part 1 */
+	u32	mac01addr2;	/* 0x.54c - MAC exact match address 1, part 2 */
+	u32	mac02addr1;	/* 0x.550 - MAC exact match address 2, part 1 */
+	u32	mac02addr2;	/* 0x.554 - MAC exact match address 2, part 2 */
+	u32	mac03addr1;	/* 0x.558 - MAC exact match address 3, part 1 */
+	u32	mac03addr2;	/* 0x.55c - MAC exact match address 3, part 2 */
+	u32	mac04addr1;	/* 0x.560 - MAC exact match address 4, part 1 */
+	u32	mac04addr2;	/* 0x.564 - MAC exact match address 4, part 2 */
+	u32	mac05addr1;	/* 0x.568 - MAC exact match address 5, part 1 */
+	u32	mac05addr2;	/* 0x.56c - MAC exact match address 5, part 2 */
+	u32	mac06addr1;	/* 0x.570 - MAC exact match address 6, part 1 */
+	u32	mac06addr2;	/* 0x.574 - MAC exact match address 6, part 2 */
+	u32	mac07addr1;	/* 0x.578 - MAC exact match address 7, part 1 */
+	u32	mac07addr2;	/* 0x.57c - MAC exact match address 7, part 2 */
+	u32	mac08addr1;	/* 0x.580 - MAC exact match address 8, part 1 */
+	u32	mac08addr2;	/* 0x.584 - MAC exact match address 8, part 2 */
+	u32	mac09addr1;	/* 0x.588 - MAC exact match address 9, part 1 */
+	u32	mac09addr2;	/* 0x.58c - MAC exact match address 9, part 2 */
+	u32	mac10addr1;	/* 0x.590 - MAC exact match address 10, part 1*/
+	u32	mac10addr2;	/* 0x.594 - MAC exact match address 10, part 2*/
+	u32	mac11addr1;	/* 0x.598 - MAC exact match address 11, part 1*/
+	u32	mac11addr2;	/* 0x.59c - MAC exact match address 11, part 2*/
+	u32	mac12addr1;	/* 0x.5a0 - MAC exact match address 12, part 1*/
+	u32	mac12addr2;	/* 0x.5a4 - MAC exact match address 12, part 2*/
+	u32	mac13addr1;	/* 0x.5a8 - MAC exact match address 13, part 1*/
+	u32	mac13addr2;	/* 0x.5ac - MAC exact match address 13, part 2*/
+	u32	mac14addr1;	/* 0x.5b0 - MAC exact match address 14, part 1*/
+	u32	mac14addr2;	/* 0x.5b4 - MAC exact match address 14, part 2*/
+	u32	mac15addr1;	/* 0x.5b8 - MAC exact match address 15, part 1*/
+	u32	mac15addr2;	/* 0x.5bc - MAC exact match address 15, part 2*/
+	u8	res20[192];
+	struct rmon_mib	rmon;	/* 0x.680-0x.73c */
+	u32	rrej;		/* 0x.740 - Receive filer rejected packet counter */
+	u8	res21[188];
+	u32	igaddr0;	/* 0x.800 - Indivdual/Group address register 0*/
+	u32	igaddr1;	/* 0x.804 - Indivdual/Group address register 1*/
+	u32	igaddr2;	/* 0x.808 - Indivdual/Group address register 2*/
+	u32	igaddr3;	/* 0x.80c - Indivdual/Group address register 3*/
+	u32	igaddr4;	/* 0x.810 - Indivdual/Group address register 4*/
+	u32	igaddr5;	/* 0x.814 - Indivdual/Group address register 5*/
+	u32	igaddr6;	/* 0x.818 - Indivdual/Group address register 6*/
+	u32	igaddr7;	/* 0x.81c - Indivdual/Group address register 7*/
 	u8	res22[96];
-	u32	gaddr0;			/* 0x.880 - Global address register 0 */
-	u32	gaddr1;			/* 0x.884 - Global address register 1 */
-	u32	gaddr2;			/* 0x.888 - Global address register 2 */
-	u32	gaddr3;			/* 0x.88c - Global address register 3 */
-	u32	gaddr4;			/* 0x.890 - Global address register 4 */
-	u32	gaddr5;			/* 0x.894 - Global address register 5 */
-	u32	gaddr6;			/* 0x.898 - Global address register 6 */
-	u32	gaddr7;			/* 0x.89c - Global address register 7 */
-	u8	res23[856];
-	u32	attr;			/* 0x.bf8 - Attributes Register */
-	u32	attreli;		/* 0x.bfc - Attributes Extract Length and Extract Index Register */
+	u32	gaddr0;		/* 0x.880 - Group address register 0 */
+	u32	gaddr1;		/* 0x.884 - Group address register 1 */
+	u32	gaddr2;		/* 0x.888 - Group address register 2 */
+	u32	gaddr3;		/* 0x.88c - Group address register 3 */
+	u32	gaddr4;		/* 0x.890 - Group address register 4 */
+	u32	gaddr5;		/* 0x.894 - Group address register 5 */
+	u32	gaddr6;		/* 0x.898 - Group address register 6 */
+	u32	gaddr7;		/* 0x.89c - Group address register 7 */
+	u8	res23a[352];
+	u32	fifocfg;	/* 0x.a00 - FIFO interface config register */
+	u8	res23b[252];
+	u8	res23c[248];
+	u32	attr;		/* 0x.bf8 - Attributes Register */
+	u32	attreli;	/* 0x.bfc - Attributes Extract Length and Extract Index Register */
 	u8	res24[1024];
 
 };
@@ -496,6 +686,8 @@
 	struct txbd8 *cur_tx;	        /* Next free ring entry */
 	struct txbd8 *dirty_tx;		/* The Ring entry to be freed. */
 	struct gfar *regs;	/* Pointer to the GFAR memory mapped Registers */
+	u32 *hash_regs[16];
+	int hash_width;
 	struct gfar *phyregs;
 	struct work_struct tq;
 	struct timer_list phy_info_timer;
@@ -506,9 +698,12 @@
 	unsigned int rx_stash_size;
 	unsigned int tx_ring_size;
 	unsigned int rx_ring_size;
-	wait_queue_head_t rxcleanupq;
-	unsigned int rxclean;
 
+	unsigned char vlan_enable:1,
+		rx_csum_enable:1,
+		extended_hash:1;
+	unsigned short padding;
+	struct vlan_group *vlgrp;
 	/* Info structure initialized by board setup code */
 	unsigned int interruptTransmit;
 	unsigned int interruptReceive;
@@ -519,6 +714,8 @@
 	int oldspeed;
 	int oldduplex;
 	int oldlink;
+
+	uint32_t msg_enable;
 };
 
 extern inline u32 gfar_read(volatile unsigned *addr)
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 28046e9..a451de6 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -46,16 +46,18 @@
 
 extern int startup_gfar(struct net_device *dev);
 extern void stop_gfar(struct net_device *dev);
-extern void gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
+extern void gfar_halt(struct net_device *dev);
+extern void gfar_start(struct net_device *dev);
+extern int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
 
-void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
+static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
 		     u64 * buf);
-void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
-int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
-int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
-void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
-int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
-void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo);
+static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
+static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+static void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo);
 
 static char stat_gstrings[][ETH_GSTRING_LEN] = {
 	"rx-dropped-by-kernel",
@@ -118,57 +120,56 @@
 	"tx-fragmented-frames",
 };
 
+/* Fill in a buffer with the strings which correspond to the
+ * stats */
+static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	
+	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
+		memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
+	else
+		memcpy(buf, stat_gstrings,
+				GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN);
+}
+
 /* Fill in an array of 64-bit statistics from various sources.
  * This array will be appended to the end of the ethtool_stats
  * structure, and returned to user space
  */
-void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
+static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
 {
 	int i;
 	struct gfar_private *priv = netdev_priv(dev);
-	u32 *rmon = (u32 *) & priv->regs->rmon;
 	u64 *extra = (u64 *) & priv->extra_stats;
-	struct gfar_stats *stats = (struct gfar_stats *) buf;
 
-	for (i = 0; i < GFAR_RMON_LEN; i++) {
-		stats->rmon[i] = (u64) (rmon[i]);
-	}
+	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
+		u32 *rmon = (u32 *) & priv->regs->rmon;
+		struct gfar_stats *stats = (struct gfar_stats *) buf;
 
-	for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
-		stats->extra[i] = extra[i];
-	}
+		for (i = 0; i < GFAR_RMON_LEN; i++)
+			stats->rmon[i] = (u64) (rmon[i]);
+
+		for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++)
+			stats->extra[i] = extra[i];
+	} else
+		for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++)
+			buf[i] = extra[i];
 }
 
 /* Returns the number of stats (and their corresponding strings) */
-int gfar_stats_count(struct net_device *dev)
+static int gfar_stats_count(struct net_device *dev)
 {
-	return GFAR_STATS_LEN;
-}
-
-void gfar_gstrings_normon(struct net_device *dev, u32 stringset, u8 * buf)
-{
-	memcpy(buf, stat_gstrings, GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN);
-}
-
-void gfar_fill_stats_normon(struct net_device *dev, 
-		struct ethtool_stats *dummy, u64 * buf)
-{
-	int i;
 	struct gfar_private *priv = netdev_priv(dev);
-	u64 *extra = (u64 *) & priv->extra_stats;
 
-	for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
-		buf[i] = extra[i];
-	}
+	if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
+		return GFAR_STATS_LEN;
+	else
+		return GFAR_EXTRA_STATS_LEN;
 }
 
-
-int gfar_stats_count_normon(struct net_device *dev)
-{
-	return GFAR_EXTRA_STATS_LEN;
-}
 /* Fills in the drvinfo structure with some basic info */
-void gfar_gdrvinfo(struct net_device *dev, struct
+static void gfar_gdrvinfo(struct net_device *dev, struct
 	      ethtool_drvinfo *drvinfo)
 {
 	strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN);
@@ -182,7 +183,7 @@
 }
 
 /* Return the current settings in the ethtool_cmd structure */
-int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
 	struct gfar_private *priv = netdev_priv(dev);
 	uint gigabit_support = 
@@ -216,13 +217,13 @@
 }
 
 /* Return the length of the register structure */
-int gfar_reglen(struct net_device *dev)
+static int gfar_reglen(struct net_device *dev)
 {
 	return sizeof (struct gfar);
 }
 
 /* Return a dump of the GFAR register space */
-void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
 {
 	int i;
 	struct gfar_private *priv = netdev_priv(dev);
@@ -233,13 +234,6 @@
 		buf[i] = theregs[i];
 }
 
-/* Fill in a buffer with the strings which correspond to the
- * stats */
-void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
-{
-	memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
-}
-
 /* Convert microseconds to ethernet clock ticks, which changes
  * depending on what speed the controller is running at */
 static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs)
@@ -291,9 +285,12 @@
 
 /* Get the coalescing parameters, and put them in the cvals
  * structure.  */
-int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
+static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
 {
 	struct gfar_private *priv = netdev_priv(dev);
+	
+	if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
+		return -EOPNOTSUPP;
 
 	cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
 	cvals->rx_max_coalesced_frames = priv->rxcount;
@@ -337,10 +334,13 @@
  * Both cvals->*_usecs and cvals->*_frames have to be > 0
  * in order for coalescing to be active
  */
-int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
+static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
 {
 	struct gfar_private *priv = netdev_priv(dev);
 
+	if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
+		return -EOPNOTSUPP;
+
 	/* Set up rx coalescing */
 	if ((cvals->rx_coalesce_usecs == 0) ||
 	    (cvals->rx_max_coalesced_frames == 0))
@@ -379,7 +379,7 @@
 /* Fills in rvals with the current ring parameters.  Currently,
  * rx, rx_mini, and rx_jumbo rings are the same size, as mini and
  * jumbo are ignored by the driver */
-void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
 {
 	struct gfar_private *priv = netdev_priv(dev);
 
@@ -401,9 +401,8 @@
  * necessary so that we don't mess things up while we're in
  * motion.  We wait for the ring to be clean before reallocating
  * the rings. */
-int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
 {
-	u32 tempval;
 	struct gfar_private *priv = netdev_priv(dev);
 	int err = 0;
 
@@ -425,44 +424,116 @@
 		return -EINVAL;
 	}
 
-	/* Stop the controller so we don't rx any more frames */
-	/* But first, make sure we clear the bits */
-	tempval = gfar_read(&priv->regs->dmactrl);
-	tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
-	gfar_write(&priv->regs->dmactrl, tempval);
-
-	tempval = gfar_read(&priv->regs->dmactrl);
-	tempval |= (DMACTRL_GRS | DMACTRL_GTS);
-	gfar_write(&priv->regs->dmactrl, tempval);
-
-	while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC)))
-		cpu_relax();
-
-	/* Note that rx is not clean right now */
-	priv->rxclean = 0;
-
 	if (dev->flags & IFF_UP) {
-		/* Tell the driver to process the rest of the frames */
-		gfar_receive(0, (void *) dev, NULL);
+		unsigned long flags;
 
-		/* Now wait for it to be done */
-		wait_event_interruptible(priv->rxcleanupq, priv->rxclean);
+		/* Halt TX and RX, and process the frames which
+		 * have already been received */
+		spin_lock_irqsave(&priv->lock, flags);
+		gfar_halt(dev);
+		gfar_clean_rx_ring(dev, priv->rx_ring_size);
+		spin_unlock_irqrestore(&priv->lock, flags);
 
-		/* Ok, all packets have been handled.  Now we bring it down,
-		 * change the ring size, and bring it up */
-
+		/* Now we take down the rings to rebuild them */
 		stop_gfar(dev);
 	}
 
+	/* Change the size */
 	priv->rx_ring_size = rvals->rx_pending;
 	priv->tx_ring_size = rvals->tx_pending;
 
+	/* Rebuild the rings with the new size */
+	if (dev->flags & IFF_UP)
+		err = startup_gfar(dev);
+
+	return err;
+}
+
+static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	int err = 0;
+
+	if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+		return -EOPNOTSUPP;
+
+	if (dev->flags & IFF_UP) {
+		unsigned long flags;
+
+		/* Halt TX and RX, and process the frames which
+		 * have already been received */
+		spin_lock_irqsave(&priv->lock, flags);
+		gfar_halt(dev);
+		gfar_clean_rx_ring(dev, priv->rx_ring_size);
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		/* Now we take down the rings to rebuild them */
+		stop_gfar(dev);
+	}
+
+	priv->rx_csum_enable = data;
+
 	if (dev->flags & IFF_UP)
 		err = startup_gfar(dev);
 
 	return err;
 }
 
+static uint32_t gfar_get_rx_csum(struct net_device *dev)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+
+	if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+		return 0;
+
+	return priv->rx_csum_enable;
+}
+
+static int gfar_set_tx_csum(struct net_device *dev, uint32_t data)
+{
+	unsigned long flags;
+	struct gfar_private *priv = netdev_priv(dev);
+
+	if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+		return -EOPNOTSUPP;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	gfar_halt(dev);
+
+	if (data)
+		dev->features |= NETIF_F_IP_CSUM;
+	else
+		dev->features &= ~NETIF_F_IP_CSUM;
+
+	gfar_start(dev);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static uint32_t gfar_get_tx_csum(struct net_device *dev)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+
+	if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+		return 0;
+
+	return (dev->features & NETIF_F_IP_CSUM) != 0;
+}
+
+static uint32_t gfar_get_msglevel(struct net_device *dev)
+{       
+	struct gfar_private *priv = netdev_priv(dev);
+	return priv->msg_enable;
+}       
+        
+static void gfar_set_msglevel(struct net_device *dev, uint32_t data)
+{       
+	struct gfar_private *priv = netdev_priv(dev);
+	priv->msg_enable = data;
+}
+
+
 struct ethtool_ops gfar_ethtool_ops = {
 	.get_settings = gfar_gsettings,
 	.get_drvinfo = gfar_gdrvinfo,
@@ -476,52 +547,10 @@
 	.get_strings = gfar_gstrings,
 	.get_stats_count = gfar_stats_count,
 	.get_ethtool_stats = gfar_fill_stats,
-};
-
-struct ethtool_ops gfar_normon_nocoalesce_ethtool_ops = {
-	.get_settings = gfar_gsettings,
-	.get_drvinfo = gfar_gdrvinfo,
-	.get_regs_len = gfar_reglen,
-	.get_regs = gfar_get_regs,
-	.get_link = ethtool_op_get_link,
-	.get_ringparam = gfar_gringparam,
-	.set_ringparam = gfar_sringparam,
-	.get_strings = gfar_gstrings_normon,
-	.get_stats_count = gfar_stats_count_normon,
-	.get_ethtool_stats = gfar_fill_stats_normon,
-};
-
-struct ethtool_ops gfar_nocoalesce_ethtool_ops = {
-	.get_settings = gfar_gsettings,
-	.get_drvinfo = gfar_gdrvinfo,
-	.get_regs_len = gfar_reglen,
-	.get_regs = gfar_get_regs,
-	.get_link = ethtool_op_get_link,
-	.get_ringparam = gfar_gringparam,
-	.set_ringparam = gfar_sringparam,
-	.get_strings = gfar_gstrings,
-	.get_stats_count = gfar_stats_count,
-	.get_ethtool_stats = gfar_fill_stats,
-};
-
-struct ethtool_ops gfar_normon_ethtool_ops = {
-	.get_settings = gfar_gsettings,
-	.get_drvinfo = gfar_gdrvinfo,
-	.get_regs_len = gfar_reglen,
-	.get_regs = gfar_get_regs,
-	.get_link = ethtool_op_get_link,
-	.get_coalesce = gfar_gcoalesce,
-	.set_coalesce = gfar_scoalesce,
-	.get_ringparam = gfar_gringparam,
-	.set_ringparam = gfar_sringparam,
-	.get_strings = gfar_gstrings_normon,
-	.get_stats_count = gfar_stats_count_normon,
-	.get_ethtool_stats = gfar_fill_stats_normon,
-};
-
-struct ethtool_ops *gfar_op_array[] = {
-	&gfar_ethtool_ops,
-	&gfar_normon_ethtool_ops,
-	&gfar_nocoalesce_ethtool_ops,
-	&gfar_normon_nocoalesce_ethtool_ops
+	.get_rx_csum = gfar_get_rx_csum,
+	.get_tx_csum = gfar_get_tx_csum,
+	.set_rx_csum = gfar_set_rx_csum,
+	.set_tx_csum = gfar_set_tx_csum,
+	.get_msglevel = gfar_get_msglevel,
+	.set_msglevel = gfar_set_msglevel,
 };