Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / net / natsemi.c
index fd44cb7..9062775 100644 (file)
@@ -3,6 +3,7 @@
        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.
 
        TODO:
        * big endian support with CFG:BEM instead of cpu_to_le32
-       * support for an external PHY
-       * NAPI
 */
 
 #include <linux/config.h>
 #include <linux/rtnetlink.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/bitops.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
                                 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 int multicast_filter_limit = 100;
+static const int multicast_filter_limit = 100;
 
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1518 effectively disables this feature. */
@@ -228,7 +226,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 */
+#define NATSEMI_DEF_EEPROM_SIZE        24 /* 12 16-bit values */
 
 /* Buffer sizes:
  * The nic writes 32-bit values, even if the upper bytes of
@@ -240,7 +238,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 char version[] __devinitdata =
+static const 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"
@@ -251,14 +249,11 @@ MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
 MODULE_LICENSE("GPL");
 
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(mtu, "i");
-MODULE_PARM(debug, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM_DESC(max_interrupt_work, 
-       "DP8381x maximum events handled per interrupt");
+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(mtu, "DP8381x MTU (all boards)");
 MODULE_PARM_DESC(debug, "DP8381x default debug level");
 MODULE_PARM_DESC(rx_copybreak, 
@@ -374,7 +369,7 @@ enum pcistuff {
 
  
 /* array of board data directly indexed by pci_tbl[x].driver_data */
-static struct {
+static const struct {
        const char *name;
        unsigned long flags;
 } natsemi_pci_info[] __devinitdata = {
@@ -441,6 +436,7 @@ enum register_offsets {
 #define DSPCFG_VAL     0x5040
 #define SDCFG_VAL      0x008c  /* set voltage thresholds for Signal Detect */
 #define DSPCFG_LOCK    0x20    /* coefficient lock bit in DSPCFG */
+#define DSPCFG_COEF    0x1000  /* see coefficient (in TSTDAT) bit in DSPCFG */
 #define TSTDAT_FIXED   0xe8    /* magic number for bad coefficients */
 
 /* misc PCI space registers */
@@ -690,6 +686,8 @@ 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 */
@@ -716,6 +714,8 @@ 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);
@@ -747,7 +747,8 @@ 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 void netdev_rx(struct net_device *dev);
+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_tx_done(struct net_device *dev);
 static int natsemi_change_mtu(struct net_device *dev, int new_mtu);
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -768,12 +769,25 @@ static void enable_wol_mode(struct net_device *dev, int enable_intr);
 static int netdev_close(struct net_device *dev);
 static int netdev_get_regs(struct net_device *dev, u8 *buf);
 static int netdev_get_eeprom(struct net_device *dev, u8 *buf);
+static struct ethtool_ops ethtool_ops;
 
 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);
@@ -877,6 +891,8 @@ 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_DEF_EEPROM_SIZE;
 
        /* Initial port:
         * - If the nic was configured to use an external phy and if find_mii
@@ -930,9 +946,13 @@ 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
+       SET_ETHTOOL_OPS(dev, &ethtool_ops);
 
        if (mtu)
                dev->mtu = mtu;
@@ -1241,7 +1261,8 @@ static void init_phy_fixup(struct net_device *dev)
                writew(1, ioaddr + PGSEL);
                writew(PMDCSR_VAL, ioaddr + PMDCSR);
                writew(TSTDAT_VAL, ioaddr + TSTDAT);
-               np->dspcfg = DSPCFG_VAL;
+               np->dspcfg = (np->srr <= SRR_DP83815_C)?
+                       DSPCFG_VAL : (DSPCFG_COEF | readw(ioaddr + DSPCFG));
                writew(np->dspcfg, ioaddr + DSPCFG);
                writew(SDCFG_VAL, ioaddr + SDCFG);
                writew(0, ioaddr + PGSEL);
@@ -1480,6 +1501,31 @@ 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);
@@ -1922,7 +1968,7 @@ static void refill_rx(struct net_device *dev)
                                break; /* Better luck next round. */
                        skb->dev = dev; /* Mark as being used by this device. */
                        np->rx_dma[entry] = pci_map_single(np->pci_dev,
-                               skb->tail, buflen, PCI_DMA_FROMDEVICE);
+                               skb->data, buflen, PCI_DMA_FROMDEVICE);
                        np->rx_ring[entry].addr = cpu_to_le32(np->rx_dma[entry]);
                }
                np->rx_ring[entry].cmd_status = cpu_to_le32(np->rx_buf_sz);
