[PATCH] Gianfar update and sysfs support
authorAndy Fleming <afleming@freescale.com>
Fri, 11 Nov 2005 18:38:59 +0000 (12:38 -0600)
committerJeff Garzik <jgarzik@pobox.com>
Fri, 18 Nov 2005 18:31:26 +0000 (13:31 -0500)
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>
Documentation/networking/gianfar.txt [new file with mode: 0644]
drivers/net/Makefile
drivers/net/gianfar.c
drivers/net/gianfar.h
drivers/net/gianfar_ethtool.c
drivers/net/gianfar_mii.h
drivers/net/gianfar_sysfs.c [new file with mode: 0644]

diff --git a/Documentation/networking/gianfar.txt b/Documentation/networking/gianfar.txt
new file mode 100644 (file)
index 0000000..ad474ea
--- /dev/null
@@ -0,0 +1,72 @@
+The Gianfar Ethernet Driver
+Sysfs File description
+
+Author: Andy Fleming <afleming@freescale.com>
+Updated: 2005-07-28
+
+SYSFS
+
+Several of the features of the gianfar driver are controlled
+through sysfs files.  These are:
+
+bd_stash:
+To stash RX Buffer Descriptors in the L2, echo 'on' or '1' to
+bd_stash, echo 'off' or '0' to disable
+
+rx_stash_len:
+To stash the first n bytes of the packet in L2, echo the number
+of bytes to buf_stash_len.  echo 0 to disable.
+
+WARNING: You could really screw these up if you set them too low or high!
+fifo_threshold:
+To change the number of bytes the controller needs in the
+fifo before it starts transmission, echo the number of bytes to 
+fifo_thresh.  Range should be 0-511.
+
+fifo_starve:
+When the FIFO has less than this many bytes during a transmit, it
+enters starve mode, and increases the priority of TX memory
+transactions.  To change, echo the number of bytes to
+fifo_starve.  Range should be 0-511.
+
+fifo_starve_off:
+Once in starve mode, the FIFO remains there until it has this
+many bytes.  To change, echo the number of bytes to
+fifo_starve_off.  Range should be 0-511.
+
+CHECKSUM OFFLOADING
+
+The eTSEC controller (first included in parts from late 2005 like
+the 8548) has the ability to perform TCP, UDP, and IP checksums
+in hardware.  The Linux kernel only offloads the TCP and UDP
+checksums (and always performs the pseudo header checksums), so
+the driver only supports checksumming for TCP/IP and UDP/IP
+packets.  Use ethtool to enable or disable this feature for RX
+and TX.
+
+VLAN
+
+In order to use VLAN, please consult Linux documentation on
+configuring VLANs.  The gianfar driver supports hardware insertion and
+extraction of VLAN headers, but not filtering.  Filtering will be
+done by the kernel.
+
+MULTICASTING
+
+The gianfar driver supports using the group hash table on the
+TSEC (and the extended hash table on the eTSEC) for multicast
+filtering.  On the eTSEC, the exact-match MAC registers are used
+before the hash tables.  See Linux documentation on how to join
+multicast groups.
+
+PADDING
+
+The gianfar driver supports padding received frames with 2 bytes
+to align the IP header to a 16-byte boundary, when supported by
+hardware.
+
+ETHTOOL
+
+The gianfar driver supports the use of ethtool for many
+configuration options.  You must run ethtool only on currently
+open interfaces.  See ethtool documentation for details.
index 27822a2..5056814 100644 (file)
@@ -13,7 +13,10 @@ obj-$(CONFIG_CHELSIO_T1) += chelsio/
 obj-$(CONFIG_BONDING) += bonding/
 obj-$(CONFIG_GIANFAR) += gianfar_driver.o
 
-gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_mii.o
+gianfar_driver-objs := gianfar.o \
+               gianfar_ethtool.o \
+               gianfar_mii.o \
+               gianfar_sysfs.o
 
 #
 # link order important here
