linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / net / natsemi.c
index db0475a..9d6d254 100644 (file)
@@ -3,7 +3,6 @@
        Written/copyright 1999-2001 by Donald Becker.
        Portions copyright (c) 2001,2002 Sun Microsystems (thockin@sun.com)
        Portions copyright 2001,2002 Manfred Spraul (manfred@colorfullife.com)
-       Portions copyright 2004 Harald Welte <laforge@gnumonks.org>
 
        This software may be used and distributed according to the terms of
        the GNU General Public License (GPL), incorporated herein by reference.
 
        Support information and updates available at
        http://www.scyld.com/network/netsemi.html
-       [link no longer provides useful info -jgarzik]
 
 
+       Linux kernel modifications:
+
+       Version 1.0.1:
+               - Spinlock fixes
+               - Bug fixes and better intr performance (Tjeerd)
+       Version 1.0.2:
+               - Now reads correct MAC address from eeprom
+       Version 1.0.3:
+               - Eliminate redundant priv->tx_full flag
+               - Call netif_start_queue from dev->tx_timeout
+               - wmb() in start_tx() to flush data
+               - Update Tx locking
+               - Clean up PCI enable (davej)
+       Version 1.0.4:
+               - Merge Donald Becker's natsemi.c version 1.07
+       Version 1.0.5:
+               - { fill me in }
+       Version 1.0.6:
+               * ethtool support (jgarzik)
+               * Proper initialization of the card (which sometimes
+               fails to occur and leaves the card in a non-functional
+               state). (uzi)
+
+               * Some documented register settings to optimize some
+               of the 100Mbit autodetection circuitry in rev C cards. (uzi)
+
+               * Polling of the PHY intr for stuff like link state
+               change and auto- negotiation to finally work properly. (uzi)
+
+               * One-liner removal of a duplicate declaration of
+               netdev_error(). (uzi)
+
+       Version 1.0.7: (Manfred Spraul)
+               * pci dma
+               * SMP locking update
+               * full reset added into tx_timeout
+               * correct multicast hash generation (both big and little endian)
+                       [copied from a natsemi driver version
+                        from Myrio Corporation, Greg Smith]
+               * suspend/resume
+
+       version 1.0.8 (Tim Hockin <thockin@sun.com>)
+               * ETHTOOL_* support
+               * Wake on lan support (Erik Gilling)
+               * MXDMA fixes for serverworks
+               * EEPROM reload
+
+       version 1.0.9 (Manfred Spraul)
+               * Main change: fix lack of synchronize
+               netif_close/netif_suspend against a last interrupt
+               or packet.
+               * do not enable superflous interrupts (e.g. the
+               drivers relies on TxDone - TxIntr not needed)
+               * wait that the hardware has really stopped in close
+               and suspend.
+               * workaround for the (at least) gcc-2.95.1 compiler
+               problem. Also simplifies the code a bit.
+               * disable_irq() in tx_timeout - needed to protect
+               against rx interrupts.
+               * stop the nic before switching into silent rx mode
+               for wol (required according to docu).
+
+       version 1.0.10:
+               * use long for ee_addr (various)
+               * print pointers properly (DaveM)
+               * include asm/irq.h (?)
+
+       version 1.0.11:
+               * check and reset if PHY errors appear (Adrian Sun)
+               * WoL cleanup (Tim Hockin)
+               * Magic number cleanup (Tim Hockin)
+               * Don't reload EEPROM on every reset (Tim Hockin)
+               * Save and restore EEPROM state across reset (Tim Hockin)
+               * MDIO Cleanup (Tim Hockin)
+               * Reformat register offsets/bits (jgarzik)
+
+       version 1.0.12:
+               * ETHTOOL_* further support (Tim Hockin)
+
+       version 1.0.13:
+               * ETHTOOL_[G]EEPROM support (Tim Hockin)
+
+       version 1.0.13:
+               * crc cleanup (Matt Domsch <Matt_Domsch@dell.com>)
+
+       version 1.0.14:
+               * Cleanup some messages and autoneg in ethtool (Tim Hockin)
+
+       version 1.0.15:
+               * Get rid of cable_magic flag
+               * use new (National provided) solution for cable magic issue
+
+       version 1.0.16:
+               * call netdev_rx() for RxErrors (Manfred Spraul)
+               * formatting and cleanups
+               * change options and full_duplex arrays to be zero
+                 initialized
+               * enable only the WoL and PHY interrupts in wol mode
+
+       version 1.0.17:
+               * only do cable_magic on 83815 and early 83816 (Tim Hockin)
+               * create a function for rx refill (Manfred Spraul)
+               * combine drain_ring and init_ring (Manfred Spraul)
+               * oom handling (Manfred Spraul)
+               * hands_off instead of playing with netif_device_{de,a}ttach
+                 (Manfred Spraul)
+               * be sure to write the MAC back to the chip (Manfred Spraul)
+               * lengthen EEPROM timeout, and always warn about timeouts
+                 (Manfred Spraul)
+               * comments update (Manfred)
+               * do the right thing on a phy-reset (Manfred and Tim)
+
        TODO:
        * big endian support with CFG:BEM instead of cpu_to_le32