@@ -2154,68 +2200,92 @@ static void netdev_tx_done(struct net_device *dev)
        }
 }
 
-/* The interrupt handler does all of the Rx thread work and cleans up
  after the Tx thread. */
+/* The interrupt handler doesn't actually handle interrupts itself, it
* schedules a NAPI poll if there is anything to do. */
 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;
-       do {
-               /* Reading automatically acknowledges all int sources. */
-               u32 intr_status = readl(ioaddr + IntrStatus);
+       
+       /* 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, intr_status,
-                               readl(ioaddr + IntrMask));
+       if (netif_msg_intr(np))
+               printk(KERN_DEBUG
+                      "%s: Interrupt, status %#08x, mask %#08x.\n",
+                      dev->name, np->intr_status,
+                      readl(ioaddr + IntrMask));
 
-               if (intr_status == 0)
-                       break;
-               handled = 1;
+       if (!np->intr_status) 
+               return IRQ_NONE;
 
-               if (intr_status &
-                  (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
-                   IntrRxErr | IntrRxOverrun)) {
-                       netdev_rx(dev);
-               }
+       prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
+
+       if (netif_rx_schedule_prep(dev)) {
+               /* Disable interrupts and register for poll */
+               natsemi_irq_disable(dev);
+               __netif_rx_schedule(dev);
+       }
+       return IRQ_HANDLED;
+}
+
+/* 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 &
-                  (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) {
+       int work_to_do = min(*budget, dev->quota);
+       int work_done = 0;
+
+       do {
+               if (np->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 (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;
+               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);
                }
-       } while (1);
+               
+               *budget -= work_done;
+               dev->quota -= work_done;
 
-       if (netif_msg_intr(np))
-               printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name);
+               if (work_done >= work_to_do)
+                       return 1;
+
+               np->intr_status = readl(ioaddr + IntrStatus);
+       } while (np->intr_status);
+
+       netif_rx_complete(dev);
 
-       return IRQ_RETVAL(handled);
+       /* 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);
+
+       return 0;
 }
 
 /* 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)
+static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
 {
        struct netdev_private *np = netdev_priv(dev);
        int entry = np->cur_rx % RX_RING_SIZE;
@@ -2233,6 +2303,12 @@ static void netdev_rx(struct net_device *dev)
                                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) {
@@ -2244,6 +2320,23 @@ static void netdev_rx(struct net_device *dev)
                                                "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++;
@@ -2276,7 +2369,7 @@ static void netdev_rx(struct net_device *dev)
                                        buflen,
                                        PCI_DMA_FROMDEVICE);
                                eth_copy_and_sum(skb,
-                                       np->rx_skbuff[entry]->tail, pkt_len, 0);
+                                       np->rx_skbuff[entry]->data, pkt_len, 0);
                                skb_put(skb, pkt_len);
                                pci_dma_sync_single_for_device(np->pci_dev,
                                        np->rx_dma[entry],
@@ -2289,7 +2382,7 @@ static void netdev_rx(struct net_device *dev)
                                np->rx_skbuff[entry] = NULL;
                        }
                        skb->protocol = eth_type_trans(skb, dev);
-                       netif_rx(skb);
+                       netif_receive_skb(skb);
                        dev->last_rx = jiffies;
                        np->stats.rx_packets++;
                        np->stats.rx_bytes += pkt_len;
@@ -2429,9 +2522,9 @@ static void __set_rx_mode(struct net_device *dev)
                rx_mode = RxFilterEnable | AcceptBroadcast
                        | AcceptMulticast | AcceptMyPhys;
                for (i = 0; i < 64; i += 2) {
-                       writew(HASH_TABLE + i, ioaddr + RxFilterAddr);
-                       writew((mc_filter[i+1]<<8) + mc_filter[i],
-                               ioaddr + RxFilterData);
+                       writel(HASH_TABLE + i, ioaddr + RxFilterAddr);
+                       writel((mc_filter[i + 1] << 8) + mc_filter[i],
+                              ioaddr + RxFilterData);
                }
        }
        writel(rx_mode, ioaddr + RxFilterAddr);
@@ -2477,177 +2570,142 @@ static void set_rx_mode(struct net_device *dev)
        spin_unlock_irq(&np->lock);
 }
 
-static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr)
+static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
        struct netdev_private *np = netdev_priv(dev);
-       u32 cmd;
+       strncpy(info->driver, DRV_NAME, ETHTOOL_BUSINFO_LEN);
+       strncpy(info->version, DRV_VERSION, ETHTOOL_BUSINFO_LEN);
+       strncpy(info->bus_info, pci_name(np->pci_dev), ETHTOOL_BUSINFO_LEN);
+}
 
-       if (get_user(cmd, (u32 __user *)useraddr))
-               return -EFAULT;
-
-       switch (cmd) {
-       /* get driver info */
-       case ETHTOOL_GDRVINFO: {
-               struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
-               strncpy(info.driver, DRV_NAME, ETHTOOL_BUSINFO_LEN);
-               strncpy(info.version, DRV_VERSION, ETHTOOL_BUSINFO_LEN);
-               info.fw_version[0] = '\0';
-               strncpy(info.bus_info, pci_name(np->pci_dev),
-                       ETHTOOL_BUSINFO_LEN);
-               info.eedump_len = NATSEMI_EEPROM_SIZE;
-               info.regdump_len = NATSEMI_REGS_SIZE;
-               if (copy_to_user(useraddr, &info, sizeof(info)))
-                       return -EFAULT;
-               return 0;
-       }
-       /* get settings */
-       case ETHTOOL_GSET: {
-               struct ethtool_cmd ecmd = { ETHTOOL_GSET };
-               spin_lock_irq(&np->lock);
-               netdev_get_ecmd(dev, &ecmd);
-               spin_unlock_irq(&np->lock);
-               if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
-                       return -EFAULT;
-               return 0;
-       }
-       /* set settings */
-       case ETHTOOL_SSET: {
-               struct ethtool_cmd ecmd;
-               int r;
-               if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
-                       return -EFAULT;
-               spin_lock_irq(&np->lock);
-               r = netdev_set_ecmd(dev, &ecmd);
-               spin_unlock_irq(&np->lock);
-               return r;
-       }
-       /* get wake-on-lan */
-       case ETHTOOL_GWOL: {
-               struct ethtool_wolinfo wol = {ETHTOOL_GWOL};
-               spin_lock_irq(&np->lock);
-               netdev_get_wol(dev, &wol.supported, &wol.wolopts);
-               netdev_get_sopass(dev, wol.sopass);
-               spin_unlock_irq(&np->lock);
-               if (copy_to_user(useraddr, &wol, sizeof(wol)))
-                       return -EFAULT;
-               return 0;
-       }
-       /* set wake-on-lan */
-       case ETHTOOL_SWOL: {
-               struct ethtool_wolinfo wol;
-               int r;
-               if (copy_from_user(&wol, useraddr, sizeof(wol)))
-                       return -EFAULT;
-               spin_lock_irq(&np->lock);
-               netdev_set_wol(dev, wol.wolopts);
-               r = netdev_set_sopass(dev, wol.sopass);
-               spin_unlock_irq(&np->lock);
-               return r;
-       }
-       /* get registers */
-       case ETHTOOL_GREGS: {
-               struct ethtool_regs regs;
-               u8 regbuf[NATSEMI_REGS_SIZE];
-               int r;
+static int get_regs_len(struct net_device *dev)
+{
+       return NATSEMI_REGS_SIZE;
+}
 
-               if (copy_from_user(&regs, useraddr, sizeof(regs)))
-                       return -EFAULT;
+static int get_eeprom_len(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       return np->eeprom_size;
+}
 
-               if (regs.len > NATSEMI_REGS_SIZE) {
-                       regs.len = NATSEMI_REGS_SIZE;
-               }
-               regs.version = NATSEMI_REGS_VER;
-               if (copy_to_user(useraddr, &regs, sizeof(regs)))
-                       return -EFAULT;
+static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       spin_lock_irq(&np->lock);
+       netdev_get_ecmd(dev, ecmd);
+       spin_unlock_irq(&np->lock);
+       return 0;
+}
 