index 0f030b7..146f951 100644 (file)
@@ -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
 #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 int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int l
 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_AUTHOR("Freescale Semiconductor, Inc");
 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 @@ static int gfar_probe(struct platform_device *pdev)
        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 @@ static int gfar_probe(struct platform_device *pdev)
                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 @@ static int gfar_probe(struct platform_device *pdev)
        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 @@ static void init_registers(struct net_device *dev)
        /* 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 @@ static void free_skb_resources(struct gfar_private *priv)
                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 @@ int startup_gfar(struct net_device *dev)
        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 @@ int startup_gfar(struct net_device *dev)
        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 @@ static int gfar_enet_open(struct net_device *dev)
        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 @@ static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb)
        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 @@ 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;
+       u16 status;
 
        /* Update transmit stats */
        priv->stats.tx_bytes += skb->len;
@@ -919,19 +933,22 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
        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 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
            (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 @@ static struct net_device_stats * gfar_get_stats(struct net_device *dev)
 /* 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 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
            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 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs)
 
 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 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
        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 @@ 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)
+       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 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
                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 @@ static void adjust_link(struct net_device *dev)
        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 @@ static void adjust_link(struct net_device *dev)
                        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 @@ static void adjust_link(struct net_device *dev)
                }
 
                gfar_write(&regs->maccfg2, tempval);
+               gfar_write(&regs->ecntrl, ecntrl);
 
                if (!priv->oldlink) {
                        new_state = 1;
@@ -1721,6 +1736,9 @@ static void gfar_set_multi(struct net_device *dev)
                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 @@ static void gfar_set_multi(struct net_device *dev)
                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 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
        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)
 {
index 5065ba8..94a91da 100644 (file)
@@ -90,12 +90,26 @@ extern const char gfar_driver_version[];
 #define GFAR_RX_MAX_RING_SIZE   256
 #define GFAR_TX_MAX_RING_SIZE   256
 
+#define GFAR_MAX_FIFO_THRESHOLD 511
+#define GFAR_MAX_FIFO_STARVE   511
+#define GFAR_MAX_FIFO_STARVE_OFF 511
+
 #define DEFAULT_RX_BUFFER_SIZE  1536
 #define TX_RING_MOD_MASK(size) (size-1)
 #define RX_RING_MOD_MASK(size) (size-1)
 #define JUMBO_BUFFER_SIZE 9728
 #define JUMBO_FRAME_SIZE 9600
 
+#define DEFAULT_FIFO_TX_THR 0x100
+#define DEFAULT_FIFO_TX_STARVE 0x40
+#define DEFAULT_FIFO_TX_STARVE_OFF 0x80
+#define DEFAULT_BD_STASH 1
+#define DEFAULT_STASH_LENGTH   64
+#define DEFAULT_STASH_INDEX    0
+
+/* The number of Exact Match registers */
+#define GFAR_EM_NUM    15
+
 /* Latency of interface clock in nanoseconds */
 /* Interface clock latency , in this case, means the
  * time described by a value of 1 in the interrupt
@@ -112,11 +126,11 @@ extern const char gfar_driver_version[];
 
 #define DEFAULT_TX_COALESCE 1
 #define DEFAULT_TXCOUNT        16
-#define DEFAULT_TXTIME 400
+#define DEFAULT_TXTIME 4
 
 #define DEFAULT_RX_COALESCE 1
 #define DEFAULT_RXCOUNT        16
-#define DEFAULT_RXTIME 400
+#define DEFAULT_RXTIME 4
 
 #define TBIPA_VALUE            0x1f
 #define MIIMCFG_INIT_VALUE     0x00000007
@@ -147,6 +161,7 @@ extern const char gfar_driver_version[];
 
 #define ECNTRL_INIT_SETTINGS   0x00001000
 #define ECNTRL_TBI_MODE         0x00000020
+#define ECNTRL_R100            0x00000008
 
 #define MRBLR_INIT_SETTINGS    DEFAULT_RX_BUFFER_SIZE
 
@@ -181,10 +196,12 @@ extern const char gfar_driver_version[];
 #define RCTRL_PRSDEP_MASK      0x000000c0
 #define RCTRL_PRSDEP_INIT      0x000000c0
 #define RCTRL_PROM             0x00000008
+#define RCTRL_EMEN             0x00000002
 #define RCTRL_CHECKSUMMING     (RCTRL_IPCSEN \
                | RCTRL_TUCSEN | RCTRL_PRSDEP_INIT)
 #define RCTRL_EXTHASH          (RCTRL_GHTX)
 #define RCTRL_VLAN             (RCTRL_PRSDEP_INIT)
+#define RCTRL_PADDING(x)       ((x << 16) & RCTRL_PAL_MASK)
 
 
 #define RSTAT_CLEAR_RHALT       0x00800000
@@ -251,28 +268,26 @@ extern const char gfar_driver_version[];
                IMASK_XFUN | IMASK_RXC | IMASK_BABT | IMASK_DPE \
                | IMASK_PERR)
 
+/* Fifo management */
+#define FIFO_TX_THR_MASK       0x01ff
+#define FIFO_TX_STARVE_MASK    0x01ff
+#define FIFO_TX_STARVE_OFF_MASK        0x01ff
 
 /* Attribute fields */
 
 /* This enables rx snooping for buffers and descriptors */
-#ifdef CONFIG_GFAR_BDSTASH
 #define ATTR_BDSTASH           0x00000800
-#else
-#define ATTR_BDSTASH           0x00000000
-#endif
 
-#ifdef CONFIG_GFAR_BUFSTASH
 #define ATTR_BUFSTASH          0x00004000
-#define STASH_LENGTH           64
-#else
-#define ATTR_BUFSTASH          0x00000000
-#endif
 
 #define ATTR_SNOOPING          0x000000c0
-#define ATTR_INIT_SETTINGS      (ATTR_SNOOPING \
-               | ATTR_BDSTASH | ATTR_BUFSTASH)
+#define ATTR_INIT_SETTINGS      ATTR_SNOOPING
 
 #define ATTRELI_INIT_SETTINGS   0x0
