linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / net / mv643xx_eth.c
index 0405e1f..c0998ef 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2002 rabeeh@galileo.co.il
  *
  * Copyright (C) 2003 PMC-Sierra, Inc.,
- *     written by Manish Lachwani (lachwani@pmc-sierra.com)
+ *     written by Manish Lachwani
  *
  * Copyright (C) 2003 Ralf Baechle <ralf@linux-mips.org>
  *
  */
 #include <linux/init.h>
 #include <linux/dma-mapping.h>
+#include <linux/in.h>
+#include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/ip.h>
 
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/ethtool.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 #include <asm/types.h>
 #include <asm/pgtable.h>
 /* Constants */
 #define VLAN_HLEN              4
 #define FCS_LEN                        4
-#define WRAP                   NET_IP_ALIGN + ETH_HLEN + VLAN_HLEN + FCS_LEN
+#define DMA_ALIGN              8       /* hw requires 8-byte alignment */
+#define HW_IP_ALIGN            2       /* hw aligns IP header */
+#define WRAP                   HW_IP_ALIGN + ETH_HLEN + VLAN_HLEN + FCS_LEN
 #define RX_SKB_SIZE            ((dev->mtu + WRAP + 7) & ~0x7)
 
-#define INT_CAUSE_UNMASK_ALL           0x0007ffff
-#define INT_CAUSE_UNMASK_ALL_EXT       0x0011ffff
-#ifdef MV643XX_RX_QUEUE_FILL_ON_TASK
-#define INT_CAUSE_MASK_ALL             0x00000000
+#define INT_UNMASK_ALL                 0x0007ffff
+#define INT_UNMASK_ALL_EXT             0x0011ffff
+#define INT_MASK_ALL                   0x00000000
+#define INT_MASK_ALL_EXT               0x00000000
 #define INT_CAUSE_CHECK_BITS           INT_CAUSE_UNMASK_ALL
 #define INT_CAUSE_CHECK_BITS_EXT       INT_CAUSE_UNMASK_ALL_EXT
-#endif
 
 #ifdef MV643XX_CHECKSUM_OFFLOAD_TX
 #define MAX_DESCS_PER_SKB      (MAX_SKB_FRAGS + 1)
@@ -77,8 +84,9 @@
 static int eth_port_link_is_up(unsigned int eth_port_num);
 static void eth_port_uc_addr_get(struct net_device *dev,
                                                unsigned char *MacAddr);
-static int mv643xx_eth_real_open(struct net_device *);
-static int mv643xx_eth_real_stop(struct net_device *);
+static void eth_port_set_multicast_list(struct net_device *);
+static int mv643xx_eth_open(struct net_device *);
+static int mv643xx_eth_stop(struct net_device *);
 static int mv643xx_eth_change_mtu(struct net_device *, int);
 static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *);
 static void eth_port_init_mac_tables(unsigned int eth_port_num);
@@ -95,7 +103,7 @@ static char mv643xx_driver_version[] = "1.0";
 static void __iomem *mv643xx_eth_shared_base;
 
 /* used to protect MV643XX_ETH_SMI_REG, which is shared across ports */
-static spinlock_t mv643xx_eth_phy_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(mv643xx_eth_phy_lock);
 
 static inline u32 mv_read(int offset)
 {
@@ -123,15 +131,8 @@ static inline void mv_write(int offset, u32 data)
  */
 static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu)
 {
-       struct mv643xx_private *mp = netdev_priv(dev);
-       unsigned long flags;
-
-       spin_lock_irqsave(&mp->lock, flags);
-
-       if ((new_mtu > 9500) || (new_mtu < 64)) {
-               spin_unlock_irqrestore(&mp->lock, flags);
+       if ((new_mtu > 9500) || (new_mtu < 64))
                return -EINVAL;
-       }
 
        dev->mtu = new_mtu;
        /*
@@ -141,17 +142,13 @@ static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu)
         * to memory is full, which might fail the open function.
         */
        if (netif_running(dev)) {
-               if (mv643xx_eth_real_stop(dev))
-                       printk(KERN_ERR
-                               "%s: Fatal error on stopping device\n",
-                               dev->name);
-               if (mv643xx_eth_real_open(dev))
+               mv643xx_eth_stop(dev);
+               if (mv643xx_eth_open(dev))
                        printk(KERN_ERR
                                "%s: Fatal error on opening device\n",
                                dev->name);
        }
 
-       spin_unlock_irqrestore(&mp->lock, flags);
        return 0;
 }
 
@@ -169,15 +166,19 @@ static void mv643xx_eth_rx_task(void *data)
        struct mv643xx_private *mp = netdev_priv(dev);
        struct pkt_info pkt_info;
        struct sk_buff *skb;
+       int unaligned;
 
        if (test_and_set_bit(0, &mp->rx_task_busy))
                panic("%s: Error in test_set_bit / clear_bit", dev->name);
 
        while (mp->rx_ring_skbs < (mp->rx_ring_size - 5)) {
-               skb = dev_alloc_skb(RX_SKB_SIZE);
+               skb = dev_alloc_skb(RX_SKB_SIZE + DMA_ALIGN);
                if (!skb)
                        break;
                mp->rx_ring_skbs++;
+               unaligned = (u32)skb->data & (DMA_ALIGN - 1);
+               if (unaligned)
+                       skb_reserve(skb, DMA_ALIGN - unaligned);
                pkt_info.cmd_sts = ETH_RX_ENABLE_INTERRUPT;
                pkt_info.byte_cnt = RX_SKB_SIZE;
                pkt_info.buf_ptr = dma_map_single(NULL, skb->data, RX_SKB_SIZE,
@@ -188,7 +189,7 @@ static void mv643xx_eth_rx_task(void *data)
                                "%s: Error allocating RX Ring\n", dev->name);
                        break;
                }
-               skb_reserve(skb, 2);
+               skb_reserve(skb, HW_IP_ALIGN);
        }
        clear_bit(0, &mp->rx_task_busy);
        /*
@@ -206,7 +207,7 @@ static void mv643xx_eth_rx_task(void *data)
        else {
                /* Return interrupts */
                mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(mp->port_num),
-                                                       INT_CAUSE_UNMASK_ALL);
+                                                       INT_UNMASK_ALL);
        }
 #endif
 }