+       * support for an external PHY
+       * NAPI
 */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/mii.h>
 #include <linux/crc32.h>
 #include <linux/bitops.h>
-#include <linux/prefetch.h>
 #include <asm/processor.h>     /* Processor type for cache alignment. */
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 
 #define DRV_NAME       "natsemi"
-#define DRV_VERSION    "2.0"
-#define DRV_RELDATE    "June 27, 2006"
+#define DRV_VERSION    "1.07+LK1.0.17"
+#define DRV_RELDATE    "Sep 27, 2002"
 
 #define RX_OFFSET      2
 
                                 NETIF_MSG_TX_ERR)
 static int debug = -1;
 
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
 static int mtu;
 
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
    This chip uses a 512 element hash table based on the Ethernet CRC.  */
-static const int multicast_filter_limit = 100;
+static int multicast_filter_limit = 100;
 
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1518 effectively disables this feature. */
@@ -114,6 +228,7 @@ static int full_duplex[MAX_UNITS];
                                 NATSEMI_PG1_NREGS)
 #define NATSEMI_REGS_VER       1 /* v1 added RFDR registers */
 #define NATSEMI_REGS_SIZE      (NATSEMI_NREGS * sizeof(u32))
+#define NATSEMI_EEPROM_SIZE    24 /* 12 16-bit values */
 
 /* Buffer sizes:
  * The nic writes 32-bit values, even if the upper bytes of
@@ -125,7 +240,7 @@ static int full_duplex[MAX_UNITS];
 #define NATSEMI_RX_LIMIT       2046    /* maximum supported by hardware */
 
 /* These identify the driver base version and may not be removed. */
-static const char version[] __devinitdata =
+static char version[] __devinitdata =
   KERN_INFO DRV_NAME " dp8381x driver, version "
       DRV_VERSION ", " DRV_RELDATE "\n"
   KERN_INFO "  originally by Donald Becker <becker@scyld.com>\n"
@@ -136,11 +251,14 @@ MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
 MODULE_LICENSE("GPL");
 
+module_param(max_interrupt_work, int, 0);
 module_param(mtu, int, 0);
 module_param(debug, int, 0);
 module_param(rx_copybreak, int, 0);
 module_param_array(options, int, NULL, 0);
 module_param_array(full_duplex, int, NULL, 0);