-               useraddr += offsetof(struct ethtool_regs, data);
+static int set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       int res;
+       spin_lock_irq(&np->lock);
+       res = netdev_set_ecmd(dev, ecmd);
+       spin_unlock_irq(&np->lock);
+       return res;
+}
 
-               spin_lock_irq(&np->lock);
-               r = netdev_get_regs(dev, regbuf);
-               spin_unlock_irq(&np->lock);
+static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       spin_lock_irq(&np->lock);
+       netdev_get_wol(dev, &wol->supported, &wol->wolopts);
+       netdev_get_sopass(dev, wol->sopass);
+       spin_unlock_irq(&np->lock);
+}
 
-               if (r)
-                       return r;
-               if (copy_to_user(useraddr, regbuf, regs.len))
-                       return -EFAULT;
-               return 0;
-       }
-       /* get message-level */
-       case ETHTOOL_GMSGLVL: {
-               struct ethtool_value edata = {ETHTOOL_GMSGLVL};
-               edata.data = np->msg_enable;
-               if (copy_to_user(useraddr, &edata, sizeof(edata)))
-                       return -EFAULT;
-               return 0;
-       }
-       /* set message-level */
-       case ETHTOOL_SMSGLVL: {
-               struct ethtool_value edata;
-               if (copy_from_user(&edata, useraddr, sizeof(edata)))
-                       return -EFAULT;
-               np->msg_enable = edata.data;
-               return 0;
-       }
-       /* restart autonegotiation */
-       case ETHTOOL_NWAY_RST: {
-               int tmp;
-               int r = -EINVAL;
-               /* if autoneg is off, it's an error */
-               tmp = mdio_read(dev, MII_BMCR);
-               if (tmp & BMCR_ANENABLE) {
-                       tmp |= (BMCR_ANRESTART);
-                       mdio_write(dev, MII_BMCR, tmp);
-                       r = 0;
-               }
-               return r;
-       }
-       /* get link status */
-       case ETHTOOL_GLINK: {
-               struct ethtool_value edata = {ETHTOOL_GLINK};
-               /* LSTATUS is latched low until a read - so read twice */
-               mdio_read(dev, MII_BMSR);
-               edata.data = (mdio_read(dev, MII_BMSR)&BMSR_LSTATUS) ? 1:0;
-               if (copy_to_user(useraddr, &edata, sizeof(edata)))
-                       return -EFAULT;
-               return 0;
-       }
-       /* get EEPROM */
-       case ETHTOOL_GEEPROM: {
-               struct ethtool_eeprom eeprom;
-               u8 eebuf[NATSEMI_EEPROM_SIZE];
-               int r;
+static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       int res;
+       spin_lock_irq(&np->lock);
+       netdev_set_wol(dev, wol->wolopts);
+       res = netdev_set_sopass(dev, wol->sopass);
+       spin_unlock_irq(&np->lock);
+       return res;
+}
 