@@ -259,14 +260,15 @@ static void mv643xx_eth_update_mac_address(struct net_device *dev)
 static void mv643xx_eth_set_rx_mode(struct net_device *dev)
 {
        struct mv643xx_private *mp = netdev_priv(dev);
-       u32 config_reg;
 
-       config_reg = ethernet_get_config_reg(mp->port_num);
        if (dev->flags & IFF_PROMISC)
-               config_reg |= (u32) MV643XX_ETH_UNICAST_PROMISCUOUS_MODE;
+               mp->port_config |= (u32) MV643XX_ETH_UNICAST_PROMISCUOUS_MODE;
        else
-               config_reg &= ~(u32) MV643XX_ETH_UNICAST_PROMISCUOUS_MODE;
-       ethernet_set_config_reg(mp->port_num, config_reg);
+               mp->port_config &= ~(u32) MV643XX_ETH_UNICAST_PROMISCUOUS_MODE;
+
+       mv_write(MV643XX_ETH_PORT_CONFIG_REG(mp->port_num), mp->port_config);
+
+       eth_port_set_multicast_list(dev);
 }
 
 /*
@@ -342,8 +344,6 @@ static int mv643xx_eth_free_tx_queue(struct net_device *dev,
        if (!(eth_int_cause_ext & (BIT0 | BIT8)))
                return released;
 
-       spin_lock(&mp->lock);
-
        /* Check only queue 0 */
        while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) {
                if (pkt_info.cmd_sts & BIT0) {
@@ -351,40 +351,21 @@ static int mv643xx_eth_free_tx_queue(struct net_device *dev,
                        stats->tx_errors++;
                }
 
-               /*
-                * If return_info is different than 0, release the skb.
-                * The case where return_info is not 0 is only in case
-                * when transmitted a scatter/gather packet, where only
-                * last skb releases the whole chain.
-                */
-               if (pkt_info.return_info) {
-                       if (skb_shinfo(pkt_info.return_info)->nr_frags)
-                               dma_unmap_page(NULL, pkt_info.buf_ptr,
-                                               pkt_info.byte_cnt,
-                                               DMA_TO_DEVICE);
-                       else
-                               dma_unmap_single(NULL, pkt_info.buf_ptr,
-                                               pkt_info.byte_cnt,
-                                               DMA_TO_DEVICE);
+               if (pkt_info.cmd_sts & ETH_TX_FIRST_DESC)
+                       dma_unmap_single(NULL, pkt_info.buf_ptr,
+                                       pkt_info.byte_cnt,
+                                       DMA_TO_DEVICE);
+               else
+                       dma_unmap_page(NULL, pkt_info.buf_ptr,
+                                       pkt_info.byte_cnt,
+                                       DMA_TO_DEVICE);
 
+               if (pkt_info.return_info) {
                        dev_kfree_skb_irq(pkt_info.return_info);
                        released = 0;
-
-                       /*
-                        * Decrement the number of outstanding skbs counter on
-                        * the TX queue.
-                        */
-                       if (mp->tx_ring_skbs == 0)
-                               panic("ERROR - TX outstanding SKBs"
-                                               " counter is corrupted");
-                       mp->tx_ring_skbs--;
-               } else
-                       dma_unmap_page(NULL, pkt_info.buf_ptr,
-                                       pkt_info.byte_cnt, DMA_TO_DEVICE);
+               }
        }
 
-       spin_unlock(&mp->lock);
-
        return released;
 }
 
@@ -412,15 +393,13 @@ static int mv643xx_eth_receive_queue(struct net_device *dev)
        struct pkt_info pkt_info;
 
 #ifdef MV643XX_NAPI
-       while (eth_port_receive(mp, &pkt_info) == ETH_OK && budget > 0) {
+       while (budget-- > 0 && eth_port_receive(mp, &pkt_info) == ETH_OK) {
 #else
        while (eth_port_receive(mp, &pkt_info) == ETH_OK) {
 #endif
                mp->rx_ring_skbs--;
                received_packets++;
-#ifdef MV643XX_NAPI
-               budget--;
-#endif
+
                /* Update statistics. Note byte count includes 4 byte CRC count */
                stats->rx_packets++;
                stats->rx_bytes += pkt_info.byte_cnt;
@@ -467,6 +446,7 @@ static int mv643xx_eth_receive_queue(struct net_device *dev)
                        netif_rx(skb);
 #endif
                }
+               dev->last_rx = jiffies;
        }
 
        return received_packets;
@@ -484,7 +464,7 @@ static int mv643xx_eth_receive_queue(struct net_device *dev)
  */
 
 static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
-                                                       struct pt_regs *regs)
+                                               struct pt_regs *regs)
 {
        struct net_device *dev = (struct net_device *)dev_id;
        struct mv643xx_private *mp = netdev_priv(dev);
@@ -493,12 +473,12 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
 
        /* Read interrupt cause registers */
        eth_int_cause = mv_read(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num)) &
-                                               INT_CAUSE_UNMASK_ALL;
+                                               INT_UNMASK_ALL;
 
        if (eth_int_cause & BIT1)
                eth_int_cause_ext = mv_read(
                        MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num)) &
-                                               INT_CAUSE_UNMASK_ALL_EXT;
+                                               INT_UNMASK_ALL_EXT;
 
 #ifdef MV643XX_NAPI
        if (!(eth_int_cause & 0x0007fffd)) {
@@ -523,9 +503,10 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
        } else {
                if (netif_rx_schedule_prep(dev)) {
                        /* Mask all the interrupts */
-                       mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), 0);
-                       mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG
-                                                               (port_num), 0);
+                       mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num),
+                                                               INT_MASK_ALL);
+                       /* wait for previous write to complete */
+                       mv_read(MV643XX_ETH_INTERRUPT_MASK_REG(port_num));
                        __netif_rx_schedule(dev);
                }
 #else
@@ -538,9 +519,12 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
                 * with skb's.
                 */
 #ifdef MV643XX_RX_QUEUE_FILL_ON_TASK
-               /* Unmask all interrupts on ethernet port */
+               /* Mask all interrupts on ethernet port */
                mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num),
-                                                       INT_CAUSE_MASK_ALL);
+                                                       INT_MASK_ALL);
+               /* wait for previous write to take effect */
+               mv_read(MV643XX_ETH_INTERRUPT_MASK_REG(port_num));
+
                queue_task(&mp->rx_task, &tq_immediate);
                mark_bh(IMMEDIATE_BH);
 #else
@@ -646,56 +630,6 @@ static unsigned int eth_port_set_tx_coal(unsigned int eth_port_num,
        return coal;
 }
 
-/*
- * mv643xx_eth_open
- *
- * This function is called when openning the network device. The function
- * should initialize all the hardware, initialize cyclic Rx/Tx
- * descriptors chain and buffers and allocate an IRQ to the network
- * device.
- *
- * Input :     a pointer to the network device structure
- *
- * Output :    zero of success , nonzero if fails.
- */
-
-static int mv643xx_eth_open(struct net_device *dev)
-{
-       struct mv643xx_private *mp = netdev_priv(dev);
-       unsigned int port_num = mp->port_num;
-       int err;
-
-       spin_lock_irq(&mp->lock);
-
-       err = request_irq(dev->irq, mv643xx_eth_int_handler,
-                       SA_SHIRQ | SA_SAMPLE_RANDOM, dev->name, dev);
-
-       if (err) {
-               printk(KERN_ERR "Can not assign IRQ number to MV643XX_eth%d\n",
-                                                               port_num);
-               err = -EAGAIN;
-               goto out;
-       }
-
-       if (mv643xx_eth_real_open(dev)) {
-               printk("%s: Error opening interface\n", dev->name);
-               err = -EBUSY;
-               goto out_free;
-       }
-
-       spin_unlock_irq(&mp->lock);
-
-       return 0;
-
-out_free:
-       free_irq(dev->irq, dev);
-
-out:
-       spin_unlock_irq(&mp->lock);
-
-       return err;
-}
-
 /*
  * ether_init_rx_desc_ring - Curve a Rx chain desc list and buffer in memory.
  *
@@ -788,28 +722,37 @@ static void ether_init_tx_desc_ring(struct mv643xx_private *mp)
        mp->port_tx_queue_command |= 1;
 }
 
-/* Helper function for mv643xx_eth_open */
-static int mv643xx_eth_real_open(struct net_device *dev)
+/*
+ * mv643xx_eth_open
+ *
+ * This function is called when openning the network device. The function
+ * should initialize all the hardware, initialize cyclic Rx/Tx
+ * descriptors chain and buffers and allocate an IRQ to the network
+ * device.
+ *
+ * Input :     a pointer to the network device structure
+ *
+ * Output :    zero of success , nonzero if fails.
+ */
+
+static int mv643xx_eth_open(struct net_device *dev)
 {
        struct mv643xx_private *mp = netdev_priv(dev);
        unsigned int port_num = mp->port_num;
        unsigned int size;
+       int err;
+
+       err = request_irq(dev->irq, mv643xx_eth_int_handler,
+                       SA_SHIRQ | SA_SAMPLE_RANDOM, dev->name, dev);
+       if (err) {
+               printk(KERN_ERR "Can not assign IRQ number to MV643XX_eth%d\n",
+                                                               port_num);
+               return -EAGAIN;
+       }
 
        /* Stop RX Queues */
        mv_write(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), 0x0000ff00);
 