+MODULE_PARM_DESC(max_interrupt_work, 
+       "DP8381x maximum events handled per interrupt");
 MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)");
 MODULE_PARM_DESC(debug, "DP8381x default debug level");
 MODULE_PARM_DESC(rx_copybreak, 
@@ -205,12 +323,12 @@ performance critical codepaths:
 The rx process only runs in the interrupt handler. Access from outside
 the interrupt handler is only permitted after disable_irq().
 
-The rx process usually runs under the netif_tx_lock. If np->intr_tx_reap
+The rx process usually runs under the dev->xmit_lock. If np->intr_tx_reap
 is set, then access is permitted under spin_lock_irq(&np->lock).
 
 Thus configuration functions that want to access everything must call
        disable_irq(dev->irq);
-       netif_tx_lock_bh(dev);
+       spin_lock_bh(dev->xmit_lock);
        spin_lock_irq(&np->lock);
 
 IV. Notes
@@ -231,6 +349,18 @@ None characterised.
 
 
 
+enum pcistuff {
+       PCI_USES_IO = 0x01,
+       PCI_USES_MEM = 0x02,
+       PCI_USES_MASTER = 0x04,
+       PCI_ADDR0 = 0x08,
+       PCI_ADDR1 = 0x10,
+};
+
+/* MMIO operations required */
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
+
+
 /*
  * Support for fibre connections on Am79C874:
  * This phy needs a special setup when connected to a fibre cable.
@@ -238,25 +368,22 @@ None characterised.
  */
 #define PHYID_AM79C874 0x0022561b
 
-enum {
-       MII_MCTRL       = 0x15,         /* mode control register */
-       MII_FX_SEL      = 0x0001,       /* 100BASE-FX (fiber) */
-       MII_EN_SCRM     = 0x0004,       /* enable scrambler (tp) */
-};
+#define MII_MCTRL      0x15    /* mode control register */
+#define MII_FX_SEL     0x0001  /* 100BASE-FX (fiber) */
+#define MII_EN_SCRM    0x0004  /* enable scrambler (tp) */
 
  
 /* array of board data directly indexed by pci_tbl[x].driver_data */
-static const struct {
+static struct {
        const char *name;
        unsigned long flags;
-       unsigned int eeprom_size;
 } natsemi_pci_info[] __devinitdata = {
-       { "NatSemi DP8381[56]", 0, 24 },
+       { "NatSemi DP8381[56]", PCI_IOTYPE },
 };
 
-static const struct pci_device_id natsemi_pci_tbl[] __devinitdata = {
-       { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { }     /* terminate list */
+static struct pci_device_id natsemi_pci_tbl[] = {
+       { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_83815, PCI_ANY_ID, PCI_ANY_ID, },
+       { 0, },
 };
 MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl);
 
@@ -564,8 +691,6 @@ struct netdev_private {
        /* Based on MTU+slack. */
        unsigned int rx_buf_sz;
        int oom;
-       /* Interrupt status */
-       u32 intr_status;
        /* Do not touch the nic registers */
        int hands_off;
        /* external phy that is used: only valid if dev->if_port != PORT_TP */
@@ -592,8 +717,6 @@ struct netdev_private {
        unsigned int iosize;
        spinlock_t lock;
        u32 msg_enable;
-       /* EEPROM data */
-       int eeprom_size;
 };
 
 static void move_int_phy(struct net_device *dev, int addr);
@@ -625,8 +748,7 @@ static void init_registers(struct net_device *dev);
 static int start_tx(struct sk_buff *skb, struct net_device *dev);
 static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
 static void netdev_error(struct net_device *dev, int intr_status);
-static int natsemi_poll(struct net_device *dev, int *budget);
-static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do);
+static void netdev_rx(struct net_device *dev);
 static void netdev_tx_done(struct net_device *dev);
 static int natsemi_change_mtu(struct net_device *dev, int new_mtu);
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -654,18 +776,6 @@ static inline void __iomem *ns_ioaddr(struct net_device *dev)
        return (void __iomem *) dev->base_addr;
 }
 
-static inline void natsemi_irq_enable(struct net_device *dev)
-{
-       writel(1, ns_ioaddr(dev) + IntrEnable);
-       readl(ns_ioaddr(dev) + IntrEnable);
-}
-
-static inline void natsemi_irq_disable(struct net_device *dev)
-{
-       writel(0, ns_ioaddr(dev) + IntrEnable);
-       readl(ns_ioaddr(dev) + IntrEnable);
-}
-
 static void move_int_phy(struct net_device *dev, int addr)
 {
        struct netdev_private *np = netdev_priv(dev);
@@ -691,42 +801,6 @@ static void move_int_phy(struct net_device *dev, int addr)
        udelay(1);
 }
 
-static void __devinit natsemi_init_media (struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       u32 tmp;
-
-       netif_carrier_off(dev);
-
-       /* get the initial settings from hardware */
-       tmp            = mdio_read(dev, MII_BMCR);
-       np->speed      = (tmp & BMCR_SPEED100)? SPEED_100     : SPEED_10;
-       np->duplex     = (tmp & BMCR_FULLDPLX)? DUPLEX_FULL   : DUPLEX_HALF;
-       np->autoneg    = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE;
-       np->advertising= mdio_read(dev, MII_ADVERTISE);
-
-       if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL
-        && netif_msg_probe(np)) {
-               printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s "
-                       "10%s %s duplex.\n",
-                       pci_name(np->pci_dev),
-                       (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE)?
-                         "enabled, advertise" : "disabled, force",
-                       (np->advertising &
-                         (ADVERTISE_100FULL|ADVERTISE_100HALF))?
-                           "0" : "",
-                       (np->advertising &
-                         (ADVERTISE_100FULL|ADVERTISE_10FULL))?
-                           "full" : "half");
-       }
-       if (netif_msg_probe(np))
-               printk(KERN_INFO
-                       "natsemi %s: Transceiver status %#04x advertising %#04x.\n",
-                       pci_name(np->pci_dev), mdio_read(dev, MII_BMSR),
-                       np->advertising);
-
-}
-
 static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        const struct pci_device_id *ent)
 {
@@ -766,7 +840,8 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        iosize = pci_resource_len(pdev, pcibar);
        irq = pdev->irq;
 
-       pci_set_master(pdev);
+       if (natsemi_pci_info[chip_idx].flags & PCI_USES_MASTER)
+               pci_set_master(pdev);
 
        dev = alloc_etherdev(sizeof (struct netdev_private));
        if (!dev)
@@ -804,8 +879,6 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        spin_lock_init(&np->lock);
        np->msg_enable = (debug >= 0) ? (1<<debug)-1 : NATSEMI_DEF_MSG;
        np->hands_off = 0;
-       np->intr_status = 0;
-       np->eeprom_size = natsemi_pci_info[chip_idx].eeprom_size;
 
        /* Initial port:
         * - If the nic was configured to use an external phy and if find_mii
@@ -859,9 +932,6 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        dev->do_ioctl = &netdev_ioctl;
        dev->tx_timeout = &tx_timeout;
        dev->watchdog_timeo = TX_TIMEOUT;
-       dev->poll = natsemi_poll;
-       dev->weight = 64;
-
 #ifdef CONFIG_NET_POLL_CONTROLLER
        dev->poll_controller = &natsemi_poll_controller;
 #endif
@@ -870,7 +940,34 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        if (mtu)
                dev->mtu = mtu;
 
-       natsemi_init_media(dev);
+       netif_carrier_off(dev);
+
+       /* get the initial settings from hardware */
+       tmp            = mdio_read(dev, MII_BMCR);
+       np->speed      = (tmp & BMCR_SPEED100)? SPEED_100     : SPEED_10;
+       np->duplex     = (tmp & BMCR_FULLDPLX)? DUPLEX_FULL   : DUPLEX_HALF;
+       np->autoneg    = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE;
+       np->advertising= mdio_read(dev, MII_ADVERTISE);
+
+       if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL
+        && netif_msg_probe(np)) {
+               printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s "
+                       "10%s %s duplex.\n",
+                       pci_name(np->pci_dev),
+                       (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE)?
+                         "enabled, advertise" : "disabled, force",
+                       (np->advertising &
+                         (ADVERTISE_100FULL|ADVERTISE_100HALF))?
+                           "0" : "",
+                       (np->advertising &
+                         (ADVERTISE_100FULL|ADVERTISE_10FULL))?
+                           "full" : "half");
+       }
+       if (netif_msg_probe(np))
+               printk(KERN_INFO
+                       "natsemi %s: Transceiver status %#04x advertising %#04x.\n",
+                       pci_name(np->pci_dev), mdio_read(dev, MII_BMSR),
+                       np->advertising);
 
        /* save the silicon revision for later querying */
        np->srr = readl(ioaddr + SiliconRev);