+#define ATTRELI_EL_MASK                0x3fff0000
+#define ATTRELI_EL(x) (x << 16)
+#define ATTRELI_EI_MASK                0x00003fff
+#define ATTRELI_EI(x) (x)
 
 
 /* TxBD status field bits */
@@ -328,6 +343,7 @@ extern const char gfar_driver_version[];
 #define RXFCB_CTU              0x0400
 #define RXFCB_EIP              0x0200
 #define RXFCB_ETU              0x0100
+#define RXFCB_CSUM_MASK                0x0f00
 #define RXFCB_PERR_MASK                0x000c
 #define RXFCB_PERR_BADL3       0x0008
 
@@ -339,14 +355,7 @@ struct txbd8
 };
 
 struct txfcb {
-       u8      vln:1,
-               ip:1,
-               ip6:1,
-               tup:1,
-               udp:1,
-               cip:1,
-               ctu:1,
-               nph:1;
+       u8      flags;
        u8      reserved;
        u8      l4os;   /* Level 4 Header Offset */
        u8      l3os;   /* Level 3 Header Offset */
@@ -362,14 +371,7 @@ struct rxbd8
 };
 
 struct rxfcb {
-       u16     vln:1,
-               ip:1,
-               ip6:1,
-               tup:1,
-               cip:1,
-               ctu:1,
-               eip:1,
-               etu:1;
+       u16     flags;
        u8      rq;     /* Receive Queue index */
        u8      pro;    /* Layer 4 Protocol */
        u16     reserved;
@@ -688,12 +690,17 @@ struct gfar_private {
        spinlock_t lock;
        unsigned int rx_buffer_size;
        unsigned int rx_stash_size;
+       unsigned int rx_stash_index;
        unsigned int tx_ring_size;
        unsigned int rx_ring_size;
+       unsigned int fifo_threshold;
+       unsigned int fifo_starve;
+       unsigned int fifo_starve_off;
 
        unsigned char vlan_enable:1,
                rx_csum_enable:1,
-               extended_hash:1;
+               extended_hash:1,
+               bd_stash_en:1;
        unsigned short padding;
        struct vlan_group *vlgrp;
        /* Info structure initialized by board setup code */
@@ -731,6 +738,6 @@ extern void stop_gfar(struct net_device *dev);
 extern void gfar_halt(struct net_device *dev);
 extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
                int enable, u32 regnum, u32 read);
-void gfar_setup_stashing(struct net_device *dev);
+void gfar_init_sysfs(struct net_device *dev);
 
 #endif /* __GIANFAR_H */
index cfa3cd7..765e810 100644 (file)
@@ -125,7 +125,7 @@ static char stat_gstrings[][ETH_GSTRING_LEN] = {
 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
index e85eb21..d527cf2 100644 (file)
@@ -24,6 +24,7 @@
 #define MII_READ_COMMAND       0x00000001
 
 #define GFAR_SUPPORTED (SUPPORTED_10baseT_Half \
+               | SUPPORTED_10baseT_Full \
                | SUPPORTED_100baseT_Half \
                | SUPPORTED_100baseT_Full \
                | SUPPORTED_Autoneg \
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
new file mode 100644 (file)
index 0000000..10d34cb
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * drivers/net/gianfar_sysfs.c
+ *
+ * Gianfar Ethernet Driver
+ * 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
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright (c) 2002-2005 Freescale Semiconductor, Inc.
+ *
+ * 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 (at your
+ * option) any later version.
+ *
+ * Sysfs file creation and management
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include "gianfar.h"
+
+#define GFAR_ATTR(_name) \
+static ssize_t gfar_show_##_name(struct class_device *cdev, char *buf); \
+static ssize_t gfar_set_##_name(struct class_device *cdev, \
+               const char *buf, size_t count); \
+static CLASS_DEVICE_ATTR(_name, 0644, gfar_show_##_name, gfar_set_##_name)
+
+#define GFAR_CREATE_FILE(_dev, _name) \
+       class_device_create_file(&_dev->class_dev, &class_device_attr_##_name)
+
+GFAR_ATTR(bd_stash);
+GFAR_ATTR(rx_stash_size);
+GFAR_ATTR(rx_stash_index);
+GFAR_ATTR(fifo_threshold);
+GFAR_ATTR(fifo_starve);
+GFAR_ATTR(fifo_starve_off);
+
+#define to_net_dev(cd) container_of(cd, struct net_device, class_dev)
+
+static ssize_t gfar_show_bd_stash(struct class_device *cdev, char *buf)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+
+       return sprintf(buf, "%s\n", priv->bd_stash_en? "on" : "off");
+}
+
+static ssize_t gfar_set_bd_stash(struct class_device *cdev,
+               const char *buf, size_t count)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       int new_setting = 0;
+       u32 temp;
+       unsigned long flags;
+
+       /* Find out the new setting */
+       if (!strncmp("on", buf, count-1) || !strncmp("1", buf, count-1))
+               new_setting = 1;
+       else if (!strncmp("off", buf, count-1) || !strncmp("0", buf, count-1))
+               new_setting = 0;
+       else
+               return count;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       /* Set the new stashing value */
+       priv->bd_stash_en = new_setting;
+
+       temp = gfar_read(&priv->regs->attr);
+       
+       if (new_setting)
+               temp |= ATTR_BDSTASH;
+       else
+               temp &= ~(ATTR_BDSTASH);
+
+       gfar_write(&priv->regs->attr, temp);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return count;
+}
+
+static ssize_t gfar_show_rx_stash_size(struct class_device *cdev, char *buf)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+
+       return sprintf(buf, "%d\n", priv->rx_stash_size);
+}
+
+static ssize_t gfar_set_rx_stash_size(struct class_device *cdev,
+               const char *buf, size_t count)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       unsigned int length = simple_strtoul(buf, NULL, 0);
+       u32 temp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (length > priv->rx_buffer_size)
+               return count;
+
+       if (length == priv->rx_stash_size)
+               return count;
+
+       priv->rx_stash_size = length;
+
+       temp = gfar_read(&priv->regs->attreli);
+       temp &= ~ATTRELI_EL_MASK;
+       temp |= ATTRELI_EL(length);
+       gfar_write(&priv->regs->attreli, temp);
+
+       /* Turn stashing on/off as appropriate */
+       temp = gfar_read(&priv->regs->attr);
+
+       if (length)
+               temp |= ATTR_BUFSTASH;
+       else
+               temp &= ~(ATTR_BUFSTASH);
+
+       gfar_write(&priv->regs->attr, temp);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return count;
+}
+
+
+/* Stashing will only be enabled when rx_stash_size != 0 */
+static ssize_t gfar_show_rx_stash_index(struct class_device *cdev, char *buf)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+
+       return sprintf(buf, "%d\n", priv->rx_stash_index);
+}
+
+static ssize_t gfar_set_rx_stash_index(struct class_device *cdev,
+               const char *buf, size_t count)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       unsigned short index = simple_strtoul(buf, NULL, 0);
+       u32 temp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (index > priv->rx_stash_size)
+               return count;
+
+       if (index == priv->rx_stash_index)
+               return count;
+
+       priv->rx_stash_index = index;
+
+       temp = gfar_read(&priv->regs->attreli);
+       temp &= ~ATTRELI_EI_MASK;
+       temp |= ATTRELI_EI(index);
+       gfar_write(&priv->regs->attreli, flags);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return count;
+}
+
+static ssize_t gfar_show_fifo_threshold(struct class_device *cdev, char *buf)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+
+       return sprintf(buf, "%d\n", priv->fifo_threshold);
+}
+
+static ssize_t gfar_set_fifo_threshold(struct class_device *cdev,
+               const char *buf, size_t count)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       unsigned int length = simple_strtoul(buf, NULL, 0);
+       u32 temp;
+       unsigned long flags;
+
+       if (length > GFAR_MAX_FIFO_THRESHOLD)
+               return count;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       priv->fifo_threshold = length;
+
+       temp = gfar_read(&priv->regs->fifo_tx_thr);
+       temp &= ~FIFO_TX_THR_MASK;
+       temp |= length;
+       gfar_write(&priv->regs->fifo_tx_thr, temp);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return count;
+}
+
+static ssize_t gfar_show_fifo_starve(struct class_device *cdev, char *buf)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+
+       return sprintf(buf, "%d\n", priv->fifo_starve);
+}
+
+
+static ssize_t gfar_set_fifo_starve(struct class_device *cdev,
+               const char *buf, size_t count)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       unsigned int num = simple_strtoul(buf, NULL, 0);
+       u32 temp;
+       unsigned long flags;
+
+       if (num > GFAR_MAX_FIFO_STARVE)
+               return count;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       priv->fifo_starve = num;
+
+       temp = gfar_read(&priv->regs->fifo_tx_starve);
+       temp &= ~FIFO_TX_STARVE_MASK;
+       temp |= num;
+       gfar_write(&priv->regs->fifo_tx_starve, temp);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return count;
+}
+
+static ssize_t gfar_show_fifo_starve_off(struct class_device *cdev, char *buf)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+
+       return sprintf(buf, "%d\n", priv->fifo_starve_off);
+}
+
+static ssize_t gfar_set_fifo_starve_off(struct class_device *cdev,
+               const char *buf, size_t count)
+{
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       unsigned int num = simple_strtoul(buf, NULL, 0);
+       u32 temp;
+       unsigned long flags;
+
+       if (num > GFAR_MAX_FIFO_STARVE_OFF)
+               return count;
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       priv->fifo_starve_off = num;
+
+       temp = gfar_read(&priv->regs->fifo_tx_starve_shutoff);
+       temp &= ~FIFO_TX_STARVE_OFF_MASK;
+       temp |= num;
+       gfar_write(&priv->regs->fifo_tx_starve_shutoff, temp);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return count;
+}
+
+void gfar_init_sysfs(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+
+       /* Initialize the default values */
+       priv->rx_stash_size = DEFAULT_STASH_LENGTH;
+       priv->rx_stash_index = DEFAULT_STASH_INDEX;
+       priv->fifo_threshold = DEFAULT_FIFO_TX_THR;
+       priv->fifo_starve = DEFAULT_FIFO_TX_STARVE;
+       priv->fifo_starve_off = DEFAULT_FIFO_TX_STARVE_OFF;
+       priv->bd_stash_en = DEFAULT_BD_STASH;
+
+       /* Create our sysfs files */
+       GFAR_CREATE_FILE(dev, bd_stash);
+       GFAR_CREATE_FILE(dev, rx_stash_size);
+       GFAR_CREATE_FILE(dev, rx_stash_index);
+       GFAR_CREATE_FILE(dev, fifo_threshold);
+       GFAR_CREATE_FILE(dev, fifo_starve);
+       GFAR_CREATE_FILE(dev, fifo_starve_off);
+
+}