-       /* Clear the ethernet port interrupts */
-       mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
-       mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);
-
-       /* Unmask RX buffer and TX end interrupt */
-       mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num),
-                                               INT_CAUSE_UNMASK_ALL);
-
-       /* Unmask phy and link status changes interrupts */
-       mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num),
-                                               INT_CAUSE_UNMASK_ALL_EXT);
-
        /* Set the MAC Address */
        memcpy(mp->port_mac_addr, dev->dev_addr, 6);
 
@@ -829,14 +772,15 @@ static int mv643xx_eth_real_open(struct net_device *dev)
                                                                GFP_KERNEL);
        if (!mp->rx_skb) {
                printk(KERN_ERR "%s: Cannot allocate Rx skb ring\n", dev->name);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto out_free_irq;
        }
        mp->tx_skb = kmalloc(sizeof(*mp->tx_skb) * mp->tx_ring_size,
                                                                GFP_KERNEL);
        if (!mp->tx_skb) {
                printk(KERN_ERR "%s: Cannot allocate Tx skb ring\n", dev->name);
-               kfree(mp->rx_skb);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto out_free_rx_skb;
        }
 
        /* Allocate TX ring */
@@ -856,9 +800,8 @@ static int mv643xx_eth_real_open(struct net_device *dev)
        if (!mp->p_tx_desc_area) {
                printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n",
                                                        dev->name, size);
-               kfree(mp->rx_skb);
-               kfree(mp->tx_skb);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto out_free_tx_skb;
        }
        BUG_ON((u32) mp->p_tx_desc_area & 0xf); /* check 16-byte alignment */
        memset((void *)mp->p_tx_desc_area, 0, mp->tx_desc_area_size);
@@ -885,13 +828,12 @@ static int mv643xx_eth_real_open(struct net_device *dev)
                printk(KERN_ERR "%s: Freeing previously allocated TX queues...",
                                                        dev->name);
                if (mp->rx_sram_size)
-                       iounmap(mp->p_rx_desc_area);
+                       iounmap(mp->p_tx_desc_area);
                else
                        dma_free_coherent(NULL, mp->tx_desc_area_size,
                                        mp->p_tx_desc_area, mp->tx_desc_dma);
-               kfree(mp->rx_skb);
-               kfree(mp->tx_skb);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto out_free_tx_skb;
        }
        memset((void *)mp->p_rx_desc_area, 0, size);
 
@@ -911,9 +853,26 @@ static int mv643xx_eth_real_open(struct net_device *dev)
        mp->tx_int_coal =
                eth_port_set_tx_coal(port_num, 133000000, MV643XX_TX_COAL);
 
-       netif_start_queue(dev);
+       /* Clear any pending ethernet port interrupts */
+       mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
+       mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);
 
+       /* Unmask phy and link status changes interrupts */
+       mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num),
+                                               INT_UNMASK_ALL_EXT);
+
+       /* Unmask RX buffer and TX end interrupt */
+       mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), INT_UNMASK_ALL);
        return 0;
+
+out_free_tx_skb:
+       kfree(mp->tx_skb);
+out_free_rx_skb:
+       kfree(mp->rx_skb);
+out_free_irq:
+       free_irq(dev->irq, dev);
+
+       return err;
 }
 
 static void mv643xx_eth_free_tx_rings(struct net_device *dev)
@@ -921,14 +880,17 @@ static void mv643xx_eth_free_tx_rings(struct net_device *dev)
        struct mv643xx_private *mp = netdev_priv(dev);
        unsigned int port_num = mp->port_num;
        unsigned int curr;
+       struct sk_buff *skb;
 
        /* Stop Tx Queues */
        mv_write(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_REG(port_num), 0x0000ff00);
 
        /* Free outstanding skb's on TX rings */
        for (curr = 0; mp->tx_ring_skbs && curr < mp->tx_ring_size; curr++) {
-               if (mp->tx_skb[curr]) {
-                       dev_kfree_skb(mp->tx_skb[curr]);
+               skb = mp->tx_skb[curr];
+               if (skb) {
+                       mp->tx_ring_skbs -= skb_shinfo(skb)->nr_frags;
+                       dev_kfree_skb(skb);
                        mp->tx_ring_skbs--;
                }
        }
@@ -984,44 +946,32 @@ static void mv643xx_eth_free_rx_rings(struct net_device *dev)
  * Output :    zero if success , nonzero if fails
  */
 
-/* Helper function for mv643xx_eth_stop */
-
-static int mv643xx_eth_real_stop(struct net_device *dev)
+static int mv643xx_eth_stop(struct net_device *dev)
 {
        struct mv643xx_private *mp = netdev_priv(dev);
        unsigned int port_num = mp->port_num;
 
+       /* Mask all interrupts on ethernet port */
+       mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), INT_MASK_ALL);
+       /* wait for previous write to complete */
+       mv_read(MV643XX_ETH_INTERRUPT_MASK_REG(port_num));
+
+#ifdef MV643XX_NAPI
+       netif_poll_disable(dev);
+#endif
        netif_carrier_off(dev);
        netif_stop_queue(dev);
 
-       mv643xx_eth_free_tx_rings(dev);
-       mv643xx_eth_free_rx_rings(dev);
-
        eth_port_reset(mp->port_num);
 
-       /* Disable ethernet port interrupts */
-       mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
-       mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);
-
-       /* Mask RX buffer and TX end interrupt */
-       mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), 0);
-
-       /* Mask phy and link status changes interrupts */
-       mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), 0);
-
-       return 0;
-}
-
-static int mv643xx_eth_stop(struct net_device *dev)
-{
-       struct mv643xx_private *mp = netdev_priv(dev);
-
-       spin_lock_irq(&mp->lock);
+       mv643xx_eth_free_tx_rings(dev);
+       mv643xx_eth_free_rx_rings(dev);
 
-       mv643xx_eth_real_stop(dev);
+#ifdef MV643XX_NAPI
+       netif_poll_enable(dev);
+#endif
 
        free_irq(dev->irq, dev);
-       spin_unlock_irq(&mp->lock);
 
        return 0;
 }
@@ -1033,23 +983,17 @@ static void mv643xx_tx(struct net_device *dev)
        struct pkt_info pkt_info;
 
        while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) {
-               if (pkt_info.return_info) {
-                       if (skb_shinfo(pkt_info.return_info)->nr_frags)
-                               dma_unmap_page(NULL, pkt_info.buf_ptr,
-                                               pkt_info.byte_cnt,
-                                               DMA_TO_DEVICE);
-                       else
-                               dma_unmap_single(NULL, pkt_info.buf_ptr,
-                                               pkt_info.byte_cnt,
-                                               DMA_TO_DEVICE);
+               if (pkt_info.cmd_sts & ETH_TX_FIRST_DESC)
+                       dma_unmap_single(NULL, pkt_info.buf_ptr,
+                                       pkt_info.byte_cnt,
+                                       DMA_TO_DEVICE);
+               else
+                       dma_unmap_page(NULL, pkt_info.buf_ptr,
+                                       pkt_info.byte_cnt,
+                                       DMA_TO_DEVICE);
 
+               if (pkt_info.return_info)
                        dev_kfree_skb_irq(pkt_info.return_info);
-
-                       if (mp->tx_ring_skbs)
-                               mp->tx_ring_skbs--;
-               } else
-                       dma_unmap_page(NULL, pkt_info.buf_ptr,
-                                       pkt_info.byte_cnt, DMA_TO_DEVICE);
        }
 
        if (netif_queue_stopped(dev) &&
@@ -1067,14 +1011,11 @@ static int mv643xx_poll(struct net_device *dev, int *budget)
        struct mv643xx_private *mp = netdev_priv(dev);
        int done = 1, orig_budget, work_done;
        unsigned int port_num = mp->port_num;
-       unsigned long flags;
 
 #ifdef MV643XX_TX_FAST_REFILL
        if (++mp->tx_clean_threshold > 5) {
-               spin_lock_irqsave(&mp->lock, flags);
                mv643xx_tx(dev);
                mp->tx_clean_threshold = 0;
-               spin_unlock_irqrestore(&mp->lock, flags);
        }
 #endif
 
@@ -1092,21 +1033,35 @@ static int mv643xx_poll(struct net_device *dev, int *budget)
        }
 
        if (done) {
-               spin_lock_irqsave(&mp->lock, flags);
-               __netif_rx_complete(dev);
+               netif_rx_complete(dev);
                mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
                mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);
                mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num),