@@ -1387,31 +1484,6 @@ static void natsemi_reset(struct net_device *dev)
        writel(rfcr, ioaddr + RxFilterAddr);
 }
 
-static void reset_rx(struct net_device *dev)
-{
-       int i;
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem *ioaddr = ns_ioaddr(dev);
-
-       np->intr_status &= ~RxResetDone;
-
-       writel(RxReset, ioaddr + ChipCmd);
-
-       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
-               np->intr_status |= readl(ioaddr + IntrStatus);
-               if (np->intr_status & RxResetDone)
-                       break;
-               udelay(15);
-       }
-       if (i==NATSEMI_HW_TIMEOUT) {
-               printk(KERN_WARNING "%s: RX reset did not complete in %d usec.\n",
-                      dev->name, i*15);
-       } else if (netif_msg_hw(np)) {
-               printk(KERN_WARNING "%s: RX reset took %d usec.\n",
-                      dev->name, i*15);
-       }
-}
-
 static void natsemi_reload_eeprom(struct net_device *dev)
 {
        struct netdev_private *np = netdev_priv(dev);
@@ -1463,7 +1535,7 @@ static int netdev_open(struct net_device *dev)
        /* Reset the chip, just in case. */
        natsemi_reset(dev);
 
-       i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev);
+       i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev);
        if (i) return i;
 
        if (netif_msg_ifup(np))
@@ -2086,92 +2158,68 @@ static void netdev_tx_done(struct net_device *dev)
        }
 }
 
