[PATCH] Gianfar update and sysfs support

This seems to have gotten lost, so I'll resend.

Signed-off-by: Andy Fleming <afleming@freescale.com>

* Added sysfs support to gianfar for modifying FIFO and stashing parameters
* Updated driver to support 10 Mbit, full duplex operation
* Improved comments throughout
* Cleaned up and optimized offloading code
* Fixed a bug where rx buffers were being improperly mapped and unmapped
* (only manifested if cache-coherency was off)
* Added support for using the eTSEC exact-match MAC registers
* Bumped the version to 1.3
* Added support for distinguishing between reduced 100 and 10 Mbit modes
* Modified default coalescing values to lower latency
* Added documentation
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 0f030b7..146f951 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -2,7 +2,8 @@
  * drivers/net/gianfar.c
  *
  * Gianfar Ethernet Driver
- * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * This driver is designed for the non-CPM ethernet controllers
+ * on the 85xx and 83xx family of integrated processors
  * Based on 8260_io/fcc_enet.c
  *
  * Author: Andy Fleming
@@ -22,8 +23,6 @@
  *  B-V +1.62
  *
  *  Theory of operation
- *  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
@@ -110,7 +109,7 @@
 #endif
 
 const char gfar_driver_name[] = "Gianfar Ethernet";
-const char gfar_driver_version[] = "1.2";
+const char gfar_driver_version[] = "1.3";
 
 static int gfar_enet_open(struct net_device *dev);
 static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
@@ -139,6 +138,10 @@
 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);
+void gfar_halt(struct net_device *dev);
+void gfar_start(struct net_device *dev);
+static void gfar_clear_exact_match(struct net_device *dev);
+static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr);
 
 extern struct ethtool_ops gfar_ethtool_ops;
 
@@ -146,12 +149,10 @@
 MODULE_DESCRIPTION("Gianfar Ethernet Driver");
 MODULE_LICENSE("GPL");
 
-int gfar_uses_fcb(struct gfar_private *priv)
+/* Returns 1 if incoming frames use an FCB */
+static inline int gfar_uses_fcb(struct gfar_private *priv)
 {
-	if (priv->vlan_enable || priv->rx_csum_enable)
-		return 1;
-	else
-		return 0;
+	return (priv->vlan_enable || priv->rx_csum_enable);
 }
 
 /* Set up the ethernet device structure, private data,
@@ -320,15 +321,10 @@
 	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
-	priv->rx_stash_size = STASH_LENGTH;
-#endif
 	priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
 	priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
 
@@ -350,6 +346,9 @@
 		goto register_fail;
 	}
 
+	/* Create all the sysfs files */
+	gfar_init_sysfs(dev);
+
 	/* Print out the device info */
 	printk(KERN_INFO DEVICE_NAME, dev->name);
 	for (idx = 0; idx < 6; idx++)
@@ -357,8 +356,7 @@
 	printk("\n");
 
 	/* Even more device info helps when determining which kernel */
-	/* provided which set of benchmarks.  Since this is global for all */
-	/* devices, we only print it once */
+	/* provided which set of benchmarks. */
 #ifdef CONFIG_GFAR_NAPI
 	printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
 #else
@@ -463,19 +461,9 @@
 	/* Initialize the max receive buffer length */
 	gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
 
-#ifdef CONFIG_GFAR_BUFSTASH
-	/* If we are stashing buffers, we need to set the
-	 * extraction length to the size of the buffer */
-	gfar_write(&priv->regs->attreli, priv->rx_stash_size << 16);
-#endif
-
 	/* Initialize the Minimum Frame Length Register */
 	gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS);
 
-	/* Setup Attributes so that snooping is on for rx */
-	gfar_write(&priv->regs->attr, ATTR_INIT_SETTINGS);
-	gfar_write(&priv->regs->attreli, ATTRELI_INIT_SETTINGS);
-
 	/* Assign the TBI an address which won't conflict with the PHYs */
 	gfar_write(&priv->regs->tbipa, TBIPA_VALUE);
 }