-               if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
-                       return -EFAULT;
+static void get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       regs->version = NATSEMI_REGS_VER;
+       spin_lock_irq(&np->lock);
+       netdev_get_regs(dev, buf);
+       spin_unlock_irq(&np->lock);
+}
 
-               if (eeprom.offset > eeprom.offset+eeprom.len)
-                       return -EINVAL;
+static u32 get_msglevel(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       return np->msg_enable;
+}
 
-               if ((eeprom.offset+eeprom.len) > NATSEMI_EEPROM_SIZE) {
-                       eeprom.len = NATSEMI_EEPROM_SIZE-eeprom.offset;
-               }
-               eeprom.magic = PCI_VENDOR_ID_NS | (PCI_DEVICE_ID_NS_83815<<16);
-               if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
-                       return -EFAULT;
+static void set_msglevel(struct net_device *dev, u32 val)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       np->msg_enable = val;
+}
 
-               useraddr += offsetof(struct ethtool_eeprom, data);
+static int nway_reset(struct net_device *dev)
+{
+       int tmp;
+       int r = -EINVAL;
+       /* if autoneg is off, it's an error */
+       tmp = mdio_read(dev, MII_BMCR);
+       if (tmp & BMCR_ANENABLE) {
+               tmp |= (BMCR_ANRESTART);
+               mdio_write(dev, MII_BMCR, tmp);
+               r = 0;
+       }
+       return r;
+}
 
