This patch uses sysfs to enable several features for the ethernet
controllers supported by the gianfar driver:
* The ability to stash or lock n bytes of all rx buffers directly in the L2
* The ability to stash or lock the buffer descriptors in the L2
* The ability to trigger transmission at a lower threshold of fifo bytes
Also added is support for using the eTSEC exact-match registers to filter
multicast addresses until they overflow.
Signed-off-by: Andy Fleming <[EMAIL PROTECTED]>
Index: Documentation/networking/gianfar.txt
===================================================================
RCS file: Documentation/networking/gianfar.txt
diff -N Documentation/networking/gianfar.txt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Documentation/networking/gianfar.txt 28 Jul 2005 21:57:09 -0000
@@ -0,0 +1,30 @@
+The Gianfar Ethernet Driver
+Sysfs File description
+
+Author: Andy Fleming <[EMAIL PROTECTED]>
+Updated: 2005-07-28
+
+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.
+
+bd_lock:
+To lock the RX Buffer Descriptors into L2, echo 'on' or '1' to
+bd_lock. echo 'off' or '0' to disable.
+
+rx_buf_lock:
+To lock the first n bytes of the packet in the L2, echo 'on' or '1'
+to buf_lock. The number of bytes is determined by buf_stash_len.
+
+fifo_threshold:
+WARNING: You could really screw this up if you set it too low!
+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.
Index: drivers/net/gianfar.c
===================================================================
RCS file:
/proj/ppc/sysperf/cvsroot/cvs_root/pq38/linux-2.6/drivers/net/gianfar.c,v
retrieving revision 1.3
diff -u -r1.3 gianfar.c
--- drivers/net/gianfar.c 7 Jun 2005 22:48:50 -0000 1.3
+++ drivers/net/gianfar.c 28 Jul 2005 21:55:08 -0000
@@ -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
@@ -148,6 +147,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;
@@ -155,13 +158,245 @@
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);
}
+
+
static int gfar_probe(struct device *device)
{
u32 tempval;
@@ -342,12 +577,15 @@
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;
@@ -366,6 +604,13 @@
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++)
@@ -548,19 +793,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);
}
@@ -738,6 +973,7 @@
struct gfar *regs = priv->regs;
int err = 0;
u32 rctrl = 0;
+ u32 attrs = 0;
gfar_write(®s->imask, IMASK_INIT_CLEAR);
@@ -903,9 +1139,13 @@
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;
@@ -915,6 +1155,31 @@
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;
@@ -1035,7 +1300,7 @@
txbdp->status &= TXBD_WRAP;
/* Set up checksumming */
- if ((dev->features & NETIF_F_IP_CSUM)
+ if ((dev->features & NETIF_F_IP_CSUM)
&& (CHECKSUM_HW == skb->ip_summed)) {
fcb = gfar_add_fcb(skb, txbdp);
gfar_tx_checksum(skb, fcb);
@@ -1125,21 +1390,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;
}
@@ -1974,6 +2225,9 @@
gfar_write(®s->gaddr6, 0xffffffff);
gfar_write(®s->gaddr7, 0xffffffff);
} else {
+ int em_num;
+ int idx;
+
/* zero out the hash */
gfar_write(®s->igaddr0, 0x0);
gfar_write(®s->igaddr1, 0x0);
@@ -1992,18 +2246,47 @@
gfar_write(®s->gaddr6, 0x0);
gfar_write(®s->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
@@ -2032,6 +2315,32 @@
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 */
Index: drivers/net/gianfar.h
===================================================================
RCS file:
/proj/ppc/sysperf/cvsroot/cvs_root/pq38/linux-2.6/drivers/net/gianfar.h,v
retrieving revision 1.3
diff -u -r1.3 gianfar.h
--- drivers/net/gianfar.h 7 Jun 2005 23:03:30 -0000 1.3
+++ drivers/net/gianfar.h 27 Jul 2005 01:06:13 -0000
@@ -90,12 +90,23 @@
#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
@@ -181,6 +192,7 @@
#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)
@@ -251,28 +263,25 @@
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 */
@@ -696,12 +705,17 @@
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