@@ -577,8 +565,7 @@
 		for (i = 0; i < priv->rx_ring_size; i++) {
 			if (priv->rx_skbuff[i]) {
 				dma_unmap_single(NULL, rxbdp->bufPtr,
-						priv->rx_buffer_size
-						+ RXBUF_ALIGNMENT,
+						priv->rx_buffer_size,
 						DMA_FROM_DEVICE);
 
 				dev_kfree_skb_any(priv->rx_skbuff[i]);
@@ -636,6 +623,7 @@
 	struct gfar *regs = priv->regs;
 	int err = 0;
 	u32 rctrl = 0;
+	u32 attrs = 0;
 
 	gfar_write(&regs->imask, IMASK_INIT_CLEAR);
 
@@ -795,18 +783,50 @@
 	if (priv->rx_csum_enable)
 		rctrl |= RCTRL_CHECKSUMMING;
 
-	if (priv->extended_hash)
+	if (priv->extended_hash) {
 		rctrl |= RCTRL_EXTHASH;
 
+		gfar_clear_exact_match(dev);
+		rctrl |= RCTRL_EMEN;
+	}
+
 	if (priv->vlan_enable)
 		rctrl |= RCTRL_VLAN;
 
+	if (priv->padding) {
+		rctrl &= ~RCTRL_PAL_MASK;
+		rctrl |= RCTRL_PADDING(priv->padding);
+	}
+
 	/* Init rctrl based on our settings */
 	gfar_write(&priv->regs->rctrl, rctrl);
 
 	if (dev->features & NETIF_F_IP_CSUM)
 		gfar_write(&priv->regs->tctrl, TCTRL_INIT_CSUM);
 
+	/* Set the extraction length and index */
+	attrs = ATTRELI_EL(priv->rx_stash_size) |
+		ATTRELI_EI(priv->rx_stash_index);
+
+	gfar_write(&priv->regs->attreli, attrs);
+
+	/* Start with defaults, and add stashing or locking
+	 * depending on the approprate variables */
+	attrs = ATTR_INIT_SETTINGS;
+
+	if (priv->bd_stash_en)
+		attrs |= ATTR_BDSTASH;
+
+	if (priv->rx_stash_size != 0)
+		attrs |= ATTR_BUFSTASH;
+
+	gfar_write(&priv->regs->attr, attrs);
+
+	gfar_write(&priv->regs->fifo_tx_thr, priv->fifo_threshold);
+	gfar_write(&priv->regs->fifo_tx_starve, priv->fifo_starve);
+	gfar_write(&priv->regs->fifo_tx_starve_shutoff, priv->fifo_starve_off);
+
+	/* Start the controller */
 	gfar_start(dev);
 
 	return 0;
@@ -851,34 +871,32 @@
 	return err;
 }
 
-static struct txfcb *gfar_add_fcb(struct sk_buff *skb, struct txbd8 *bdp)
+static inline 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;
+	u8 flags = 0;
 
 	/* 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;
+	flags = TXFCB_DEFAULT;
 
-	/* Notify the controller what the protocol is */
-	if (skb->nh.iph->protocol == IPPROTO_UDP)
-		fcb->udp = 1;
+	/* Tell the controller what the protocol is */
+	/* And provide the already calculated phcs */
+	if (skb->nh.iph->protocol == IPPROTO_UDP) {
+		flags |= TXFCB_UDP;
+		fcb->phcs = skb->h.uh->check;
+	} else
+		fcb->phcs = skb->h.th->check;
 
 	/* l3os is the distance between the start of the
 	 * frame (skb->data) and the start of the IP hdr.
@@ -887,17 +905,12 @@
 	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);
+	fcb->flags = flags;
 }
 
-void gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb)
+void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb)
 {
-	fcb->vln = 1;
+	fcb->flags |= TXFCB_VLN;
 	fcb->vlctl = vlan_tx_tag_get(skb);
 }
 
@@ -908,6 +921,7 @@
 	struct gfar_private *priv = netdev_priv(dev);
 	struct txfcb *fcb = NULL;
 	struct txbd8 *txbdp;
+	u16 status;
 
 	/* Update transmit stats */
 	priv->stats.tx_bytes += skb->len;
@@ -919,19 +933,22 @@
 	txbdp = priv->cur_tx;
 
 	/* Clear all but the WRAP status flags */
-	txbdp->status &= TXBD_WRAP;
+	status = txbdp->status & TXBD_WRAP;
 
 	/* Set up checksumming */
-	if ((dev->features & NETIF_F_IP_CSUM)
-			&& (CHECKSUM_HW == skb->ip_summed)) {
+	if (likely((dev->features & NETIF_F_IP_CSUM)
+			&& (CHECKSUM_HW == skb->ip_summed))) {
 		fcb = gfar_add_fcb(skb, txbdp);
+		status |= TXBD_TOE;
 		gfar_tx_checksum(skb, fcb);
 	}
 
 	if (priv->vlan_enable &&
 			unlikely(priv->vlgrp && vlan_tx_tag_present(skb))) {
-		if (NULL == fcb)
+		if (unlikely(NULL == fcb)) {
 			fcb = gfar_add_fcb(skb, txbdp);
+			status |= TXBD_TOE;
+		}
 
 		gfar_tx_vlan(skb, fcb);
 	}
@@ -949,14 +966,16 @@
 	    (priv->skb_curtx + 1) & TX_RING_MOD_MASK(priv->tx_ring_size);
 
 	/* Flag the BD as interrupt-causing */
-	txbdp->status |= TXBD_INTERRUPT;
+	status |= TXBD_INTERRUPT;
 
 	/* Flag the BD as ready to go, last in frame, and  */
 	/* in need of CRC */
-	txbdp->status |= (TXBD_READY | TXBD_LAST | TXBD_CRC);
+	status |= (TXBD_READY | TXBD_LAST | TXBD_CRC);
 
 	dev->trans_start = jiffies;
 
+	txbdp->status = status;
+
 	/* If this was the last BD in the ring, the next one */
 	/* is at the beginning of the ring */
 	if (txbdp->status & TXBD_WRAP)
@@ -1010,21 +1029,7 @@
 /* Changes the mac address if the controller is not running. */
 int gfar_set_mac_address(struct net_device *dev)
 {
-	struct gfar_private *priv = netdev_priv(dev);
-	int i;
-	char tmpbuf[MAC_ADDR_LEN];
-	u32 tempval;
-
-	/* Now copy it into the mac registers backwards, cuz */
-	/* little endian is silly */
-	for (i = 0; i < MAC_ADDR_LEN; i++)
-		tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->dev_addr[i];
-
-	gfar_write(&priv->regs->macstnaddr1, *((u32 *) (tmpbuf)));
-
-	tempval = *((u32 *) (tmpbuf + 4));
-
-	gfar_write(&priv->regs->macstnaddr2, tempval);
+	gfar_set_mac_for_addr(dev, 0, dev->dev_addr);
 
 	return 0;
 }
@@ -1110,7 +1115,7 @@
 	    INCREMENTAL_BUFFER_SIZE;
 
 	/* Only stop and start the controller if it isn't already
-	 * stopped */
+	 * stopped, and we changed something */
 	if ((oldsize != tempsize) && (dev->flags & IFF_UP))
 		stop_gfar(dev);
 
@@ -1220,6 +1225,7 @@
 
 struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
 {
+	unsigned int alignamount;
 	struct gfar_private *priv = netdev_priv(dev);
 	struct sk_buff *skb = NULL;
 	unsigned int timeout = SKB_ALLOC_TIMEOUT;
@@ -1231,18 +1237,18 @@
 	if (NULL == skb)
 		return NULL;
 
+	alignamount = RXBUF_ALIGNMENT -
+		(((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1));
+
 	/* We need the data buffer to be aligned properly.  We will reserve
 	 * as many bytes as needed to align the data properly
 	 */
-	skb_reserve(skb,
-		    RXBUF_ALIGNMENT -
-		    (((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1)));
+	skb_reserve(skb, alignamount);
 
 	skb->dev = dev;
 
 	bdp->bufPtr = dma_map_single(NULL, skb->data,
-			priv->rx_buffer_size + RXBUF_ALIGNMENT,
-			DMA_FROM_DEVICE);
+			priv->rx_buffer_size, DMA_FROM_DEVICE);
 
 	bdp->length = 0;
 
@@ -1350,7 +1356,7 @@
 	/* 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)
+	if ((fcb->flags & RXFCB_CSUM_MASK) == (RXFCB_CIP | RXFCB_CTU))
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	else
 		skb->ip_summed = CHECKSUM_NONE;
@@ -1401,7 +1407,7 @@
 		skb->protocol = eth_type_trans(skb, dev);
 
 		/* Send the packet up the stack */
-		if (unlikely(priv->vlgrp && fcb->vln))
+		if (unlikely(priv->vlgrp && (fcb->flags & RXFCB_VLN)))
 			ret = gfar_rx_vlan(skb, priv->vlgrp, fcb->vlctl);
 		else
 			ret = RECEIVE(skb);
@@ -1620,6 +1626,7 @@
 	spin_lock_irqsave(&priv->lock, flags);
 	if (phydev->link) {
 		u32 tempval = gfar_read(&regs->maccfg2);
+		u32 ecntrl = gfar_read(&regs->ecntrl);
 
 		/* Now we make sure that we can be in full duplex mode.
 		 * If not, we operate in half-duplex mode. */
@@ -1644,6 +1651,13 @@
 			case 10:
 				tempval =
 				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
+
+				/* Reduced mode distinguishes
+				 * between 10 and 100 */
+				if (phydev->speed == SPEED_100)
+					ecntrl |= ECNTRL_R100;
+				else
+					ecntrl &= ~(ECNTRL_R100);
 				break;
 			default:
 				if (netif_msg_link(priv))
@@ -1657,6 +1671,7 @@
 		}
 
 		gfar_write(&regs->maccfg2, tempval);
+		gfar_write(&regs->ecntrl, ecntrl);
 
 		if (!priv->oldlink) {
 			new_state = 1;
@@ -1721,6 +1736,9 @@
 		gfar_write(&regs->gaddr6, 0xffffffff);
 		gfar_write(&regs->gaddr7, 0xffffffff);
 	} else {
+		int em_num;
+		int idx;
+
 		/* zero out the hash */
 		gfar_write(&regs->igaddr0, 0x0);
 		gfar_write(&regs->igaddr1, 0x0);
@@ -1739,18 +1757,47 @@
 		gfar_write(&regs->gaddr6, 0x0);
 		gfar_write(&regs->gaddr7, 0x0);
 
+		/* If we have extended hash tables, we need to
+		 * clear the exact match registers to prepare for
+		 * setting them */
+		if (priv->extended_hash) {
+			em_num = GFAR_EM_NUM + 1;
+			gfar_clear_exact_match(dev);
+			idx = 1;
+		} else {
+			idx = 0;
+			em_num = 0;
+		}
+
 		if(dev->mc_count == 0)
 			return;
 
 		/* Parse the list, and set the appropriate bits */
 		for(mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) {
-			gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr);
+			if (idx < em_num) {
+				gfar_set_mac_for_addr(dev, idx,
+						mc_ptr->dmi_addr);
+				idx++;
+			} else
+				gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr);
 		}
 	}
 
 	return;
 }
 
+
+/* Clears each of the exact match registers to zero, so they
+ * don't interfere with normal reception */
+static void gfar_clear_exact_match(struct net_device *dev)
+{
+	int idx;
+	u8 zero_arr[MAC_ADDR_LEN] = {0,0,0,0,0,0};
+
+	for(idx = 1;idx < GFAR_EM_NUM + 1;idx++)
+		gfar_set_mac_for_addr(dev, idx, (u8 *)zero_arr);
+}
+
 /* Set the appropriate hash bit for the given addr */
 /* The algorithm works like so:
  * 1) Take the Destination Address (ie the multicast address), and
@@ -1781,6 +1828,32 @@
 	return;
 }
 
+
+/* There are multiple MAC Address register pairs on some controllers
+ * This function sets the numth pair to a given address
+ */
+static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	int idx;
+	char tmpbuf[MAC_ADDR_LEN];
+	u32 tempval;
+	u32 *macptr = &priv->regs->macstnaddr1;
+
+	macptr += num*2;
+
+	/* Now copy it into the mac registers backwards, cuz */
+	/* little endian is silly */
+	for (idx = 0; idx < MAC_ADDR_LEN; idx++)
+		tmpbuf[MAC_ADDR_LEN - 1 - idx] = addr[idx];
+
+	gfar_write(macptr, *((u32 *) (tmpbuf)));
+
+	tempval = *((u32 *) (tmpbuf + 4));
+
+	gfar_write(macptr+1, tempval);
+}
+
 /* GFAR error interrupt handler */
 static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs)
 {