-                                               INT_CAUSE_UNMASK_ALL);
-               mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num),
-                                               INT_CAUSE_UNMASK_ALL_EXT);
-               spin_unlock_irqrestore(&mp->lock, flags);
+                                               INT_UNMASK_ALL);
        }
 
        return done ? 0 : 1;
 }
 #endif
 
+/* Hardware can't handle unaligned fragments smaller than 9 bytes.
+ * This helper function detects that case.
+ */
+
+static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
+{
+       unsigned int frag;
+       skb_frag_t *fragp;
+
+       for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
+               fragp = &skb_shinfo(skb)->frags[frag];
+               if (fragp->size <= 8 && fragp->page_offset & 0x7)
+                       return 1;
+       }
+       return 0;
+}
+
+
 /*
  * mv643xx_eth_start_xmit
  *
@@ -1150,32 +1105,44 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
                return 1;
        }
 
+#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
+       if (has_tiny_unaligned_frags(skb)) {
+               if ((skb_linearize(skb, GFP_ATOMIC) != 0)) {
+                       stats->tx_dropped++;
+                       printk(KERN_DEBUG "%s: failed to linearize tiny "
+                                       "unaligned fragment\n", dev->name);
+                       return 1;
+               }
+       }
+
        spin_lock_irqsave(&mp->lock, flags);
 
-       /* Update packet info data structure -- DMA owned, first last */
-#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
        if (!skb_shinfo(skb)->nr_frags) {
-linear:
                if (skb->ip_summed != CHECKSUM_HW) {
+                       /* Errata BTS #50, IHL must be 5 if no HW checksum */
                        pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT |
-                                       ETH_TX_FIRST_DESC | ETH_TX_LAST_DESC;
+                                          ETH_TX_FIRST_DESC |
+                                          ETH_TX_LAST_DESC |
+                                          5 << ETH_TX_IHL_SHIFT;
                        pkt_info.l4i_chk = 0;
                } else {
-                       u32 ipheader = skb->nh.iph->ihl << 11;
-
                        pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT |
-                                       ETH_TX_FIRST_DESC | ETH_TX_LAST_DESC |
-                                       ETH_GEN_TCP_UDP_CHECKSUM |
-                                       ETH_GEN_IP_V_4_CHECKSUM | ipheader;
+                                          ETH_TX_FIRST_DESC |
+                                          ETH_TX_LAST_DESC |
+                                          ETH_GEN_TCP_UDP_CHECKSUM |
+                                          ETH_GEN_IP_V_4_CHECKSUM |
+                                          skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
                        /* CPU already calculated pseudo header checksum. */
-                       if (skb->nh.iph->protocol == IPPROTO_UDP) {
+                       if ((skb->protocol == ETH_P_IP) &&
+                           (skb->nh.iph->protocol == IPPROTO_UDP) ) {
                                pkt_info.cmd_sts |= ETH_UDP_FRAME;
                                pkt_info.l4i_chk = skb->h.uh->check;
-                       } else if (skb->nh.iph->protocol == IPPROTO_TCP)
+                       } else if ((skb->protocol == ETH_P_IP) &&
+                                  (skb->nh.iph->protocol == IPPROTO_TCP))
                                pkt_info.l4i_chk = skb->h.th->check;
                        else {
                                printk(KERN_ERR
-                                       "%s: chksum proto != TCP or UDP\n",
+                                       "%s: chksum proto != IPv4 TCP or UDP\n",
                                        dev->name);
                                spin_unlock_irqrestore(&mp->lock, flags);
                                return 1;
@@ -1185,7 +1152,6 @@ linear:
                pkt_info.buf_ptr = dma_map_single(NULL, skb->data, skb->len,
                                                        DMA_TO_DEVICE);
                pkt_info.return_info = skb;
-               mp->tx_ring_skbs++;
                status = eth_port_send(mp, &pkt_info);
                if ((status == ETH_ERROR) || (status == ETH_QUEUE_FULL))
                        printk(KERN_ERR "%s: Error on transmitting packet\n",
@@ -1193,27 +1159,6 @@ linear:
                stats->tx_bytes += pkt_info.byte_cnt;
        } else {
                unsigned int frag;
-               u32 ipheader;
-
-               /* Since hardware can't handle unaligned fragments smaller
-                * than 9 bytes, if we find any, we linearize the skb
-                * and start again.  When I've seen it, it's always been
-                * the first frag (probably near the end of the page),
-                * but we check all frags to be safe.
-                */
-               for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
-                       skb_frag_t *fragp;
-
-                       fragp = &skb_shinfo(skb)->frags[frag];
-                       if (fragp->size <= 8 && fragp->page_offset & 0x7) {
-                               skb_linearize(skb, GFP_ATOMIC);
-                               printk(KERN_DEBUG "%s: unaligned tiny fragment"
-                                               "%d of %d, fixed\n",
-                                               dev->name, frag,
-                                               skb_shinfo(skb)->nr_frags);
-                               goto linear;
-                       }
-               }
 
                /* first frag which is skb header */
                pkt_info.byte_cnt = skb_headlen(skb);
@@ -1222,21 +1167,27 @@ linear:
                                                        DMA_TO_DEVICE);
                pkt_info.l4i_chk = 0;
                pkt_info.return_info = 0;
-               pkt_info.cmd_sts = ETH_TX_FIRST_DESC;
 
-               if (skb->ip_summed == CHECKSUM_HW) {
-                       ipheader = skb->nh.iph->ihl << 11;
-                       pkt_info.cmd_sts |= ETH_GEN_TCP_UDP_CHECKSUM |
-                                       ETH_GEN_IP_V_4_CHECKSUM | ipheader;
+               if (skb->ip_summed != CHECKSUM_HW)
+                       /* Errata BTS #50, IHL must be 5 if no HW checksum */
+                       pkt_info.cmd_sts = ETH_TX_FIRST_DESC |
+                                          5 << ETH_TX_IHL_SHIFT;
+               else {
+                       pkt_info.cmd_sts = ETH_TX_FIRST_DESC |
+                                          ETH_GEN_TCP_UDP_CHECKSUM |
+                                          ETH_GEN_IP_V_4_CHECKSUM |
+                                          skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
                        /* CPU already calculated pseudo header checksum. */
-                       if (skb->nh.iph->protocol == IPPROTO_UDP) {
+                       if ((skb->protocol == ETH_P_IP) &&
+                           (skb->nh.iph->protocol == IPPROTO_UDP)) {
                                pkt_info.cmd_sts |= ETH_UDP_FRAME;
                                pkt_info.l4i_chk = skb->h.uh->check;
-                       } else if (skb->nh.iph->protocol == IPPROTO_TCP)
+                       } else if ((skb->protocol == ETH_P_IP) &&
+                                  (skb->nh.iph->protocol == IPPROTO_TCP))
                                pkt_info.l4i_chk = skb->h.th->check;
                        else {
                                printk(KERN_ERR
-                                       "%s: chksum proto != TCP or UDP\n",
+                                       "%s: chksum proto != IPv4 TCP or UDP\n",
                                        dev->name);
                                spin_unlock_irqrestore(&mp->lock, flags);
                                return 1;
@@ -1267,7 +1218,6 @@ linear:
                                pkt_info.cmd_sts |= ETH_TX_ENABLE_INTERRUPT |
                                                        ETH_TX_LAST_DESC;
                                pkt_info.return_info = skb;
-                               mp->tx_ring_skbs++;
                        } else {
                                pkt_info.return_info = 0;
                        }
@@ -1297,6 +1247,8 @@ linear:
                }
        }
 #else
+       spin_lock_irqsave(&mp->lock, flags);
+
        pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | ETH_TX_FIRST_DESC |
                                                        ETH_TX_LAST_DESC;
        pkt_info.l4i_chk = 0;
@@ -1304,7 +1256,6 @@ linear:
        pkt_info.buf_ptr = dma_map_single(NULL, skb->data, skb->len,
                                                                DMA_TO_DEVICE);
        pkt_info.return_info = skb;
-       mp->tx_ring_skbs++;
        status = eth_port_send(mp, &pkt_info);
        if ((status == ETH_ERROR) || (status == ETH_QUEUE_FULL))
                printk(KERN_ERR "%s: Error on transmitting packet\n",
@@ -1349,6 +1300,22 @@ static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev)
        return &mp->stats;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void mv643xx_netpoll(struct net_device *netdev)
+{
+       struct mv643xx_private *mp = netdev_priv(netdev);
+       int port_num = mp->port_num;
+
+       mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), INT_MASK_ALL);
+       /* wait for previous write to complete */
+       mv_read(MV643XX_ETH_INTERRUPT_MASK_REG(port_num));
+
+       mv643xx_eth_int_handler(netdev->irq, netdev, NULL);
+
+       mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), INT_UNMASK_ALL);
+}
+#endif
+
 /*/
  * mv643xx_eth_probe
  *
@@ -1360,9 +1327,8 @@ static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev)
  * Input :     struct device *
  * Output :    -ENOMEM if failed , 0 if success
  */