-/* The interrupt handler doesn't actually handle interrupts itself, it
* schedules a NAPI poll if there is anything to do. */
+/* The interrupt handler does all of the Rx thread work and cleans up
  after the Tx thread. */
 static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
 {
        struct net_device *dev = dev_instance;
        struct netdev_private *np = netdev_priv(dev);
        void __iomem * ioaddr = ns_ioaddr(dev);
+       int boguscnt = max_interrupt_work;
+       unsigned int handled = 0;
 
        if (np->hands_off)
                return IRQ_NONE;
-       
-       /* Reading automatically acknowledges. */
-       np->intr_status = readl(ioaddr + IntrStatus);
-
-       if (netif_msg_intr(np))
-               printk(KERN_DEBUG
-                      "%s: Interrupt, status %#08x, mask %#08x.\n",
-                      dev->name, np->intr_status,
-                      readl(ioaddr + IntrMask));
-
-       if (!np->intr_status) 
-               return IRQ_NONE;
-
-       prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
+       do {
+               /* Reading automatically acknowledges all int sources. */
+               u32 intr_status = readl(ioaddr + IntrStatus);
 
-       if (netif_rx_schedule_prep(dev)) {
-               /* Disable interrupts and register for poll */
-               natsemi_irq_disable(dev);
-               __netif_rx_schedule(dev);
-       }
-       return IRQ_HANDLED;
-}
+               if (netif_msg_intr(np))
+                       printk(KERN_DEBUG
+                               "%s: Interrupt, status %#08x, mask %#08x.\n",
+                               dev->name, intr_status,
+                               readl(ioaddr + IntrMask));
 
-/* This is the NAPI poll routine.  As well as the standard RX handling
- * it also handles all other interrupts that the chip might raise.
- */
-static int natsemi_poll(struct net_device *dev, int *budget)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
+               if (intr_status == 0)
+                       break;
+               handled = 1;
 
-       int work_to_do = min(*budget, dev->quota);
-       int work_done = 0;
+               if (intr_status &
+                  (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
+                   IntrRxErr | IntrRxOverrun)) {
+                       netdev_rx(dev);
+               }
 
-       do {
-               if (np->intr_status &
-                   (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) {
+               if (intr_status &
+                  (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) {
                        spin_lock(&np->lock);
                        netdev_tx_done(dev);
                        spin_unlock(&np->lock);
                }
 
                /* Abnormal error summary/uncommon events handlers. */
-               if (np->intr_status & IntrAbnormalSummary)
-                       netdev_error(dev, np->intr_status);
-               
-               if (np->intr_status &
-                   (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
-                    IntrRxErr | IntrRxOverrun)) {
-                       netdev_rx(dev, &work_done, work_to_do);
+               if (intr_status & IntrAbnormalSummary)
+                       netdev_error(dev, intr_status);
+
+               if (--boguscnt < 0) {
+                       if (netif_msg_intr(np))
+                               printk(KERN_WARNING
+                                       "%s: Too much work at interrupt, "
+                                       "status=%#08x.\n",
+                                       dev->name, intr_status);
+                       break;
                }
-               
-               *budget -= work_done;
-               dev->quota -= work_done;
-
-               if (work_done >= work_to_do)
-                       return 1;
-
-               np->intr_status = readl(ioaddr + IntrStatus);
-       } while (np->intr_status);
+       } while (1);
 
-       netif_rx_complete(dev);
-
-       /* Reenable interrupts providing nothing is trying to shut
-        * the chip down. */
-       spin_lock(&np->lock);
-       if (!np->hands_off && netif_running(dev))
-               natsemi_irq_enable(dev);
-       spin_unlock(&np->lock);
+       if (netif_msg_intr(np))
+               printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name);
 
-       return 0;
+       return IRQ_RETVAL(handled);
 }
 
 /* This routine is logically part of the interrupt handler, but separated
    for clarity and better register allocation. */
-static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
+static void netdev_rx(struct net_device *dev)
 {
        struct netdev_private *np = netdev_priv(dev);
        int entry = np->cur_rx % RX_RING_SIZE;
@@ -2189,12 +2237,6 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
                                entry, desc_status);
                if (--boguscnt < 0)
                        break;
-
-               if (*work_done >= work_to_do)
-                       break;
-
-               (*work_done)++;
-
                pkt_len = (desc_status & DescSizeMask) - 4;
                if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){
                        if (desc_status & DescMore) {
@@ -2206,23 +2248,6 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
                                                "status %#08x.\n", dev->name,
                                                np->cur_rx, desc_status);
                                np->stats.rx_length_errors++;
-
-                               /* The RX state machine has probably
-                                * locked up beneath us.  Follow the
-                                * reset procedure documented in
-                                * AN-1287. */
-
-                               spin_lock_irq(&np->lock);
-                               reset_rx(dev);
-                               reinit_rx(dev);
-                               writel(np->ring_dma, ioaddr + RxRingPtr);
-                               check_link(dev);
-                               spin_unlock_irq(&np->lock);
-
-                               /* We'll enable RX on exit from this
-                                * function. */
-                               break;
-
                        } else {
                                /* There was an error. */
                                np->stats.rx_errors++;
@@ -2268,7 +2293,7 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
                                np->rx_skbuff[entry] = NULL;
                        }
                        skb->protocol = eth_type_trans(skb, dev);
-                       netif_receive_skb(skb);
+                       netif_rx(skb);
                        dev->last_rx = jiffies;
                        np->stats.rx_packets++;
                        np->stats.rx_bytes += pkt_len;
@@ -2471,8 +2496,7 @@ static int get_regs_len(struct net_device *dev)
 
 static int get_eeprom_len(struct net_device *dev)
 {
-       struct netdev_private *np = netdev_priv(dev);
-       return np->eeprom_size;
+       return NATSEMI_EEPROM_SIZE;
 }
 
 static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
@@ -2559,20 +2583,15 @@ static u32 get_link(struct net_device *dev)
 static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
 {
        struct netdev_private *np = netdev_priv(dev);
-       u8 *eebuf;
+       u8 eebuf[NATSEMI_EEPROM_SIZE];
        int res;
 
-       eebuf = kmalloc(np->eeprom_size, GFP_KERNEL);
-       if (!eebuf)
-               return -ENOMEM;
-
        eeprom->magic = PCI_VENDOR_ID_NS | (PCI_DEVICE_ID_NS_83815<<16);
        spin_lock_irq(&np->lock);
        res = netdev_get_eeprom(dev, eebuf);
        spin_unlock_irq(&np->lock);
        if (!res)
                memcpy(data, eebuf+eeprom->offset, eeprom->len);
-       kfree(eebuf);
        return res;
 }
 
@@ -2928,10 +2947,9 @@ static int netdev_get_eeprom(struct net_device *dev, u8 *buf)
        int i;
        u16 *ebuf = (u16 *)buf;
        void __iomem * ioaddr = ns_ioaddr(dev);
-       struct netdev_private *np = netdev_priv(dev);
 
        /* eeprom_read reads 16 bits, and indexes by 16 bits */
-       for (i = 0; i < np->eeprom_size/2; i++) {
+       for (i = 0; i < NATSEMI_EEPROM_SIZE/2; i++) {
                ebuf[i] = eeprom_read(ioaddr, i);
                /* The EEPROM itself stores data bit-swapped, but eeprom_read
                 * reads it back "sanely". So we swap it back here in order to
@@ -3056,7 +3074,9 @@ static int netdev_close(struct net_device *dev)
        del_timer_sync(&np->timer);
        disable_irq(dev->irq);
        spin_lock_irq(&np->lock);
-       natsemi_irq_disable(dev);
+       /* Disable interrupts, and flush posted writes */
+       writel(0, ioaddr + IntrEnable);
+       readl(ioaddr + IntrEnable);
        np->hands_off = 1;
        spin_unlock_irq(&np->lock);
        enable_irq(dev->irq);
@@ -3138,9 +3158,6 @@ static void __devexit natsemi_remove1 (struct pci_dev *pdev)
  *     * netdev_timer: timer stopped by natsemi_suspend.
  *     * intr_handler: doesn't acquire the spinlock. suspend calls
  *             disable_irq() to enforce synchronization.
- *      * natsemi_poll: checks before reenabling interrupts.  suspend
- *              sets hands_off, disables interrupts and then waits with
- *              netif_poll_disable().
  *
  * Interrupts must be disabled, otherwise hands_off can cause irq storms.
  */
@@ -3166,8 +3183,6 @@ static int natsemi_suspend (struct pci_dev *pdev, pm_message_t state)
                spin_unlock_irq(&np->lock);
                enable_irq(dev->irq);
 
-               netif_poll_disable(dev);
-
                /* Update the error counts. */
                __get_stats(dev);
 
@@ -3220,7 +3235,6 @@ static int natsemi_resume (struct pci_dev *pdev)
                mod_timer(&np->timer, jiffies + 1*HZ);
        }
        netif_device_attach(dev);
-       netif_poll_enable(dev);
 out:
        rtnl_unlock();
        return 0;