aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Fleming <afleming@freescale.com>2005-11-11 12:38:59 -0600
committerJeff Garzik <jgarzik@pobox.com>2005-11-18 13:31:26 -0500
commit7f7f53168dbee6d6a462acea666fddd18aad4f08 (patch)
tree9c59f9b4b7c06f70b48197408f37398e54b4771e
parentfed5eccdcf542742786701b2514b5cb7ab282b93 (diff)
downloadlinux-7f7f53168dbee6d6a462acea666fddd18aad4f08.tar.gz
[PATCH] Gianfar update and sysfs support
This seems to have gotten lost, so I'll resend. Signed-off-by: Andy Fleming <afleming@freescale.com> * Added sysfs support to gianfar for modifying FIFO and stashing parameters * Updated driver to support 10 Mbit, full duplex operation * Improved comments throughout * Cleaned up and optimized offloading code * Fixed a bug where rx buffers were being improperly mapped and unmapped * (only manifested if cache-coherency was off) * Added support for using the eTSEC exact-match MAC registers * Bumped the version to 1.3 * Added support for distinguishing between reduced 100 and 10 Mbit modes * Modified default coalescing values to lower latency * Added documentation Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
-rw-r--r--Documentation/networking/gianfar.txt72
-rw-r--r--drivers/net/Makefile5
-rw-r--r--drivers/net/gianfar.c231
-rw-r--r--drivers/net/gianfar.h69
-rw-r--r--drivers/net/gianfar_ethtool.c2
-rw-r--r--drivers/net/gianfar_mii.h1
-rw-r--r--drivers/net/gianfar_sysfs.c311
7 files changed, 579 insertions, 112 deletions
diff --git a/Documentation/networking/gianfar.txt b/Documentation/networking/gianfar.txt
new file mode 100644
index 00000000000000..ad474ea07d07f1
--- /dev/null
+++ b/Documentation/networking/gianfar.txt
@@ -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.
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 27822a2f068373..505681406314a3 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -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
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 0f030b73cbb328..146f9513aea57f 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -2,7 +2,8 @@
* drivers/net/gianfar.c
*
* Gianfar Ethernet Driver
- * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * This driver is designed for the non-CPM ethernet controllers
+ * on the 85xx and 83xx family of integrated processors
* Based on 8260_io/fcc_enet.c
*
* Author: Andy Fleming
@@ -22,8 +23,6 @@
* B-V +1.62
*
* Theory of operation
- * This driver is designed for the non-CPM ethernet controllers
- * on the 85xx and 83xx family of integrated processors
*
* The driver is initialized through platform_device. Structures which
* define the configuration needed by the board are defined in a
@@ -110,7 +109,7 @@
#endif
const char gfar_driver_name[] = "Gianfar Ethernet";
-const char gfar_driver_version[] = "1.2";
+const char gfar_driver_version[] = "1.3";
static int gfar_enet_open(struct net_device *dev);
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
@@ -139,6 +138,10 @@ static 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)
{
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 5065ba82cb7608..94a91da84fbb7a 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -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 */
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index cfa3cd7c91a0a9..765e810620fe95 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -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
diff --git a/drivers/net/gianfar_mii.h b/drivers/net/gianfar_mii.h
index e85eb216fb5be4..d527cf2f9c1d7f 100644
--- a/drivers/net/gianfar_mii.h
+++ b/drivers/net/gianfar_mii.h
@@ -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
index 00000000000000..10d34cb1919289
--- /dev/null
+++ b/drivers/net/gianfar_sysfs.c
@@ -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);
+
+}