-static int mv643xx_eth_probe(struct device *ddev)
+static int mv643xx_eth_probe(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(ddev);
        struct mv643xx_eth_platform_data *pd;
        int port_num = pdev->id;
        struct mv643xx_private *mp;
@@ -1375,7 +1341,7 @@ static int mv643xx_eth_probe(struct device *ddev)
        if (!dev)
                return -ENOMEM;
 
-       dev_set_drvdata(ddev, dev);
+       platform_set_drvdata(pdev, dev);
 
        mp = netdev_priv(dev);
 
@@ -1399,6 +1365,10 @@ static int mv643xx_eth_probe(struct device *ddev)
        dev->weight = 64;
 #endif
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       dev->poll_controller = mv643xx_netpoll;
+#endif
+
        dev->watchdog_timeo = 2 * HZ;
        dev->tx_queue_len = mp->tx_ring_size;
        dev->base_addr = 0;
@@ -1411,7 +1381,7 @@ static int mv643xx_eth_probe(struct device *ddev)
         * Zero copy can only work if we use Discovery II memory. Else, we will
         * have to map the buffers to ISA memory which is only 16 MB
         */
-       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HW_CSUM;
+       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
 #endif
 #endif
 
@@ -1504,6 +1474,9 @@ static int mv643xx_eth_probe(struct device *ddev)
        printk(KERN_NOTICE "%s: RX NAPI Enabled \n", dev->name);
 #endif
 
+       if (mp->tx_sram_size > 0)
+               printk(KERN_NOTICE "%s: Using SRAM\n", dev->name);
+
        return 0;
 
 out:
@@ -1512,21 +1485,20 @@ out:
        return err;
 }
 
-static int mv643xx_eth_remove(struct device *ddev)
+static int mv643xx_eth_remove(struct platform_device *pdev)
 {
-       struct net_device *dev = dev_get_drvdata(ddev);
+       struct net_device *dev = platform_get_drvdata(pdev);
 
        unregister_netdev(dev);
        flush_scheduled_work();
 
        free_netdev(dev);
-       dev_set_drvdata(ddev, NULL);
+       platform_set_drvdata(pdev, NULL);
        return 0;
 }
 
-static int mv643xx_eth_shared_probe(struct device *ddev)
+static int mv643xx_eth_shared_probe(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(ddev);
        struct resource *res;
 
        printk(KERN_NOTICE "MV-643xx 10/100/1000 Ethernet Driver\n");
@@ -1544,7 +1516,7 @@ static int mv643xx_eth_shared_probe(struct device *ddev)
 
 }
 
-static int mv643xx_eth_shared_remove(struct device *ddev)
+static int mv643xx_eth_shared_remove(struct platform_device *pdev)
 {
        iounmap(mv643xx_eth_shared_base);
        mv643xx_eth_shared_base = NULL;
@@ -1552,18 +1524,20 @@ static int mv643xx_eth_shared_remove(struct device *ddev)
        return 0;
 }
 
