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