This patch exposes certain features of the gianfar ethernet driver through 
sysfs so that they can be configured at runtime.

These include allocating and/or locking buffers and buffer descriptors in 
the L2 cache, and modifying the threshold in the FIFO at which the 
controller starts transmitting.

This patch also enables the gianfar driver to use the eTSEC's exact-match 
MAC address registers to filter multicast traffic, and only use the hash 
table if there are more addresses than those registers can support.

This patch replaces the previous patch called "sysfs feature enablement 
for gianfar".

Signed-off-by: Andy Fleming <[EMAIL PROTECTED]>

* Merged stashing/sysfs/exact match support into PHY tree

---
commit c65b921e4720850c7532f95caf2dc9341b26efa2
tree 8822bc37587224d6dd54ea3aae070f45b123e55d
parent d58caeb7c62c4f2b6c6f81ad6ee4537f0ba5ba60
author Andrew Fleming <[EMAIL PROTECTED]> Wed, 03 Aug 2005 13:42:02 -0500
committer Andrew Fleming <[EMAIL PROTECTED]> Wed, 03 Aug 2005 13:42:02 -0500

 drivers/net/gianfar.c |  381 ++++++++++++++++++++++++++++++++++++++++++++-----
 drivers/net/gianfar.h |   38 +++--
 2 files changed, 370 insertions(+), 49 deletions(-)

diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
--- 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
@@ -140,6 +139,10 @@ static int gfar_process_frame(struct net
 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;
 
@@ -147,12 +150,242 @@ MODULE_AUTHOR("Freescale Semiconductor, 
 MODULE_DESCRIPTION("Gianfar Ethernet Driver");
 MODULE_LICENSE("GPL");
 
-int gfar_uses_fcb(struct gfar_private *priv)
+#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(bd_lock);
+GFAR_ATTR(rx_buf_lock);
+GFAR_ATTR(fifo_threshold);
+
+#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;
+
+       /* 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;
+
+       /* Set the new stashing value */
+       priv->bd_stash_en = new_setting;
+
+       temp = gfar_read(&priv->regs->attr);
+       temp &= ~(ATTR_BDLOCK);
+       
+       if (new_setting) {
+               temp |= ATTR_BDSTASH;
+
+               if (priv->bd_lock_en)
+                       temp |= ATTR_BDLOCK;
+       }
+
+       gfar_write(&priv->regs->attr, temp);
+
+       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;
+
+       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);
+       temp &= ~ATTR_BUFLOCK;
+
+       if (length) {
+               temp |= ATTR_BUFSTASH;
+
+               if (priv->rx_buf_lock)
+                       temp |= ATTR_BUFLOCK;
+       }
+
+       gfar_write(&priv->regs->attr, temp);
+
+       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;
+
+       if (length > GFAR_MAX_FIFO_THRESHOLD)
+               return count;
+
+       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);
+
+       return count;
+}
+
+static ssize_t gfar_show_bd_lock(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_lock_en? "on" : "off");
+}
+
+static ssize_t gfar_set_bd_lock(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;
+
+       /* 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;
+
+       /* Set the new locking value */
+       priv->bd_lock_en = new_setting;
+
+       temp = gfar_read(&priv->regs->attr);
+       temp &= ~(ATTR_BDLOCK);
+       
+       if (new_setting) {
+               temp |= ATTR_BDLOCK;
+
+               priv->bd_stash_en = 1;
+       }
+
+       gfar_write(&priv->regs->attr, temp);
+
+       return count;
+}
+
+static ssize_t gfar_show_rx_buf_lock(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->rx_buf_lock? "on" : "off");
+}
+
+static ssize_t gfar_set_rx_buf_lock(struct class_device *cdev,
+               const char *buf, size_t count)
 {
-       if (priv->vlan_enable || priv->rx_csum_enable)
-               return 1;
+       struct net_device *dev = to_net_dev(cdev);
+       struct gfar_private *priv = netdev_priv(dev);
+       int new_setting = 0;
+       u32 temp;
+
+       /* 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 0;
+               return count;
+
+       /* Set the new locking value */
+       priv->rx_buf_lock = new_setting;
+
+       temp = gfar_read(&priv->regs->attr);
+
+       /* If stashing is on, we just enable or disable locking,
+        * depending on the new setting.  If stashing was off,
+        * then we need to enable it, and set the extraction length
+        * to the driver's default value.
+        */
+       if (priv->rx_stash_size) {
+               temp &= ~(ATTR_BUFLOCK);
+
+               if (new_setting)
+                       temp |= ATTR_BUFLOCK;
+               else
+                       temp |= ATTR_BUFSTASH;
+       } else if (new_setting) {
+               u32 attreli;
+
+               temp |= ATTR_BUFLOCK;
+
+               priv->rx_stash_size = DEFAULT_STASH_LENGTH;
+
+               attreli = gfar_read(&priv->regs->attreli);
+               attreli |= ATTRELI_EL(priv->rx_stash_size);
+               gfar_write(&priv->regs->attreli, attreli);
+       }
+       
+       gfar_write(&priv->regs->attr, temp);
+
+       return count;
+}
+
+/* Returns 1 if incoming frames use an FCB */
+static inline int gfar_uses_fcb(struct gfar_private *priv)
+{
+       return (priv->vlan_enable || priv->rx_csum_enable);
 }
 
 /* Set up the ethernet device structure, private data,
@@ -328,12 +561,15 @@ static int gfar_probe(struct device *dev
                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->rx_stash_size = DEFAULT_STASH_LENGTH;
        priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
        priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
 
+       priv->fifo_threshold = DEFAULT_FIFO_TX_THR;
+       priv->bd_stash_en = DEFAULT_BD_STASH;
+       priv->rx_buf_lock = DEFAULT_RX_BUF_LOCK;
+       priv->bd_lock_en = DEFAULT_BD_LOCK;
+
        priv->txcoalescing = DEFAULT_TX_COALESCE;
        priv->txcount = DEFAULT_TXCOUNT;
        priv->txtime = DEFAULT_TXTIME;
@@ -352,6 +588,13 @@ static int gfar_probe(struct device *dev
                goto register_fail;
        }
 
+       /* Create our sysfs files */
+       GFAR_CREATE_FILE(dev, bd_stash);
+       GFAR_CREATE_FILE(dev, bd_lock);
+       GFAR_CREATE_FILE(dev, rx_buf_lock);
+       GFAR_CREATE_FILE(dev, rx_stash_size);
+       GFAR_CREATE_FILE(dev, fifo_threshold);
+
        /* Print out the device info */
        printk(KERN_INFO DEVICE_NAME, dev->name);
        for (idx = 0; idx < 6; idx++)