-static struct device_driver mv643xx_eth_driver = {
-       .name = MV643XX_ETH_NAME,
-       .bus = &platform_bus_type,
+static struct platform_driver mv643xx_eth_driver = {
        .probe = mv643xx_eth_probe,
        .remove = mv643xx_eth_remove,
+       .driver = {
+               .name = MV643XX_ETH_NAME,
+       },
 };
 
-static struct device_driver mv643xx_eth_shared_driver = {
-       .name = MV643XX_ETH_SHARED_NAME,
-       .bus = &platform_bus_type,
+static struct platform_driver mv643xx_eth_shared_driver = {
        .probe = mv643xx_eth_shared_probe,
        .remove = mv643xx_eth_shared_remove,
+       .driver = {
+               .name = MV643XX_ETH_SHARED_NAME,
+       },
 };
 
 /*
@@ -1579,11 +1553,11 @@ static int __init mv643xx_init_module(void)
 {
        int rc;
 
-       rc = driver_register(&mv643xx_eth_shared_driver);
+       rc = platform_driver_register(&mv643xx_eth_shared_driver);
        if (!rc) {
-               rc = driver_register(&mv643xx_eth_driver);
+               rc = platform_driver_register(&mv643xx_eth_driver);
                if (rc)
-                       driver_unregister(&mv643xx_eth_shared_driver);
+                       platform_driver_unregister(&mv643xx_eth_shared_driver);
        }
        return rc;
 }
@@ -1599,8 +1573,8 @@ static int __init mv643xx_init_module(void)
  */
 static void __exit mv643xx_cleanup_module(void)
 {
-       driver_unregister(&mv643xx_eth_driver);
-       driver_unregister(&mv643xx_eth_shared_driver);
+       platform_driver_unregister(&mv643xx_eth_driver);
+       platform_driver_unregister(&mv643xx_eth_shared_driver);
 }
 
 module_init(mv643xx_init_module);
@@ -1876,6 +1850,9 @@ static void eth_port_start(struct mv643xx_private *mp)
        /* Enable port Rx. */
        mv_write(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num),
                                                mp->port_rx_queue_command);
+
+       /* Disable port bandwidth limits by clearing MTU register */
+       mv_write(MV643XX_ETH_MAXIMUM_TRANSMIT_UNIT(port_num), 0);
 }
 
 /*
@@ -2016,6 +1993,196 @@ static int eth_port_uc_addr(unsigned int eth_port_num, unsigned char uc_nibble,
        return 1;
 }
 
+/*
+ * The entries in each table are indexed by a hash of a packet's MAC
+ * address.  One bit in each entry determines whether the packet is
+ * accepted.  There are 4 entries (each 8 bits wide) in each register
+ * of the table.  The bits in each entry are defined as follows:
+ *     0       Accept=1, Drop=0
+ *     3-1     Queue                   (ETH_Q0=0)
+ *     7-4     Reserved = 0;
+ */
+static void eth_port_set_filter_table_entry(int table, unsigned char entry)
+{
+       unsigned int table_reg;
+       unsigned int tbl_offset;
+       unsigned int reg_offset;
+
+       tbl_offset = (entry / 4) * 4;   /* Register offset of DA table entry */
+       reg_offset = entry % 4;         /* Entry offset within the register */
+
+       /* Set "accepts frame bit" at specified table entry */
+       table_reg = mv_read(table + tbl_offset);
+       table_reg |= 0x01 << (8 * reg_offset);
+       mv_write(table + tbl_offset, table_reg);
+}
+
+/*
+ * eth_port_mc_addr - Multicast address settings.
+ *
+ * The MV device supports multicast using two tables:
+ * 1) Special Multicast Table for MAC addresses of the form
+ *    0x01-00-5E-00-00-XX (where XX is between 0x00 and 0x_FF).
+ *    The MAC DA[7:0] bits are used as a pointer to the Special Multicast
+ *    Table entries in the DA-Filter table.
+ * 2) Other Multicast Table for multicast of another type. A CRC-8bit
+ *    is used as an index to the Other Multicast Table entries in the
+ *    DA-Filter table.  This function calculates the CRC-8bit value.
+ * In either case, eth_port_set_filter_table_entry() is then called
+ * to set to set the actual table entry.
+ */
+static void eth_port_mc_addr(unsigned int eth_port_num, unsigned char *p_addr)
+{
+       unsigned int mac_h;
+       unsigned int mac_l;
+       unsigned char crc_result = 0;
+       int table;
+       int mac_array[48];
+       int crc[8];
+       int i;
+
+       if ((p_addr[0] == 0x01) && (p_addr[1] == 0x00) &&
+           (p_addr[2] == 0x5E) && (p_addr[3] == 0x00) && (p_addr[4] == 0x00)) {
+               table = MV643XX_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE
+                                       (eth_port_num);
+               eth_port_set_filter_table_entry(table, p_addr[5]);
+               return;
+       }
+
+       /* Calculate CRC-8 out of the given address */
+       mac_h = (p_addr[0] << 8) | (p_addr[1]);
+       mac_l = (p_addr[2] << 24) | (p_addr[3] << 16) |
+                       (p_addr[4] << 8) | (p_addr[5] << 0);
+
+       for (i = 0; i < 32; i++)
+               mac_array[i] = (mac_l >> i) & 0x1;
+       for (i = 32; i < 48; i++)
+               mac_array[i] = (mac_h >> (i - 32)) & 0x1;
+
+       crc[0] = mac_array[45] ^ mac_array[43] ^ mac_array[40] ^ mac_array[39] ^
+                mac_array[35] ^ mac_array[34] ^ mac_array[31] ^ mac_array[30] ^
+                mac_array[28] ^ mac_array[23] ^ mac_array[21] ^ mac_array[19] ^
+                mac_array[18] ^ mac_array[16] ^ mac_array[14] ^ mac_array[12] ^
+                mac_array[8]  ^ mac_array[7]  ^ mac_array[6]  ^ mac_array[0];
+
+       crc[1] = mac_array[46] ^ mac_array[45] ^ mac_array[44] ^ mac_array[43] ^
+                mac_array[41] ^ mac_array[39] ^ mac_array[36] ^ mac_array[34] ^
+                mac_array[32] ^ mac_array[30] ^ mac_array[29] ^ mac_array[28] ^
+                mac_array[24] ^ mac_array[23] ^ mac_array[22] ^ mac_array[21] ^
+                mac_array[20] ^ mac_array[18] ^ mac_array[17] ^ mac_array[16] ^
+                mac_array[15] ^ mac_array[14] ^ mac_array[13] ^ mac_array[12] ^
+                mac_array[9]  ^ mac_array[6]  ^ mac_array[1]  ^ mac_array[0];
+
+       crc[2] = mac_array[47] ^ mac_array[46] ^ mac_array[44] ^ mac_array[43] ^
+                mac_array[42] ^ mac_array[39] ^ mac_array[37] ^ mac_array[34] ^
+                mac_array[33] ^ mac_array[29] ^ mac_array[28] ^ mac_array[25] ^
+                mac_array[24] ^ mac_array[22] ^ mac_array[17] ^ mac_array[15] ^
+                mac_array[13] ^ mac_array[12] ^ mac_array[10] ^ mac_array[8]  ^
+                mac_array[6]  ^ mac_array[2]  ^ mac_array[1]  ^ mac_array[0];
+
+       crc[3] = mac_array[47] ^ mac_array[45] ^ mac_array[44] ^ mac_array[43] ^
+                mac_array[40] ^ mac_array[38] ^ mac_array[35] ^ mac_array[34] ^
+                mac_array[30] ^ mac_array[29] ^ mac_array[26] ^ mac_array[25] ^
+                mac_array[23] ^ mac_array[18] ^ mac_array[16] ^ mac_array[14] ^
+                mac_array[13] ^ mac_array[11] ^ mac_array[9]  ^ mac_array[7]  ^
+                mac_array[3]  ^ mac_array[2]  ^ mac_array[1];
+
+       crc[4] = mac_array[46] ^ mac_array[45] ^ mac_array[44] ^ mac_array[41] ^
+                mac_array[39] ^ mac_array[36] ^ mac_array[35] ^ mac_array[31] ^
+                mac_array[30] ^ mac_array[27] ^ mac_array[26] ^ mac_array[24] ^
+                mac_array[19] ^ mac_array[17] ^ mac_array[15] ^ mac_array[14] ^
+                mac_array[12] ^ mac_array[10] ^ mac_array[8]  ^ mac_array[4]  ^
+                mac_array[3]  ^ mac_array[2];
+
+       crc[5] = mac_array[47] ^ mac_array[46] ^ mac_array[45] ^ mac_array[42] ^
+                mac_array[40] ^ mac_array[37] ^ mac_array[36] ^ mac_array[32] ^
+                mac_array[31] ^ mac_array[28] ^ mac_array[27] ^ mac_array[25] ^
+                mac_array[20] ^ mac_array[18] ^ mac_array[16] ^ mac_array[15] ^
+                mac_array[13] ^ mac_array[11] ^ mac_array[9]  ^ mac_array[5]  ^
+                mac_array[4]  ^ mac_array[3];
+
+       crc[6] = mac_array[47] ^ mac_array[46] ^ mac_array[43] ^ mac_array[41] ^
+                mac_array[38] ^ mac_array[37] ^ mac_array[33] ^ mac_array[32] ^
+                mac_array[29] ^ mac_array[28] ^ mac_array[26] ^ mac_array[21] ^
+                mac_array[19] ^ mac_array[17] ^ mac_array[16] ^ mac_array[14] ^
+                mac_array[12] ^ mac_array[10] ^ mac_array[6]  ^ mac_array[5]  ^
+                mac_array[4];
+
+       crc[7] = mac_array[47] ^ mac_array[44] ^ mac_array[42] ^ mac_array[39] ^
+                mac_array[38] ^ mac_array[34] ^ mac_array[33] ^ mac_array[30] ^
+                mac_array[29] ^ mac_array[27] ^ mac_array[22] ^ mac_array[20] ^
+                mac_array[18] ^ mac_array[17] ^ mac_array[15] ^ mac_array[13] ^
+                mac_array[11] ^ mac_array[7]  ^ mac_array[6]  ^ mac_array[5];
+
+       for (i = 0; i < 8; i++)
+               crc_result = crc_result | (crc[i] << i);
+
+       table = MV643XX_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE(eth_port_num);
+       eth_port_set_filter_table_entry(table, crc_result);
+}
+
+/*
+ * Set the entire multicast list based on dev->mc_list.
+ */
+static void eth_port_set_multicast_list(struct net_device *dev)
+{
+
+       struct dev_mc_list      *mc_list;
+       int                     i;
+       int                     table_index;
+       struct mv643xx_private  *mp = netdev_priv(dev);
+       unsigned int            eth_port_num = mp->port_num;
+
+       /* If the device is in promiscuous mode or in all multicast mode,
+        * we will fully populate both multicast tables with accept.
+        * This is guaranteed to yield a match on all multicast addresses...
+        */
+       if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI)) {
+               for (table_index = 0; table_index <= 0xFC; table_index += 4) {
+                       /* Set all entries in DA filter special multicast
+                        * table (Ex_dFSMT)
+                        * Set for ETH_Q0 for now
+                        * Bits
+                        * 0      Accept=1, Drop=0
+                        * 3-1  Queue    ETH_Q0=0
+                        * 7-4  Reserved = 0;
+                        */
+                       mv_write(MV643XX_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE(eth_port_num) + table_index, 0x01010101);
+
+                       /* Set all entries in DA filter other multicast
+                        * table (Ex_dFOMT)
+                        * Set for ETH_Q0 for now
+                        * Bits
+                        * 0      Accept=1, Drop=0
+                        * 3-1  Queue    ETH_Q0=0
+                        * 7-4  Reserved = 0;
+                        */
+                       mv_write(MV643XX_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE(eth_port_num) + table_index, 0x01010101);
+               }
+               return;
+       }
+
+       /* We will clear out multicast tables every time we get the list.
+        * Then add the entire new list...
+        */
+       for (table_index = 0; table_index <= 0xFC; table_index += 4) {
+               /* Clear DA filter special multicast table (Ex_dFSMT) */
+               mv_write(MV643XX_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE
+                               (eth_port_num) + table_index, 0);
+
+               /* Clear DA filter other multicast table (Ex_dFOMT) */
+               mv_write(MV643XX_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE
+                               (eth_port_num) + table_index, 0);
+       }
+
+       /* Get pointer to net_device multicast list and add each one... */
+       for (i = 0, mc_list = dev->mc_list;
+                       (i < 256) && (mc_list != NULL) && (i < dev->mc_count);
+                       i++, mc_list = mc_list->next)
+               if (mc_list->dmi_addrlen == 6)
+                       eth_port_mc_addr(eth_port_num, mc_list->dmi_addr);
+}
+
 /*
  * eth_port_init_mac_tables - Clear all entrance in the UC, SMC and OMC tables
  *
@@ -2043,11 +2210,11 @@ static void eth_port_init_mac_tables(unsigned int eth_port_num)
 
        for (table_index = 0; table_index <= 0xFC; table_index += 4) {
                /* Clear DA filter special multicast table (Ex_dFSMT) */