-               spin_lock_irq(&np->lock);
-               r = netdev_get_eeprom(dev, eebuf);
-               spin_unlock_irq(&np->lock);
+static u32 get_link(struct net_device *dev)
+{
+       /* LSTATUS is latched low until a read - so read twice */
+       mdio_read(dev, MII_BMSR);
+       return (mdio_read(dev, MII_BMSR)&BMSR_LSTATUS) ? 1:0;
+}
 
-               if (r)
-                       return r;
-               if (copy_to_user(useraddr, eebuf+eeprom.offset, eeprom.len))
-                       return -EFAULT;
-               return 0;
-       }
+static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       u8 *eebuf;
+       int res;
 
-       }
+       eebuf = kmalloc(np->eeprom_size, GFP_KERNEL);
+       if (!eebuf)
+               return -ENOMEM;
 
-       return -EOPNOTSUPP;
+       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;
 }
 
+static struct ethtool_ops ethtool_ops = {
+       .get_drvinfo = get_drvinfo,
+       .get_regs_len = get_regs_len,
+       .get_eeprom_len = get_eeprom_len,
+       .get_settings = get_settings,
+       .set_settings = set_settings,
+       .get_wol = get_wol,
+       .set_wol = set_wol,
+       .get_regs = get_regs,
+       .get_msglevel = get_msglevel,
+       .set_msglevel = set_msglevel,
+       .nway_reset = nway_reset,
+       .get_link = get_link,
+       .get_eeprom = get_eeprom,
+};
+
 static int netdev_set_wol(struct net_device *dev, u32 newval)
 {
        struct netdev_private *np = netdev_priv(dev);
@@ -2984,9 +3042,10 @@ 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 < NATSEMI_EEPROM_SIZE/2; i++) {
+       for (i = 0; i < np->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
@@ -3002,8 +3061,6 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
        struct netdev_private *np = netdev_priv(dev);
 
        switch(cmd) {
-       case SIOCETHTOOL:
-               return netdev_ethtool_ioctl(dev, rq->ifr_data);
        case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
        case SIOCDEVPRIVATE:            /* for binary compat, remove in 2.5 */
                data->phy_id = np->phy_addr_external;
@@ -3113,9 +3170,7 @@ static int netdev_close(struct net_device *dev)
        del_timer_sync(&np->timer);
        disable_irq(dev->irq);
        spin_lock_irq(&np->lock);
-       /* Disable interrupts, and flush posted writes */
-       writel(0, ioaddr + IntrEnable);
-       readl(ioaddr + IntrEnable);
+       natsemi_irq_disable(dev);
        np->hands_off = 1;
        spin_unlock_irq(&np->lock);
        enable_irq(dev->irq);
@@ -3197,11 +3252,14 @@ 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.
  */
 
-static int natsemi_suspend (struct pci_dev *pdev, u32 state)
+static int natsemi_suspend (struct pci_dev *pdev, pm_message_t state)
 {
        struct net_device *dev = pci_get_drvdata (pdev);
        struct netdev_private *np = netdev_priv(dev);
@@ -3222,6 +3280,8 @@ static int natsemi_suspend (struct pci_dev *pdev, u32 state)
                spin_unlock_irq(&np->lock);
                enable_irq(dev->irq);
 
+               netif_poll_disable(dev);
+
                /* Update the error counts. */
                __get_stats(dev);
 
@@ -3274,6 +3334,7 @@ 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;