@@ -465,19 +708,9 @@ static void init_registers(struct net_de
        /* 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);
 }
@@ -638,6 +871,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);
 
@@ -797,9 +1031,13 @@ 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;
 
@@ -809,6 +1047,31 @@ int startup_gfar(struct net_device *dev)
        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->bd_lock_en)
+               attrs |= ATTR_BDLOCK;
+
+       if (priv->rx_stash_size != 0)
+               attrs |= ATTR_BUFSTASH;
+
+       if (priv->rx_buf_lock)
+               attrs |= ATTR_BUFLOCK;
+
+       gfar_write(&priv->regs->attr, attrs);
+
+       /* Start the controller */
        gfar_start(dev);
 
        return 0;
@@ -1012,21 +1275,7 @@ static struct net_device_stats * gfar_ge
 /* 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;
 }
@@ -1723,6 +1972,9 @@ static void gfar_set_multi(struct net_de
                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);
@@ -1741,18 +1993,47 @@ static void gfar_set_multi(struct net_de
                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 +2062,32 @@ static void gfar_set_hash_for_addr(struc
        gfar_write(priv->hash_regs[whichreg], tempval);
 
        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 */
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -91,12 +91,23 @@ 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 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_BD_STASH 1
+#define DEFAULT_STASH_LENGTH   64
+#define DEFAULT_RX_BUF_LOCK 0
+#define DEFAULT_BD_LOCK 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
@@ -182,6 +193,7 @@ 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)
@@ -252,28 +264,25 @@ extern const char gfar_driver_version[];
                IMASK_XFUN | IMASK_RXC | IMASK_BABT | IMASK_DPE \
                | IMASK_PERR)
 
+/* Fifo management */
+#define FIFO_TX_THR_MASK       0x00ff
 
 /* Attribute fields */
 
 /* This enables rx snooping for buffers and descriptors */
-#ifdef CONFIG_GFAR_BDSTASH
 #define ATTR_BDSTASH           0x00000800
-#else
-#define ATTR_BDSTASH           0x00000000
-#endif
+#define ATTR_BDLOCK            0x00000c00
 
-#ifdef CONFIG_GFAR_BUFSTASH
 #define ATTR_BUFSTASH          0x00004000
-#define STASH_LENGTH           64
-#else
-#define ATTR_BUFSTASH          0x00000000
-#endif
+#define ATTR_BUFLOCK           0x00006000
 
 #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(x) (x)
 
 
 /* TxBD status field bits */
@@ -689,12 +698,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 char vlan_enable:1,
                rx_csum_enable:1,
-               extended_hash:1;
+               extended_hash:1,
+               bd_stash_en:1,
+               bd_lock_en:1,
+               rx_buf_lock:1;
        unsigned short padding;
        struct vlan_group *vlgrp;
        /* Info structure initialized by board setup code */
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to