-               mv_write((MV643XX_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE
-                                       (eth_port_num) + table_index), 0);
+               mv_write(MV643XX_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE
+                                       (eth_port_num) + table_index, 0);
                /* Clear DA filter other multicast table (Ex_dFOMT) */
-               mv_write((MV643XX_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE
-                                       (eth_port_num) + table_index), 0);
+               mv_write(MV643XX_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE
+                                       (eth_port_num) + table_index, 0);
        }
 }
 
@@ -2285,34 +2452,6 @@ static void eth_port_reset(unsigned int port_num)
        mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num), reg_data);
 }
 
-/*
- * ethernet_set_config_reg - Set specified bits in configuration register.
- *
- * DESCRIPTION:
- *     This function sets specified bits in the given ethernet
- *     configuration register.
- *
- * INPUT:
- *     unsigned int    eth_port_num    Ethernet Port number.
- *     unsigned int    value           32 bit value.
- *
- * OUTPUT:
- *     The set bits in the value parameter are set in the configuration
- *     register.
- *
- * RETURN:
- *     None.
- *
- */
-static void ethernet_set_config_reg(unsigned int eth_port_num,
-                                                       unsigned int value)
-{
-       unsigned int eth_config_reg;
-
-       eth_config_reg = mv_read(MV643XX_ETH_PORT_CONFIG_REG(eth_port_num));
-       eth_config_reg |= value;
-       mv_write(MV643XX_ETH_PORT_CONFIG_REG(eth_port_num), eth_config_reg);
-}
 
 static int eth_port_autoneg_supported(unsigned int eth_port_num)
 {
@@ -2338,31 +2477,6 @@ static int eth_port_link_is_up(unsigned int eth_port_num)
        return 0;
 }
 
-/*
- * ethernet_get_config_reg - Get the port configuration register
- *
- * DESCRIPTION:
- *     This function returns the configuration register value of the given
- *     ethernet port.
- *
- * INPUT:
- *     unsigned int    eth_port_num    Ethernet Port number.
- *
- * OUTPUT:
- *     None.
- *
- * RETURN:
- *     Port configuration register value.
- */
-static unsigned int ethernet_get_config_reg(unsigned int eth_port_num)
-{
-       unsigned int eth_config_reg;
-
-       eth_config_reg = mv_read(MV643XX_ETH_PORT_CONFIG_EXTEND_REG
-                                                               (eth_port_num));
-       return eth_config_reg;
-}
-
 /*
  * eth_port_read_smi_reg - Read PHY registers
  *
@@ -2521,6 +2635,9 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
                return ETH_ERROR;
        }
 
+       mp->tx_ring_skbs++;
+       BUG_ON(mp->tx_ring_skbs > mp->tx_ring_size);
+
        /* Get the Tx Desc ring indexes */
        tx_desc_curr = mp->tx_curr_desc_q;
        tx_desc_used = mp->tx_used_desc_q;
@@ -2587,6 +2704,9 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
        if (mp->tx_resource_err)
                return ETH_QUEUE_FULL;
 
+       mp->tx_ring_skbs++;
+       BUG_ON(mp->tx_ring_skbs > mp->tx_ring_size);
+
        /* Get the Tx Desc ring indexes */
        tx_desc_curr = mp->tx_curr_desc_q;
        tx_desc_used = mp->tx_used_desc_q;
@@ -2639,23 +2759,27 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
  *     Tx ring 'first' and 'used' indexes are updated.
  *
  * RETURN:
- *     ETH_ERROR in case the routine can not access Tx desc ring.
- *     ETH_RETRY in case there is transmission in process.
- *     ETH_END_OF_JOB if the routine has nothing to release.
- *     ETH_OK otherwise.
+ *     ETH_OK on success
+ *     ETH_ERROR otherwise.
  *
  */
 static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp,
                                                struct pkt_info *p_pkt_info)
 {
        int tx_desc_used;
+       int tx_busy_desc;
+       struct eth_tx_desc *p_tx_desc_used;
+       unsigned int command_status;
+       unsigned long flags;
+       int err = ETH_OK;
+
+       spin_lock_irqsave(&mp->lock, flags);
+
 #ifdef MV643XX_CHECKSUM_OFFLOAD_TX
-       int tx_busy_desc = mp->tx_first_desc_q;
+       tx_busy_desc = mp->tx_first_desc_q;
 #else
-       int tx_busy_desc = mp->tx_curr_desc_q;
+       tx_busy_desc = mp->tx_curr_desc_q;
 #endif
-       struct eth_tx_desc *p_tx_desc_used;
-       unsigned int command_status;
 
        /* Get the Tx Desc ring indexes */
        tx_desc_used = mp->tx_used_desc_q;
@@ -2663,22 +2787,30 @@ static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp,
        p_tx_desc_used = &mp->p_tx_desc_area[tx_desc_used];
 
        /* Sanity check */
-       if (p_tx_desc_used == NULL)
-               return ETH_ERROR;
+       if (p_tx_desc_used == NULL) {
+               err = ETH_ERROR;
+               goto out;
+       }
 
        /* Stop release. About to overlap the current available Tx descriptor */
-       if (tx_desc_used == tx_busy_desc && !mp->tx_resource_err)
-               return ETH_END_OF_JOB;
+       if (tx_desc_used == tx_busy_desc && !mp->tx_resource_err) {
+               err = ETH_ERROR;
+               goto out;
+       }
 
        command_status = p_tx_desc_used->cmd_sts;
 
        /* Still transmitting... */
-       if (command_status & (ETH_BUFFER_OWNED_BY_DMA))
-               return ETH_RETRY;
+       if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) {
+               err = ETH_ERROR;
+               goto out;
+       }
 
        /* Pass the packet information to the caller */
        p_pkt_info->cmd_sts = command_status;
        p_pkt_info->return_info = mp->tx_skb[tx_desc_used];
+       p_pkt_info->buf_ptr = p_tx_desc_used->buf_ptr;
+       p_pkt_info->byte_cnt = p_tx_desc_used->byte_cnt;
        mp->tx_skb[tx_desc_used] = NULL;
 
        /* Update the next descriptor to release. */
@@ -2687,7 +2819,13 @@ static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp,
        /* Any Tx return cancels the Tx resource error status */
        mp->tx_resource_err = 0;
 
-       return ETH_OK;
+       BUG_ON(mp->tx_ring_skbs == 0);
+       mp->tx_ring_skbs--;
+
+out:
+       spin_unlock_irqrestore(&mp->lock, flags);
+
+       return err;
 }
 
 /*
@@ -2719,11 +2857,14 @@ static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp,
        int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
        volatile struct eth_rx_desc *p_rx_desc;
        unsigned int command_status;
+       unsigned long flags;
 
        /* Do not process Rx ring in case of Rx ring resource error */
        if (mp->rx_resource_err)
                return ETH_QUEUE_FULL;
 
+       spin_lock_irqsave(&mp->lock, flags);
+
        /* Get the Rx Desc ring 'curr and 'used' indexes */
        rx_curr_desc = mp->rx_curr_desc_q;
        rx_used_desc = mp->rx_used_desc_q;
@@ -2735,8 +2876,10 @@ static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp,
        rmb();
 
        /* Nothing to receive... */
-       if (command_status & (ETH_BUFFER_OWNED_BY_DMA))
+       if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) {
+               spin_unlock_irqrestore(&mp->lock, flags);
                return ETH_END_OF_JOB;
+       }
 
        p_pkt_info->byte_cnt = (p_rx_desc->byte_cnt) - RX_BUF_OFFSET;
        p_pkt_info->cmd_sts = command_status;
@@ -2744,8 +2887,10 @@ static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp,
        p_pkt_info->return_info = mp->rx_skb[rx_curr_desc];
        p_pkt_info->l4i_chk = p_rx_desc->buf_size;
 
-       /* Clean the return info field to indicate that the packet has been */
-       /* moved to the upper layers                                        */
+       /*
+        * Clean the return info field to indicate that the
+        * packet has been moved to the upper layers
+        */
        mp->rx_skb[rx_curr_desc] = NULL;
 
        /* Update current index in data structure */
@@ -2756,6 +2901,8 @@ static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp,
        if (rx_next_curr_desc == rx_used_desc)
                mp->rx_resource_err = 1;
 
+       spin_unlock_irqrestore(&mp->lock, flags);
+
        return ETH_OK;
 }
 
@@ -2784,6 +2931,9 @@ static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv643xx_private *mp,
 {
        int used_rx_desc;       /* Where to return Rx resource */
        volatile struct eth_rx_desc *p_used_rx_desc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mp->lock, flags);
 
        /* Get 'used' Rx descriptor */
        used_rx_desc = mp->rx_used_desc_q;
@@ -2807,6 +2957,8 @@ static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv643xx_private *mp,
        /* Any Rx return cancels the Rx resource error status */
        mp->rx_resource_err = 0;
 
+       spin_unlock_irqrestore(&mp->lock, flags);
+
        return ETH_OK;
 }
 
@@ -2819,7 +2971,7 @@ struct mv643xx_stats {
 };
 
 #define MV643XX_STAT(m) sizeof(((struct mv643xx_private *)0)->m), \
-                     offsetof(struct mv643xx_private, m)
+                                       offsetof(struct mv643xx_private, m)
 
 static const struct mv643xx_stats mv643xx_gstrings_stats[] = {
        { "rx_packets", MV643XX_STAT(stats.rx_packets) },
@@ -2970,9 +3122,8 @@ mv643xx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        return 0;
 }
 
-static void
-mv643xx_get_drvinfo(struct net_device *netdev,
-                       struct ethtool_drvinfo *drvinfo)
+static void mv643xx_get_drvinfo(struct net_device *netdev,
+                               struct ethtool_drvinfo *drvinfo)
 {
        strncpy(drvinfo->driver,  mv643xx_driver_name, 32);
        strncpy(drvinfo->version, mv643xx_driver_version, 32);
@@ -2981,39 +3132,37 @@ mv643xx_get_drvinfo(struct net_device *netdev,
        drvinfo->n_stats = MV643XX_STATS_LEN;
 }
 
-static int 
-mv643xx_get_stats_count(struct net_device *netdev)
+static int mv643xx_get_stats_count(struct net_device *netdev)
 {
        return MV643XX_STATS_LEN;
 }
 
-static void 
-mv643xx_get_ethtool_stats(struct net_device *netdev, 
-               struct ethtool_stats *stats, uint64_t *data)
+static void mv643xx_get_ethtool_stats(struct net_device *netdev,
+                               struct ethtool_stats *stats, uint64_t *data)
 {
        struct mv643xx_private *mp = netdev->priv;
        int i;
 
        eth_update_mib_counters(mp);
 
-       for(i = 0; i < MV643XX_STATS_LEN; i++) {
+       for (i = 0; i < MV643XX_STATS_LEN; i++) {
                char *p = (char *)mp+mv643xx_gstrings_stats[i].stat_offset;     
-               data[i] = (mv643xx_gstrings_stats[i].sizeof_stat == 
+               data[i] = (mv643xx_gstrings_stats[i].sizeof_stat ==
                        sizeof(uint64_t)) ? *(uint64_t *)p : *(uint32_t *)p;
        }
 }
 
-static void 
-mv643xx_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data)
+static void mv643xx_get_strings(struct net_device *netdev, uint32_t stringset,
+                               uint8_t *data)
 {
        int i;
 
        switch(stringset) {
        case ETH_SS_STATS:
                for (i=0; i < MV643XX_STATS_LEN; i++) {
-                       memcpy(data + i * ETH_GSTRING_LEN, 
-                       mv643xx_gstrings_stats[i].stat_string,
-                       ETH_GSTRING_LEN);
+                       memcpy(data + i * ETH_GSTRING_LEN,
+                                       mv643xx_gstrings_stats[i].stat_string,
+                                       ETH_GSTRING_LEN);
                }
                break;
        }