fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / net / natsemi.c
index 9bae8d9..ffa0afd 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.
 
        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
 */
 
-#if !defined(__OPTIMIZE__)
-#warning  You must compile this file with the correct options!
-#warning  See the last lines of the source file.
-#error You must compile this driver with "-O".
-#endif
-
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/string.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>
 
 #define DRV_NAME       "natsemi"
-#define DRV_VERSION    "1.07+LK1.0.17"
-#define DRV_RELDATE    "Sep 27, 2002"
+#define DRV_VERSION    "2.1"
+#define DRV_RELDATE    "Sept 11, 2006"
 
 #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 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. */
@@ -234,12 +114,18 @@ 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 PKT_BUF_SZ             1536 /* Size of each temporary Rx buffer. */
+/* Buffer sizes:
+ * The nic writes 32-bit values, even if the upper bytes of
+ * a 32-bit value are beyond the end of the buffer.
+ */
+#define NATSEMI_HEADERS                22      /* 2*mac,type,vlan,crc */
+#define NATSEMI_PADDING                16      /* 2 bytes should be sufficient */
+#define NATSEMI_LONGPKT                1518    /* limit for normal packets */
+#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"
@@ -250,19 +136,16 @@ 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, 
+MODULE_PARM_DESC(rx_copybreak,
        "DP8381x copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(options, 
+MODULE_PARM_DESC(options,
        "DP8381x: Bits 0-3: media type, bit 17: full duplex");
 MODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)");
 
@@ -322,12 +205,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 dev->xmit_lock. If np->intr_tx_reap
+The rx process usually runs under the netif_tx_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);
-       spin_lock_bh(dev->xmit_lock);
+       netif_tx_lock_bh(dev);
        spin_lock_irq(&np->lock);
 
 IV. Notes
@@ -348,29 +231,32 @@ None characterised.
 
 
 
-enum pcistuff {
-       PCI_USES_IO = 0x01,
-       PCI_USES_MEM = 0x02,
-       PCI_USES_MASTER = 0x04,
-       PCI_ADDR0 = 0x08,
-       PCI_ADDR1 = 0x10,
-};
+/*
+ * Support for fibre connections on Am79C874:
+ * This phy needs a special setup when connected to a fibre cable.
+ * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/22235.pdf
+ */
+#define PHYID_AM79C874 0x0022561b
 
-/* MMIO operations required */
-#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
+enum {
+       MII_MCTRL       = 0x15,         /* mode control register */
+       MII_FX_SEL      = 0x0001,       /* 100BASE-FX (fiber) */
+       MII_EN_SCRM     = 0x0004,       /* enable scrambler (tp) */
+};
 
 
 /* array of board data directly indexed by pci_tbl[x].driver_data */
-static struct {
+static const struct {
        const char *name;
        unsigned long flags;
+       unsigned int eeprom_size;
 } natsemi_pci_info[] __devinitdata = {
-       { "NatSemi DP8381[56]", PCI_IOTYPE },
+       { "NatSemi DP8381[56]", 0, 24 },
 };
 
-static struct pci_device_id natsemi_pci_tbl[] = {
-       { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_83815, PCI_ANY_ID, PCI_ANY_ID, },
-       { 0, },
+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 */
 };
 MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl);
 
@@ -428,6 +314,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 */
@@ -463,6 +350,9 @@ enum EECtrl_bits {
        EE_DataIn               = 0x01,
        EE_ChipSelect           = 0x08,
        EE_DataOut              = 0x02,
+       MII_Data                = 0x10,
+       MII_Write               = 0x20,
+       MII_ShiftClk            = 0x40,
 };
 
 enum PCIBusCfg_bits {
@@ -524,6 +414,22 @@ enum TxConfig_bits {
        TxCarrierIgn            = 0x80000000
 };
 
+/*
+ * Tx Configuration:
+ * - 256 byte DMA burst length
+ * - fill threshold 512 bytes (i.e. restart DMA when 512 bytes are free)
+ * - 64 bytes initial drain threshold (i.e. begin actual transmission
+ *   when 64 byte are in the fifo)
+ * - on tx underruns, increase drain threshold by 64.
+ * - at most use a drain threshold of 1472 bytes: The sum of the fill
+ *   threshold and the drain threshold must be less than 2016 bytes.
+ *
+ */
+#define TX_FLTH_VAL            ((512/32) << 8)
+#define TX_DRTH_VAL_START      (64/32)
+#define TX_DRTH_VAL_INC                2
+#define TX_DRTH_VAL_LIMIT      (1472/32)
+
 enum RxConfig_bits {
        RxDrthMask              = 0x3e,
        RxMxdmaMask             = 0x700000,
@@ -540,6 +446,7 @@ enum RxConfig_bits {
        RxAcceptRunt            = 0x40000000,
        RxAcceptErr             = 0x80000000
 };
+#define RX_DRTH_VAL            (128/8)
 
 enum ClkRun_bits {
        PMEEnable               = 0x100,
@@ -594,9 +501,12 @@ enum MIntrCtrl_bits {
 };
 
 enum PhyCtrl_bits {
-       PhyAddrMask             = 0xf,
+       PhyAddrMask             = 0x1f,
 };
 
+#define PHY_ADDR_NONE          32
+#define PHY_ADDR_INTERNAL      1
+
 /* values we might find in the silicon revision register */
 #define SRR_DP83815_C  0x0302
 #define SRR_DP83815_D  0x0403
@@ -654,9 +564,13 @@ 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;
-       /* These values are keep track of the transceiver/media in use */
+       /* external phy that is used: only valid if dev->if_port != PORT_TP */
+       int mii;
+       int phy_addr_external;
        unsigned int full_duplex;
        /* Rx filter */
        u32 cur_rx_mode;
@@ -669,16 +583,27 @@ struct netdev_private {
        u32 srr;
        /* expected DSPCFG value */
        u16 dspcfg;
+       /* parms saved in ethtool format */
+       u16     speed;          /* The forced speed, 10Mb, 100Mb, gigabit */
+       u8      duplex;         /* Duplex, half or full */
+       u8      autoneg;        /* Autonegotiation enabled */
        /* MII transceiver section */
        u16 advertising;
        unsigned int iosize;
        spinlock_t lock;
        u32 msg_enable;
+       /* EEPROM data */
+       int eeprom_size;
 };
 
-static int eeprom_read(long ioaddr, int location);
-static int mdio_read(struct net_device *dev, int phy_id, int reg);
-static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 data);
+static void move_int_phy(struct net_device *dev, int addr);
+static int eeprom_read(void __iomem *ioaddr, int location);
+static int mdio_read(struct net_device *dev, int reg);
+static void mdio_write(struct net_device *dev, int reg, u16 data);
+static void init_phy_fixup(struct net_device *dev);
+static int miiport_read(struct net_device *dev, int phy_id, int reg);
+static void miiport_write(struct net_device *dev, int phy_id, int reg, u16 data);
+static int find_mii(struct net_device *dev);
 static void natsemi_reset(struct net_device *dev);
 static void natsemi_reload_eeprom(struct net_device *dev);
 static void natsemi_stop_rxtx(struct net_device *dev);
@@ -698,10 +623,15 @@ static void free_ring(struct net_device *dev);
 static void reinit_ring(struct net_device *dev);
 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 irqreturn_t intr_handler(int irq, void *dev_instance);
 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
+static void natsemi_poll_controller(struct net_device *dev);
+#endif
 static void __set_rx_mode(struct net_device *dev);
 static void set_rx_mode(struct net_device *dev);
 static void __get_stats(struct net_device *dev);
@@ -717,7 +647,85 @@ 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 const 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);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+       int target = 31;
+
+       /*
+        * The internal phy is visible on the external mii bus. Therefore we must
+        * move it away before we can send commands to an external phy.
+        * There are two addresses we must avoid:
+        * - the address on the external phy that is used for transmission.
+        * - the address that we want to access. User space can access phys
+        *   on the mii bus with SIOCGMIIREG/SIOCSMIIREG, independant from the
+        *   phy that is used for transmission.
+        */
+
+       if (target == addr)
+               target--;
+       if (target == np->phy_addr_external)
+               target--;
+       writew(target, ioaddr + PhyCtrl);
+       readw(ioaddr + PhyCtrl);
+       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)
@@ -726,7 +734,8 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        struct netdev_private *np;
        int i, option, irq, chip_idx = ent->driver_data;
        static int find_cnt = -1;
-       unsigned long ioaddr, iosize;
+       unsigned long iostart, iosize;
+       void __iomem *ioaddr;
        const int pcibar = 1; /* PCI base address register */
        int prev_eedata;
        u32 tmp;
@@ -753,12 +762,11 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        }
 
        find_cnt++;
-       ioaddr = pci_resource_start(pdev, pcibar);
+       iostart = pci_resource_start(pdev, pcibar);
        iosize = pci_resource_len(pdev, pcibar);
        irq = pdev->irq;
 
-       if (natsemi_pci_info[chip_idx].flags & PCI_USES_MASTER)
-               pci_set_master(pdev);
+       pci_set_master(pdev);
 
        dev = alloc_etherdev(sizeof (struct netdev_private));
        if (!dev)
@@ -766,11 +774,11 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        SET_MODULE_OWNER(dev);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
-       i = pci_request_regions(pdev, dev->name);
+       i = pci_request_regions(pdev, DRV_NAME);
        if (i)
                goto err_pci_request_regions;
 
-       ioaddr = (unsigned long) ioremap (ioaddr, iosize);
+       ioaddr = ioremap(iostart, iosize);
        if (!ioaddr) {
                i = -ENOMEM;
                goto err_ioremap;
@@ -785,10 +793,10 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
                prev_eedata = eedata;
        }
 
-       dev->base_addr = ioaddr;
+       dev->base_addr = (unsigned long __force) ioaddr;
        dev->irq = irq;
 
-       np = dev->priv;
+       np = netdev_priv(dev);
 
        np->pci_dev = pdev;
        pci_set_drvdata(pdev, dev);
@@ -796,11 +804,35 @@ 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
+        *   finds a phy: use external port, first phy that replies.
+        * - Otherwise: internal port.
+        * Note that the phy address for the internal phy doesn't matter:
+        * The address would be used to access a phy over the mii bus, but
+        * the internal phy is accessed through mapped registers.
+        */
+       if (readl(ioaddr + ChipConfig) & CfgExtPhy)
+               dev->if_port = PORT_MII;
+       else
+               dev->if_port = PORT_TP;
        /* Reset the chip to erase previous misconfiguration. */
        natsemi_reload_eeprom(dev);
        natsemi_reset(dev);
 
+       if (dev->if_port != PORT_TP) {
+               np->phy_addr_external = find_mii(dev);
+               if (np->phy_addr_external == PHY_ADDR_NONE) {
+                       dev->if_port = PORT_TP;
+                       np->phy_addr_external = PHY_ADDR_INTERNAL;
+               }
+       } else {
+               np->phy_addr_external = PHY_ADDR_INTERNAL;
+       }
+
        option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
        if (dev->mem_start)
                option = dev->mem_start;
@@ -811,8 +843,8 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
                        np->full_duplex = 1;
                if (option & 15)
                        printk(KERN_INFO
-                               "%s: ignoring user supplied media type %d",
-                               dev->name, option & 15);
+                               "natsemi %s: ignoring user supplied media type %d",
+                               pci_name(np->pci_dev), option & 15);
        }
        if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt])
                np->full_duplex = 1;
@@ -823,56 +855,49 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
        dev->stop = &netdev_close;
        dev->get_stats = &get_stats;
        dev->set_multicast_list = &set_rx_mode;
+       dev->change_mtu = &natsemi_change_mtu;
        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;
 
+       natsemi_init_media(dev);
+
+       /* save the silicon revision for later querying */
+       np->srr = readl(ioaddr + SiliconRev);
+       if (netif_msg_hw(np))
+               printk(KERN_INFO "natsemi %s: silicon revision %#04x.\n",
+                               pci_name(np->pci_dev), np->srr);
+
        i = register_netdev(dev);
        if (i)
                goto err_register_netdev;
 
-       netif_carrier_off(dev);
-
        if (netif_msg_drv(np)) {
-               printk(KERN_INFO "%s: %s at %#08lx, ",
-                       dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
+               printk(KERN_INFO "natsemi %s: %s at %#08lx (%s), ",
+                       dev->name, natsemi_pci_info[chip_idx].name, iostart,
+                       pci_name(np->pci_dev));
                for (i = 0; i < ETH_ALEN-1; i++)
                                printk("%02x:", dev->dev_addr[i]);
-               printk("%02x, IRQ %d.\n", dev->dev_addr[i], irq);
-       }
-
-       np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
-       if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000
-        && netif_msg_probe(np)) {
-               u32 chip_config = readl(ioaddr + ChipConfig);
-               printk(KERN_INFO "%s: Transceiver default autonegotiation %s "
-                       "10%s %s duplex.\n",
-                       dev->name,
-                       chip_config & CfgAnegEnable ?
-                         "enabled, advertise" : "disabled, force",
-                       chip_config & CfgAneg100 ? "0" : "",
-                       chip_config & CfgAnegFull ? "full" : "half");
+               printk("%02x, IRQ %d", dev->dev_addr[i], irq);
+               if (dev->if_port == PORT_TP)
+                       printk(", port TP.\n");
+               else
+                       printk(", port MII, phy ad %d.\n", np->phy_addr_external);
        }
-       if (netif_msg_probe(np))
-               printk(KERN_INFO
-                       "%s: Transceiver status %#04x advertising %#04x.\n",
-                       dev->name, mdio_read(dev, 1, MII_BMSR),
-                       np->advertising);
-
-       /* save the silicon revision for later querying */
-       np->srr = readl(ioaddr + SiliconRev);
-       if (netif_msg_hw(np))
-               printk(KERN_INFO "%s: silicon revision %#04x.\n",
-                               dev->name, np->srr);
-
-
        return 0;
 
  err_register_netdev:
-       iounmap ((void *) dev->base_addr);
+       iounmap(ioaddr);
 
  err_ioremap:
        pci_release_regions(pdev);
@@ -904,12 +929,13 @@ enum EEPROM_Cmds {
        EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
 };
 
-static int eeprom_read(long addr, int location)
+static int eeprom_read(void __iomem *addr, int location)
 {
        int i;
        int retval = 0;
-       long ee_addr = addr + EECtrl;
+       void __iomem *ee_addr = addr + EECtrl;
        int read_cmd = location | EE_ReadCmd;
+
        writel(EE_Write0, ee_addr);
 
        /* Shift the read command bits out. */
@@ -939,25 +965,342 @@ static int eeprom_read(long addr, int location)
 
 /* MII transceiver control section.
  * The 83815 series has an internal transceiver, and we present the
- * management registers as if they were MII connected. */
+ * internal management registers as if they were MII connected.
+ * External Phy registers are referenced through the MII interface.
+ */
 
-static int mdio_read(struct net_device *dev, int phy_id, int reg)
+/* clock transitions >= 20ns (25MHz)
+ * One readl should be good to PCI @ 100MHz
+ */
+#define mii_delay(ioaddr)  readl(ioaddr + EECtrl)
+
+static int mii_getbit (struct net_device *dev)
 {
-       if (phy_id == 1 && reg < 32)
-               return readl(dev->base_addr+BasicControl+(reg<<2))&0xffff;
+       int data;
+       void __iomem *ioaddr = ns_ioaddr(dev);
+
+       writel(MII_ShiftClk, ioaddr + EECtrl);
+       data = readl(ioaddr + EECtrl);
+       writel(0, ioaddr + EECtrl);
+       mii_delay(ioaddr);
+       return (data & MII_Data)? 1 : 0;
+}
+
+static void mii_send_bits (struct net_device *dev, u32 data, int len)
+{
+       u32 i;
+       void __iomem *ioaddr = ns_ioaddr(dev);
+
+       for (i = (1 << (len-1)); i; i >>= 1)
+       {
+               u32 mdio_val = MII_Write | ((data & i)? MII_Data : 0);
+               writel(mdio_val, ioaddr + EECtrl);
+               mii_delay(ioaddr);
+               writel(mdio_val | MII_ShiftClk, ioaddr + EECtrl);
+               mii_delay(ioaddr);
+       }
+       writel(0, ioaddr + EECtrl);
+       mii_delay(ioaddr);
+}
+
+static int miiport_read(struct net_device *dev, int phy_id, int reg)
+{
+       u32 cmd;
+       int i;
+       u32 retval = 0;
+
+       /* Ensure sync */
+       mii_send_bits (dev, 0xffffffff, 32);
+       /* ST(2), OP(2), ADDR(5), REG#(5), TA(2), Data(16) total 32 bits */
+       /* ST,OP = 0110'b for read operation */
+       cmd = (0x06 << 10) | (phy_id << 5) | reg;
+       mii_send_bits (dev, cmd, 14);
+       /* Turnaround */
+       if (mii_getbit (dev))
+               return 0;
+       /* Read data */
+       for (i = 0; i < 16; i++) {
+               retval <<= 1;
+               retval |= mii_getbit (dev);
+       }
+       /* End cycle */
+       mii_getbit (dev);
+       return retval;
+}
+
+static void miiport_write(struct net_device *dev, int phy_id, int reg, u16 data)
+{
+       u32 cmd;
+
+       /* Ensure sync */
+       mii_send_bits (dev, 0xffffffff, 32);
+       /* ST(2), OP(2), ADDR(5), REG#(5), TA(2), Data(16) total 32 bits */
+       /* ST,OP,AAAAA,RRRRR,TA = 0101xxxxxxxxxx10'b = 0x5002 for write */
+       cmd = (0x5002 << 16) | (phy_id << 23) | (reg << 18) | data;
+       mii_send_bits (dev, cmd, 32);
+       /* End cycle */
+       mii_getbit (dev);
+}
+
+static int mdio_read(struct net_device *dev, int reg)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+
+       /* The 83815 series has two ports:
+        * - an internal transceiver
+        * - an external mii bus
+        */
+       if (dev->if_port == PORT_TP)
+               return readw(ioaddr+BasicControl+(reg<<2));
        else
-               return 0xffff;
+               return miiport_read(dev, np->phy_addr_external, reg);
+}
+
+static void mdio_write(struct net_device *dev, int reg, u16 data)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+
+       /* The 83815 series has an internal transceiver; handle separately */
+       if (dev->if_port == PORT_TP)
+               writew(data, ioaddr+BasicControl+(reg<<2));
+       else
+               miiport_write(dev, np->phy_addr_external, reg, data);
+}
+
+static void init_phy_fixup(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+       int i;
+       u32 cfg;
+       u16 tmp;
+
+       /* restore stuff lost when power was out */
+       tmp = mdio_read(dev, MII_BMCR);
+       if (np->autoneg == AUTONEG_ENABLE) {
+               /* renegotiate if something changed */
+               if ((tmp & BMCR_ANENABLE) == 0
+                || np->advertising != mdio_read(dev, MII_ADVERTISE))
+               {
+                       /* turn on autonegotiation and force negotiation */
+                       tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
+                       mdio_write(dev, MII_ADVERTISE, np->advertising);
+               }
+       } else {
+               /* turn off auto negotiation, set speed and duplexity */
+               tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
+               if (np->speed == SPEED_100)
+                       tmp |= BMCR_SPEED100;
+               if (np->duplex == DUPLEX_FULL)
+                       tmp |= BMCR_FULLDPLX;
+               /*
+                * Note: there is no good way to inform the link partner
+                * that our capabilities changed. The user has to unplug
+                * and replug the network cable after some changes, e.g.
+                * after switching from 10HD, autoneg off to 100 HD,
+                * autoneg off.
+                */
+       }
+       mdio_write(dev, MII_BMCR, tmp);
+       readl(ioaddr + ChipConfig);
+       udelay(1);
+
+       /* find out what phy this is */
+       np->mii = (mdio_read(dev, MII_PHYSID1) << 16)
+                               + mdio_read(dev, MII_PHYSID2);
+
+       /* handle external phys here */
+       switch (np->mii) {
+       case PHYID_AM79C874:
+               /* phy specific configuration for fibre/tp operation */
+               tmp = mdio_read(dev, MII_MCTRL);
+               tmp &= ~(MII_FX_SEL | MII_EN_SCRM);
+               if (dev->if_port == PORT_FIBRE)
+                       tmp |= MII_FX_SEL;
+               else
+                       tmp |= MII_EN_SCRM;
+               mdio_write(dev, MII_MCTRL, tmp);
+               break;
+       default:
+               break;
+       }
+       cfg = readl(ioaddr + ChipConfig);
+       if (cfg & CfgExtPhy)
+               return;
+
+       /* On page 78 of the spec, they recommend some settings for "optimum
+          performance" to be done in sequence.  These settings optimize some
+          of the 100Mbit autodetection circuitry.  They say we only want to
+          do this for rev C of the chip, but engineers at NSC (Bradley
+          Kennedy) recommends always setting them.  If you don't, you get
+          errors on some autonegotiations that make the device unusable.
+
+          It seems that the DSP needs a few usec to reinitialize after
+          the start of the phy. Just retry writing these values until they
+          stick.
+       */
+       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+
+               int dspcfg;
+               writew(1, ioaddr + PGSEL);
+               writew(PMDCSR_VAL, ioaddr + PMDCSR);
+               writew(TSTDAT_VAL, ioaddr + TSTDAT);
+               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);
+               readl(ioaddr + ChipConfig);
+               udelay(10);
+
+               writew(1, ioaddr + PGSEL);
+               dspcfg = readw(ioaddr + DSPCFG);
+               writew(0, ioaddr + PGSEL);
+               if (np->dspcfg == dspcfg)
+                       break;
+       }
+
+       if (netif_msg_link(np)) {
+               if (i==NATSEMI_HW_TIMEOUT) {
+                       printk(KERN_INFO
+                               "%s: DSPCFG mismatch after retrying for %d usec.\n",
+                               dev->name, i*10);
+               } else {
+                       printk(KERN_INFO
+                               "%s: DSPCFG accepted after %d usec.\n",
+                               dev->name, i*10);
+               }
+       }
+       /*
+        * Enable PHY Specific event based interrupts.  Link state change
+        * and Auto-Negotiation Completion are among the affected.
+        * Read the intr status to clear it (needed for wake events).
+        */
+       readw(ioaddr + MIntrStatus);
+       writew(MICRIntEn, ioaddr + MIntrCtrl);
+}
+
+static int switch_port_external(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+       u32 cfg;
+
+       cfg = readl(ioaddr + ChipConfig);
+       if (cfg & CfgExtPhy)
+               return 0;
+
+       if (netif_msg_link(np)) {
+               printk(KERN_INFO "%s: switching to external transceiver.\n",
+                               dev->name);
+       }
+
+       /* 1) switch back to external phy */
+       writel(cfg | (CfgExtPhy | CfgPhyDis), ioaddr + ChipConfig);
+       readl(ioaddr + ChipConfig);
+       udelay(1);
+
+       /* 2) reset the external phy: */
+       /* resetting the external PHY has been known to cause a hub supplying
+        * power over Ethernet to kill the power.  We don't want to kill
+        * power to this computer, so we avoid resetting the phy.
+        */
+
+       /* 3) reinit the phy fixup, it got lost during power down. */
+       move_int_phy(dev, np->phy_addr_external);
+       init_phy_fixup(dev);
+
+       return 1;
+}
+
+static int switch_port_internal(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+       int i;
+       u32 cfg;
+       u16 bmcr;
+
+       cfg = readl(ioaddr + ChipConfig);
+       if (!(cfg &CfgExtPhy))
+               return 0;
+
+       if (netif_msg_link(np)) {
+               printk(KERN_INFO "%s: switching to internal transceiver.\n",
+                               dev->name);
+       }
+       /* 1) switch back to internal phy: */
+       cfg = cfg & ~(CfgExtPhy | CfgPhyDis);
+       writel(cfg, ioaddr + ChipConfig);
+       readl(ioaddr + ChipConfig);
+       udelay(1);
+
+       /* 2) reset the internal phy: */
+       bmcr = readw(ioaddr+BasicControl+(MII_BMCR<<2));
+       writel(bmcr | BMCR_RESET, ioaddr+BasicControl+(MII_BMCR<<2));
+       readl(ioaddr + ChipConfig);
+       udelay(10);
+       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+               bmcr = readw(ioaddr+BasicControl+(MII_BMCR<<2));
+               if (!(bmcr & BMCR_RESET))
+                       break;
+               udelay(10);
+       }
+       if (i==NATSEMI_HW_TIMEOUT && netif_msg_link(np)) {
+               printk(KERN_INFO
+                       "%s: phy reset did not complete in %d usec.\n",
+                       dev->name, i*10);
+       }
+       /* 3) reinit the phy fixup, it got lost during power down. */
+       init_phy_fixup(dev);
+
+       return 1;
 }
 
-static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 data)
+/* Scan for a PHY on the external mii bus.
+ * There are two tricky points:
+ * - Do not scan while the internal phy is enabled. The internal phy will
+ *   crash: e.g. reads from the DSPCFG register will return odd values and
+ *   the nasty random phy reset code will reset the nic every few seconds.
+ * - The internal phy must be moved around, an external phy could
+ *   have the same address as the internal phy.
+ */
+static int find_mii(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
-       if (phy_id == 1 && reg < 32) {
-               writew(data, dev->base_addr+BasicControl+(reg<<2));
-               switch (reg) {
-                       case MII_ADVERTISE: np->advertising = data; break;
+       struct netdev_private *np = netdev_priv(dev);
+       int tmp;
+       int i;
+       int did_switch;
+
+       /* Switch to external phy */
+       did_switch = switch_port_external(dev);
+
+       /* Scan the possible phy addresses:
+        *
+        * PHY address 0 means that the phy is in isolate mode. Not yet
+        * supported due to lack of test hardware. User space should
+        * handle it through ethtool.
+        */
+       for (i = 1; i <= 31; i++) {
+               move_int_phy(dev, i);
+               tmp = miiport_read(dev, i, MII_BMSR);
+               if (tmp != 0xffff && tmp != 0x0000) {
+                       /* found something! */
+                       np->mii = (mdio_read(dev, MII_PHYSID1) << 16)
+                                       + mdio_read(dev, MII_PHYSID2);
+                       if (netif_msg_probe(np)) {
+                               printk(KERN_INFO "natsemi %s: found external phy %08x at address %d.\n",
+                                               pci_name(np->pci_dev), np->mii, i);
+                       }
+                       break;
                }
        }
+       /* And switch back to internal phy: */
+       if (did_switch)
+               switch_port_internal(dev);
+       return i;
 }
 
 /* CFG bits [13:16] [18:23] */
@@ -975,7 +1318,8 @@ static void natsemi_reset(struct net_device *dev)
        u32 rfcr;
        u16 pmatch[3];
        u16 sopass[3];
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
 
        /*
         * Resetting the chip causes some registers to be lost.
@@ -986,26 +1330,26 @@ static void natsemi_reset(struct net_device *dev)
         */
 
        /* CFG */
-       cfg = readl(dev->base_addr + ChipConfig) & CFG_RESET_SAVE;
+       cfg = readl(ioaddr + ChipConfig) & CFG_RESET_SAVE;
        /* WCSR */
-       wcsr = readl(dev->base_addr + WOLCmd) & WCSR_RESET_SAVE;
+       wcsr = readl(ioaddr + WOLCmd) & WCSR_RESET_SAVE;
        /* RFCR */
-       rfcr = readl(dev->base_addr + RxFilterAddr) & RFCR_RESET_SAVE;
+       rfcr = readl(ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
        /* PMATCH */
        for (i = 0; i < 3; i++) {
-               writel(i*2, dev->base_addr + RxFilterAddr);
-               pmatch[i] = readw(dev->base_addr + RxFilterData);
+               writel(i*2, ioaddr + RxFilterAddr);
+               pmatch[i] = readw(ioaddr + RxFilterData);
        }
        /* SOPAS */
        for (i = 0; i < 3; i++) {
-               writel(0xa+(i*2), dev->base_addr + RxFilterAddr);
-               sopass[i] = readw(dev->base_addr + RxFilterData);
+               writel(0xa+(i*2), ioaddr + RxFilterAddr);
+               sopass[i] = readw(ioaddr + RxFilterData);
        }
 
        /* now whack the chip */
-       writel(ChipReset, dev->base_addr + ChipCmd);
+       writel(ChipReset, ioaddr + ChipCmd);
        for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
-               if (!(readl(dev->base_addr + ChipCmd) & ChipReset))
+               if (!(readl(ioaddr + ChipCmd) & ChipReset))
                        break;
                udelay(5);
        }
@@ -1018,50 +1362,81 @@ static void natsemi_reset(struct net_device *dev)
        }
 
        /* restore CFG */
-       cfg |= readl(dev->base_addr + ChipConfig) & ~CFG_RESET_SAVE;
-       writel(cfg, dev->base_addr + ChipConfig);
+       cfg |= readl(ioaddr + ChipConfig) & ~CFG_RESET_SAVE;
+       /* turn on external phy if it was selected */
+       if (dev->if_port == PORT_TP)
+               cfg &= ~(CfgExtPhy | CfgPhyDis);
+       else
+               cfg |= (CfgExtPhy | CfgPhyDis);
+       writel(cfg, ioaddr + ChipConfig);
        /* restore WCSR */
-       wcsr |= readl(dev->base_addr + WOLCmd) & ~WCSR_RESET_SAVE;
-       writel(wcsr, dev->base_addr + WOLCmd);
+       wcsr |= readl(ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
+       writel(wcsr, ioaddr + WOLCmd);
        /* read RFCR */
-       rfcr |= readl(dev->base_addr + RxFilterAddr) & ~RFCR_RESET_SAVE;
+       rfcr |= readl(ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
        /* restore PMATCH */
        for (i = 0; i < 3; i++) {
-               writel(i*2, dev->base_addr + RxFilterAddr);
-               writew(pmatch[i], dev->base_addr + RxFilterData);
+               writel(i*2, ioaddr + RxFilterAddr);
+               writew(pmatch[i], ioaddr + RxFilterData);
        }
        for (i = 0; i < 3; i++) {
-               writel(0xa+(i*2), dev->base_addr + RxFilterAddr);
-               writew(sopass[i], dev->base_addr + RxFilterData);
+               writel(0xa+(i*2), ioaddr + RxFilterAddr);
+               writew(sopass[i], ioaddr + RxFilterData);
        }
        /* restore RFCR */
-       writel(rfcr, dev->base_addr + RxFilterAddr);
+       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 = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
        int i;
 
-       writel(EepromReload, dev->base_addr + PCIBusCfg);
+       writel(EepromReload, ioaddr + PCIBusCfg);
        for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
                udelay(50);
-               if (!(readl(dev->base_addr + PCIBusCfg) & EepromReload))
+               if (!(readl(ioaddr + PCIBusCfg) & EepromReload))
                        break;
        }
        if (i==NATSEMI_HW_TIMEOUT) {
-               printk(KERN_WARNING "%s: EEPROM did not reload in %d usec.\n",
-                       dev->name, i*50);
+               printk(KERN_WARNING "natsemi %s: EEPROM did not reload in %d usec.\n",
+                       pci_name(np->pci_dev), i*50);
        } else if (netif_msg_hw(np)) {
-               printk(KERN_DEBUG "%s: EEPROM reloaded in %d usec.\n",
-                       dev->name, i*50);
+               printk(KERN_DEBUG "natsemi %s: EEPROM reloaded in %d usec.\n",
+                       pci_name(np->pci_dev), i*50);
        }
 }
 
 static void natsemi_stop_rxtx(struct net_device *dev)
 {
-       long ioaddr = dev->base_addr;
-       struct netdev_private *np = dev->priv;
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       struct netdev_private *np = netdev_priv(dev);
        int i;
 
        writel(RxOff | TxOff, ioaddr + ChipCmd);
@@ -1081,14 +1456,14 @@ static void natsemi_stop_rxtx(struct net_device *dev)
 
 static int netdev_open(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
-       long ioaddr = dev->base_addr;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
        int i;
 
        /* Reset the chip, just in case. */
        natsemi_reset(dev);
 
-       i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev);
+       i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev);
        if (i) return i;
 
        if (netif_msg_ifup(np))
@@ -1130,7 +1505,11 @@ static int netdev_open(struct net_device *dev)
 
 static void do_cable_magic(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+
+       if (dev->if_port != PORT_TP)
+               return;
 
        if (np->srr >= SRR_DP83816_A5)
                return;
@@ -1141,57 +1520,68 @@ static void do_cable_magic(struct net_device *dev)
         * activity LED while idle.  This process is based on instructions
         * from engineers at National.
         */
-       if (readl(dev->base_addr + ChipConfig) & CfgSpeed100) {
+       if (readl(ioaddr + ChipConfig) & CfgSpeed100) {
                u16 data;
 
-               writew(1, dev->base_addr + PGSEL);
+               writew(1, ioaddr + PGSEL);
                /*
                 * coefficient visibility should already be enabled via
                 * DSPCFG | 0x1000
                 */
-               data = readw(dev->base_addr + TSTDAT) & 0xff;
+               data = readw(ioaddr + TSTDAT) & 0xff;
                /*
                 * the value must be negative, and within certain values
                 * (these values all come from National)
                 */
                if (!(data & 0x80) || ((data >= 0xd8) && (data <= 0xff))) {
-                       struct netdev_private *np = dev->priv;
+                       struct netdev_private *np = netdev_priv(dev);
 
                        /* the bug has been triggered - fix the coefficient */
-                       writew(TSTDAT_FIXED, dev->base_addr + TSTDAT);
+                       writew(TSTDAT_FIXED, ioaddr + TSTDAT);
                        /* lock the value */
-                       data = readw(dev->base_addr + DSPCFG);
+                       data = readw(ioaddr + DSPCFG);
                        np->dspcfg = data | DSPCFG_LOCK;
-                       writew(np->dspcfg, dev->base_addr + DSPCFG);
+                       writew(np->dspcfg, ioaddr + DSPCFG);
                }
-               writew(0, dev->base_addr + PGSEL);
+               writew(0, ioaddr + PGSEL);
        }
 }
 
 static void undo_cable_magic(struct net_device *dev)
 {
        u16 data;
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       if (dev->if_port != PORT_TP)
+               return;
 
        if (np->srr >= SRR_DP83816_A5)
                return;
 
-       writew(1, dev->base_addr + PGSEL);
+       writew(1, ioaddr + PGSEL);
        /* make sure the lock bit is clear */
-       data = readw(dev->base_addr + DSPCFG);
+       data = readw(ioaddr + DSPCFG);
        np->dspcfg = data & ~DSPCFG_LOCK;
-       writew(np->dspcfg, dev->base_addr + DSPCFG);
-       writew(0, dev->base_addr + PGSEL);
+       writew(np->dspcfg, ioaddr + DSPCFG);
+       writew(0, ioaddr + PGSEL);
 }
 
 static void check_link(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
-       long ioaddr = dev->base_addr;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
        int duplex;
-       int chipcfg = readl(ioaddr + ChipConfig);
+       u16 bmsr;
+
+       /* The link status field is latched: it remains low after a temporary
+        * link failure until it's read. We need the current link status,
+        * thus read twice.
+        */
+       mdio_read(dev, MII_BMSR);
+       bmsr = mdio_read(dev, MII_BMSR);
 
-       if (!(chipcfg & CfgLink)) {
+       if (!(bmsr & BMSR_LSTATUS)) {
                if (netif_carrier_ok(dev)) {
                        if (netif_msg_link(np))
                                printk(KERN_NOTICE "%s: link down.\n",
@@ -1208,7 +1598,16 @@ static void check_link(struct net_device *dev)
                do_cable_magic(dev);
        }
 
-       duplex = np->full_duplex || (chipcfg & CfgFullDuplex ? 1 : 0);
+       duplex = np->full_duplex;
+       if (!duplex) {
+               if (bmsr & BMSR_ANEGCOMPLETE) {
+                       int tmp = mii_nway_result(
+                               np->advertising & mdio_read(dev, MII_LPA));
+                       if (tmp == LPA_100FULL || tmp == LPA_10FULL)
+                               duplex = 1;
+               } else if (mdio_read(dev, MII_BMCR) & BMCR_FULLDPLX)
+                       duplex = 1;
+       }
 
        /* if duplex is set then bit 28 must be set, too */
        if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
@@ -1231,42 +1630,10 @@ static void check_link(struct net_device *dev)
 
 static void init_registers(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
-       long ioaddr = dev->base_addr;
-       int i;
-
-       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
-               if (readl(dev->base_addr + ChipConfig) & CfgAnegDone)
-                       break;
-               udelay(10);
-       }
-       if (i==NATSEMI_HW_TIMEOUT && netif_msg_link(np)) {
-               printk(KERN_INFO
-                       "%s: autonegotiation did not complete in %d usec.\n",
-                       dev->name, i*10);
-       }
-
-       /* On page 78 of the spec, they recommend some settings for "optimum
-          performance" to be done in sequence.  These settings optimize some
-          of the 100Mbit autodetection circuitry.  They say we only want to
-          do this for rev C of the chip, but engineers at NSC (Bradley
-          Kennedy) recommends always setting them.  If you don't, you get
-          errors on some autonegotiations that make the device unusable.
-       */
-       writew(1, ioaddr + PGSEL);
-       writew(PMDCSR_VAL, ioaddr + PMDCSR);
-       writew(TSTDAT_VAL, ioaddr + TSTDAT);
-       writew(DSPCFG_VAL, ioaddr + DSPCFG);
-       writew(SDCFG_VAL, ioaddr + SDCFG);
-       writew(0, ioaddr + PGSEL);
-       np->dspcfg = DSPCFG_VAL;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
 
-       /* Enable PHY Specific event based interrupts.  Link state change
-          and Auto-Negotiation Completion are among the affected.
-          Read the intr status to clear it (needed for wake events).
-       */
-       readw(ioaddr + MIntrStatus);
-       writew(MICRIntEn, ioaddr + MIntrCtrl);
+       init_phy_fixup(dev);
 
        /* clear any interrupts that are pending, such as wake events */
        readl(ioaddr + IntrStatus);
@@ -1289,13 +1656,18 @@ static void init_registers(struct net_device *dev)
         * ECRETRY=1
         * ATP=1
         */
-       np->tx_config = TxAutoPad | TxCollRetry | TxMxdma_256 | (0x1002);
+       np->tx_config = TxAutoPad | TxCollRetry | TxMxdma_256 |
+                               TX_FLTH_VAL | TX_DRTH_VAL_START;
        writel(np->tx_config, ioaddr + TxConfig);
 
        /* DRTH 0x10: start copying to memory if 128 bytes are in the fifo
         * MXDMA 0: up to 256 byte bursts
         */
-       np->rx_config = RxMxdma_256 | 0x20;
+       np->rx_config = RxMxdma_256 | RX_DRTH_VAL;
+       /* if receive ring now has bigger buffers than normal, enable jumbo */
+       if (np->rx_buf_sz > NATSEMI_LONGPKT)
+               np->rx_config |= RxAcceptLong;
+
        writel(np->rx_config, ioaddr + RxConfig);
 
        /* Disable PME:
@@ -1337,10 +1709,9 @@ static void init_registers(struct net_device *dev)
 static void netdev_timer(unsigned long data)
 {
        struct net_device *dev = (struct net_device *)data;
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
        int next_tick = 5*HZ;
-       long ioaddr = dev->base_addr;
-       u16 dspcfg;
 
        if (netif_msg_timer(np)) {
                /* DO NOT read the IntrStatus register,
@@ -1350,33 +1721,40 @@ static void netdev_timer(unsigned long data)
                        dev->name);
        }
 
-       spin_lock_irq(&np->lock);
+       if (dev->if_port == PORT_TP) {
+               u16 dspcfg;
 
-       /* check for a nasty random phy-reset - use dspcfg as a flag */
-       writew(1, ioaddr+PGSEL);
-       dspcfg = readw(ioaddr+DSPCFG);
-       writew(0, ioaddr+PGSEL);
-       if (dspcfg != np->dspcfg) {
-               if (!netif_queue_stopped(dev)) {
-                       spin_unlock_irq(&np->lock);
-                       if (netif_msg_hw(np))
-                               printk(KERN_NOTICE "%s: possible phy reset: "
-                                       "re-initializing\n", dev->name);
-                       disable_irq(dev->irq);
-                       spin_lock_irq(&np->lock);
-                       natsemi_stop_rxtx(dev);
-                       dump_ring(dev);
-                       reinit_ring(dev);
-                       init_registers(dev);
-                       spin_unlock_irq(&np->lock);
-                       enable_irq(dev->irq);
+               spin_lock_irq(&np->lock);
+               /* check for a nasty random phy-reset - use dspcfg as a flag */
+               writew(1, ioaddr+PGSEL);
+               dspcfg = readw(ioaddr+DSPCFG);
+               writew(0, ioaddr+PGSEL);
+               if (dspcfg != np->dspcfg) {
+                       if (!netif_queue_stopped(dev)) {
+                               spin_unlock_irq(&np->lock);
+                               if (netif_msg_hw(np))
+                                       printk(KERN_NOTICE "%s: possible phy reset: "
+                                               "re-initializing\n", dev->name);
+                               disable_irq(dev->irq);
+                               spin_lock_irq(&np->lock);
+                               natsemi_stop_rxtx(dev);
+                               dump_ring(dev);
+                               reinit_ring(dev);
+                               init_registers(dev);
+                               spin_unlock_irq(&np->lock);
+                               enable_irq(dev->irq);
+                       } else {
+                               /* hurry back */
+                               next_tick = HZ;
+                               spin_unlock_irq(&np->lock);
+                       }
                } else {
-                       /* hurry back */
-                       next_tick = HZ;
+                       /* init_registers() calls check_link() for the above case */
+                       check_link(dev);
                        spin_unlock_irq(&np->lock);
                }
        } else {
-               /* init_registers() calls check_link() for the above case */
+               spin_lock_irq(&np->lock);
                check_link(dev);
                spin_unlock_irq(&np->lock);
        }
@@ -1386,7 +1764,7 @@ static void netdev_timer(unsigned long data)
                refill_rx(dev);
                enable_irq(dev->irq);
                if (!np->oom) {
-                       writel(RxOn, dev->base_addr + ChipCmd);
+                       writel(RxOn, ioaddr + ChipCmd);
                } else {
                        next_tick = 1;
                }
@@ -1396,7 +1774,7 @@ static void netdev_timer(unsigned long data)
 
 static void dump_ring(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
 
        if (netif_msg_pktdata(np)) {
                int i;
@@ -1419,8 +1797,8 @@ static void dump_ring(struct net_device *dev)
 
 static void tx_timeout(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
-       long ioaddr = dev->base_addr;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
 
        disable_irq(dev->irq);
        spin_lock_irq(&np->lock);
@@ -1450,7 +1828,7 @@ static void tx_timeout(struct net_device *dev)
 
 static int alloc_ring(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
        np->rx_ring = pci_alloc_consistent(np->pci_dev,
                sizeof(struct netdev_desc) * (RX_RING_SIZE+TX_RING_SIZE),
                &np->ring_dma);
@@ -1462,21 +1840,21 @@ static int alloc_ring(struct net_device *dev)
 
 static void refill_rx(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
 
        /* Refill the Rx ring buffers. */
        for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
                struct sk_buff *skb;
                int entry = np->dirty_rx % RX_RING_SIZE;
                if (np->rx_skbuff[entry] == NULL) {
-                       unsigned int buflen = np->rx_buf_sz + RX_OFFSET;
+                       unsigned int buflen = np->rx_buf_sz+NATSEMI_PADDING;
                        skb = dev_alloc_skb(buflen);
                        np->rx_skbuff[entry] = skb;
                        if (skb == NULL)
                                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);
@@ -1488,10 +1866,19 @@ static void refill_rx(struct net_device *dev)
        }
 }
 
+static void set_bufsize(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       if (dev->mtu <= ETH_DATA_LEN)
+               np->rx_buf_sz = ETH_DATA_LEN + NATSEMI_HEADERS;
+       else
+               np->rx_buf_sz = dev->mtu + NATSEMI_HEADERS;
+}
+
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 static void init_ring(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
        int i;
 
        /* 1) TX ring */
@@ -1507,8 +1894,9 @@ static void init_ring(struct net_device *dev)
        /* 2) RX ring */
        np->dirty_rx = 0;
        np->cur_rx = RX_RING_SIZE;
-       np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
        np->oom = 0;
+       set_bufsize(dev);
+
        np->rx_head_desc = &np->rx_ring[0];
 
        /* Please be carefull before changing this loop - at least gcc-2.95.1
@@ -1528,7 +1916,7 @@ static void init_ring(struct net_device *dev)
 
 static void drain_tx(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
        int i;
 
        for (i = 0; i < TX_RING_SIZE; i++) {
@@ -1543,10 +1931,10 @@ static void drain_tx(struct net_device *dev)
        }
 }
 
-static void drain_ring(struct net_device *dev)
+static void drain_rx(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
-       unsigned int buflen = np->rx_buf_sz + RX_OFFSET;
+       struct netdev_private *np = netdev_priv(dev);
+       unsigned int buflen = np->rx_buf_sz;
        int i;
 
        /* Free all the skbuffs in the Rx queue. */
@@ -1561,28 +1949,27 @@ static void drain_ring(struct net_device *dev)
                }
                np->rx_skbuff[i] = NULL;
        }
+}
+
+static void drain_ring(struct net_device *dev)
+{
+       drain_rx(dev);
        drain_tx(dev);
 }
 
 static void free_ring(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
        pci_free_consistent(np->pci_dev,
                sizeof(struct netdev_desc) * (RX_RING_SIZE+TX_RING_SIZE),
                np->rx_ring, np->ring_dma);
 }
 
-static void reinit_ring(struct net_device *dev)
+static void reinit_rx(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
        int i;
 
-       /* drain TX ring */
-       drain_tx(dev);
-       np->dirty_tx = np->cur_tx = 0;
-       for (i=0;i<TX_RING_SIZE;i++)
-               np->tx_ring[i].cmd_status = 0;
-
        /* RX Ring */
        np->dirty_rx = 0;
        np->cur_rx = RX_RING_SIZE;
@@ -1594,9 +1981,24 @@ static void reinit_ring(struct net_device *dev)
        refill_rx(dev);
 }
 
+static void reinit_ring(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       int i;
+
+       /* drain TX ring */
+       drain_tx(dev);
+       np->dirty_tx = np->cur_tx = 0;
+       for (i=0;i<TX_RING_SIZE;i++)
+               np->tx_ring[i].cmd_status = 0;
+
+       reinit_rx(dev);
+}
+
 static int start_tx(struct sk_buff *skb, struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
        unsigned entry;
 
        /* Note: Ordering is important here, set the field with the
@@ -1625,7 +2027,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
                                netif_stop_queue(dev);
                }
                /* Wake the potentially-idle transmit channel. */
-               writel(TxOn, dev->base_addr + ChipCmd);
+               writel(TxOn, ioaddr + ChipCmd);
        } else {
                dev_kfree_skb_irq(skb);
                np->stats.tx_dropped++;
@@ -1643,7 +2045,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
 
 static void netdev_tx_done(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
 
        for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
                int entry = np->dirty_tx % TX_RING_SIZE;
@@ -1684,83 +2086,116 @@ 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. */
-static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
+/* 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 net_device *dev = dev_instance;
-       struct netdev_private *np = dev->priv;
-       long ioaddr = dev->base_addr;
-       int boguscnt = max_interrupt_work;
-       unsigned int handled = 0;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
 
        if (np->hands_off)
                return IRQ_NONE;
-       do {
-               /* Reading automatically acknowledges all int sources. */
-               u32 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));
+       /* Reading automatically acknowledges. */
+       np->intr_status = readl(ioaddr + IntrStatus);
 
-               if (intr_status == 0)
-                       break;
-               handled = 1;
+       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 &
-                  (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
-                   IntrRxErr | IntrRxOverrun)) {
-                       netdev_rx(dev);
-               }
+       if (!np->intr_status)
+               return IRQ_NONE;
+
+       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);
+
+       int work_to_do = min(*budget, dev->quota);
+       int work_done = 0;
 
-               if (intr_status &
-                  (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) {
+       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);
 
-       if (netif_msg_intr(np))
-               printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name);
+               *budget -= work_done;
+               dev->quota -= work_done;
 
-       return IRQ_RETVAL(handled);
+               if (work_done >= work_to_do)
+                       return 1;
+
+               np->intr_status = readl(ioaddr + IntrStatus);
+       } while (np->intr_status);
+
+       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);
+
+       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 = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
        int entry = np->cur_rx % RX_RING_SIZE;
        int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
        s32 desc_status = le32_to_cpu(np->rx_head_desc->cmd_status);
-       unsigned int buflen = np->rx_buf_sz + RX_OFFSET;
+       unsigned int buflen = np->rx_buf_sz;
+       void __iomem * ioaddr = ns_ioaddr(dev);
 
        /* If the driver owns the next entry it's a new packet. Send it up. */
        while (desc_status < 0) { /* e.g. & DescOwn */
+               int pkt_len;
                if (netif_msg_rx_status(np))
                        printk(KERN_DEBUG
                                "  netdev_rx() entry %d status was %#08x.\n",
                                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) {
                                if (netif_msg_rx_err(np))
@@ -1771,6 +2206,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++;
@@ -1783,10 +2235,14 @@ static void netdev_rx(struct net_device *dev)
                                if (desc_status & DescRxCRC)
                                        np->stats.rx_crc_errors++;
                        }
+               } else if (pkt_len > np->rx_buf_sz) {
+                       /* if this is the tail of a double buffer
+                        * packet, we've already counted the error
+                        * on the first part.  Ignore the second half.
+                        */
                } else {
                        struct sk_buff *skb;
                        /* Omit CRC size. */
-                       int pkt_len = (desc_status & DescSizeMask) - 4;
                        /* Check if the packet is long enough to accept
                         * without copying to a minimally-sized skbuff. */
                        if (pkt_len < rx_copybreak
@@ -1798,14 +2254,9 @@ static void netdev_rx(struct net_device *dev)
                                        np->rx_dma[entry],
                                        buflen,
                                        PCI_DMA_FROMDEVICE);
-#if HAS_IP_COPYSUM
                                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);
-#else
-                               memcpy(skb_put(skb, pkt_len),
-                                       np->rx_skbuff[entry]->tail, pkt_len);
-#endif
                                pci_dma_sync_single_for_device(np->pci_dev,
                                        np->rx_dma[entry],
                                        buflen,
@@ -1817,7 +2268,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;
@@ -1832,24 +2283,23 @@ static void netdev_rx(struct net_device *dev)
        if (np->oom)
                mod_timer(&np->timer, jiffies + 1);
        else
-               writel(RxOn, dev->base_addr + ChipCmd);
+               writel(RxOn, ioaddr + ChipCmd);
 }
 
 static void netdev_error(struct net_device *dev, int intr_status)
 {
-       struct netdev_private *np = dev->priv;
-       long ioaddr = dev->base_addr;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
 
        spin_lock(&np->lock);
        if (intr_status & LinkChange) {
-               u16 adv = mdio_read(dev, 1, MII_ADVERTISE);
-               u16 lpa = mdio_read(dev, 1, MII_LPA);
-               if (mdio_read(dev, 1, MII_BMCR) & BMCR_ANENABLE
+               u16 lpa = mdio_read(dev, MII_LPA);
+               if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE
                 && netif_msg_link(np)) {
                        printk(KERN_INFO
                                "%s: Autonegotiation advertising"
                                " %#04x  partner %#04x.\n", dev->name,
-                               adv, lpa);
+                               np->advertising, lpa);
                }
 
                /* read MII int status to clear the flag */
@@ -1860,12 +2310,18 @@ static void netdev_error(struct net_device *dev, int intr_status)
                __get_stats(dev);
        }
        if (intr_status & IntrTxUnderrun) {
-               if ((np->tx_config & TxDrthMask) < 62)
-                       np->tx_config += 2;
-               if (netif_msg_tx_err(np))
-                       printk(KERN_NOTICE
-                               "%s: increased Tx threshold, txcfg %#08x.\n",
-                               dev->name, np->tx_config);
+               if ((np->tx_config & TxDrthMask) < TX_DRTH_VAL_LIMIT) {
+                       np->tx_config += TX_DRTH_VAL_INC;
+                       if (netif_msg_tx_err(np))
+                               printk(KERN_NOTICE
+                                       "%s: increased tx threshold, txcfg %#08x.\n",
+                                       dev->name, np->tx_config);
+               } else {
+                       if (netif_msg_tx_err(np))
+                               printk(KERN_NOTICE
+                                       "%s: tx underrun with maximum tx threshold, txcfg %#08x.\n",
+                                       dev->name, np->tx_config);
+               }
                writel(np->tx_config, ioaddr + TxConfig);
        }
        if (intr_status & WOLPkt && netif_msg_wol(np)) {
@@ -1892,8 +2348,8 @@ static void netdev_error(struct net_device *dev, int intr_status)
 
 static void __get_stats(struct net_device *dev)
 {
-       long ioaddr = dev->base_addr;
-       struct netdev_private *np = dev->priv;
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       struct netdev_private *np = netdev_priv(dev);
 
        /* The chip only need report frame silently dropped. */
        np->stats.rx_crc_errors += readl(ioaddr + RxCRCErrs);
@@ -1902,7 +2358,7 @@ static void __get_stats(struct net_device *dev)
 
 static struct net_device_stats *get_stats(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
 
        /* The chip only need report frame silently dropped. */
        spin_lock_irq(&np->lock);
@@ -1913,18 +2369,24 @@ static struct net_device_stats *get_stats(struct net_device *dev)
        return &np->stats;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void natsemi_poll_controller(struct net_device *dev)
+{
+       disable_irq(dev->irq);
+       intr_handler(dev->irq, dev);
+       enable_irq(dev->irq);
+}
+#endif
+
 #define HASH_TABLE     0x200
 static void __set_rx_mode(struct net_device *dev)
 {
-       long ioaddr = dev->base_addr;
-       struct netdev_private *np = dev->priv;
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       struct netdev_private *np = netdev_priv(dev);
        u8 mc_filter[64]; /* Multicast hash filter */
        u32 rx_mode;
 
        if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
-               /* Unconditionally log net taps. */
-               printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n",
-                       dev->name);
                rx_mode = RxFilterEnable | AcceptBroadcast
                        | AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
        } else if ((dev->mc_count > multicast_filter_limit)
@@ -1943,199 +2405,195 @@ 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);
        np->cur_rx_mode = rx_mode;
 }
 
+static int natsemi_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if (new_mtu < 64 || new_mtu > NATSEMI_RX_LIMIT-NATSEMI_HEADERS)
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+
+       /* synchronized against open : rtnl_lock() held by caller */
+       if (netif_running(dev)) {
+               struct netdev_private *np = netdev_priv(dev);
+               void __iomem * ioaddr = ns_ioaddr(dev);
+
+               disable_irq(dev->irq);
+               spin_lock(&np->lock);
+               /* stop engines */
+               natsemi_stop_rxtx(dev);
+               /* drain rx queue */
+               drain_rx(dev);
+               /* change buffers */
+               set_bufsize(dev);
+               reinit_rx(dev);
+               writel(np->ring_dma, ioaddr + RxRingPtr);
+               /* restart engines */
+               writel(RxOn | TxOn, ioaddr + ChipCmd);
+               spin_unlock(&np->lock);
+               enable_irq(dev->irq);
+       }
+       return 0;
+}
+
 static void set_rx_mode(struct net_device *dev)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
        spin_lock_irq(&np->lock);
        if (!np->hands_off)
                __set_rx_mode(dev);
        spin_unlock_irq(&np->lock);
 }
 
-static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
+static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
-       struct netdev_private *np = dev->priv;
-       u32 cmd;
+       struct netdev_private *np = netdev_priv(dev);
+       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 *)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, 1, MII_BMCR);
-               if (tmp & BMCR_ANENABLE) {
-                       tmp |= (BMCR_ANRESTART);
-                       mdio_write(dev, 1, 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, 1, MII_BMSR);
-               edata.data = (mdio_read(dev, 1, 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 const 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 = dev->priv;
-       u32 data = readl(dev->base_addr + WOLCmd) & ~WakeOptsSummary;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       u32 data = readl(ioaddr + WOLCmd) & ~WakeOptsSummary;
 
        /* translate to bitmasks this chip understands */
        if (newval & WAKE_PHY)
@@ -2156,15 +2614,16 @@ static int netdev_set_wol(struct net_device *dev, u32 newval)
                }
        }
 
-       writel(data, dev->base_addr + WOLCmd);
+       writel(data, ioaddr + WOLCmd);
 
        return 0;
 }
 
 static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur)
 {
-       struct netdev_private *np = dev->priv;
-       u32 regval = readl(dev->base_addr + WOLCmd);
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       u32 regval = readl(ioaddr + WOLCmd);
 
        *supported = (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST
                        | WAKE_ARP | WAKE_MAGIC);
@@ -2198,7 +2657,8 @@ static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur)
 
 static int netdev_set_sopass(struct net_device *dev, u8 *newval)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
        u16 *sval = (u16 *)newval;
        u32 addr;
 
@@ -2207,29 +2667,30 @@ static int netdev_set_sopass(struct net_device *dev, u8 *newval)
        }
 
        /* enable writing to these registers by disabling the RX filter */
-       addr = readl(dev->base_addr + RxFilterAddr) & ~RFCRAddressMask;
+       addr = readl(ioaddr + RxFilterAddr) & ~RFCRAddressMask;
        addr &= ~RxFilterEnable;
-       writel(addr, dev->base_addr + RxFilterAddr);
+       writel(addr, ioaddr + RxFilterAddr);
 
        /* write the three words to (undocumented) RFCR vals 0xa, 0xc, 0xe */
-       writel(addr | 0xa, dev->base_addr + RxFilterAddr);
-       writew(sval[0], dev->base_addr + RxFilterData);
+       writel(addr | 0xa, ioaddr + RxFilterAddr);
+       writew(sval[0], ioaddr + RxFilterData);
 
-       writel(addr | 0xc, dev->base_addr + RxFilterAddr);
-       writew(sval[1], dev->base_addr + RxFilterData);
+       writel(addr | 0xc, ioaddr + RxFilterAddr);
+       writew(sval[1], ioaddr + RxFilterData);
 
-       writel(addr | 0xe, dev->base_addr + RxFilterAddr);
-       writew(sval[2], dev->base_addr + RxFilterData);
+       writel(addr | 0xe, ioaddr + RxFilterAddr);
+       writew(sval[2], ioaddr + RxFilterData);
 
        /* re-enable the RX filter */
-       writel(addr | RxFilterEnable, dev->base_addr + RxFilterAddr);
+       writel(addr | RxFilterEnable, ioaddr + RxFilterAddr);
 
        return 0;
 }
 
 static int netdev_get_sopass(struct net_device *dev, u8 *data)
 {
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
        u16 *sval = (u16 *)data;
        u32 addr;
 
@@ -2239,74 +2700,93 @@ static int netdev_get_sopass(struct net_device *dev, u8 *data)
        }
 
        /* read the three words from (undocumented) RFCR vals 0xa, 0xc, 0xe */
-       addr = readl(dev->base_addr + RxFilterAddr) & ~RFCRAddressMask;
+       addr = readl(ioaddr + RxFilterAddr) & ~RFCRAddressMask;
 
-       writel(addr | 0xa, dev->base_addr + RxFilterAddr);
-       sval[0] = readw(dev->base_addr + RxFilterData);
+       writel(addr | 0xa, ioaddr + RxFilterAddr);
+       sval[0] = readw(ioaddr + RxFilterData);
 
-       writel(addr | 0xc, dev->base_addr + RxFilterAddr);
-       sval[1] = readw(dev->base_addr + RxFilterData);
+       writel(addr | 0xc, ioaddr + RxFilterAddr);
+       sval[1] = readw(ioaddr + RxFilterData);
 
-       writel(addr | 0xe, dev->base_addr + RxFilterAddr);
-       sval[2] = readw(dev->base_addr + RxFilterData);
+       writel(addr | 0xe, ioaddr + RxFilterAddr);
+       sval[2] = readw(ioaddr + RxFilterData);
 
-       writel(addr, dev->base_addr + RxFilterAddr);
+       writel(addr, ioaddr + RxFilterAddr);
 
        return 0;
 }
 
 static int netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
 {
+       struct netdev_private *np = netdev_priv(dev);
        u32 tmp;
 
-       ecmd->supported =
-               (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
-               SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
-               SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
-
-       /* only supports twisted-pair or MII */
-       tmp = readl(dev->base_addr + ChipConfig);
-       if (tmp & CfgExtPhy)
-               ecmd->port = PORT_MII;
-       else
-               ecmd->port = PORT_TP;
-
-       /* only supports internal transceiver */
-       ecmd->transceiver = XCVR_INTERNAL;
-
-       /* not sure what this is for */
-       ecmd->phy_address = readw(dev->base_addr + PhyCtrl) & PhyAddrMask;
-
-       ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
-       tmp = mdio_read(dev, 1, MII_ADVERTISE);
-       if (tmp & ADVERTISE_10HALF)
+       ecmd->port        = dev->if_port;
+       ecmd->speed       = np->speed;
+       ecmd->duplex      = np->duplex;
+       ecmd->autoneg     = np->autoneg;
+       ecmd->advertising = 0;
+       if (np->advertising & ADVERTISE_10HALF)
                ecmd->advertising |= ADVERTISED_10baseT_Half;
-       if (tmp & ADVERTISE_10FULL)
+       if (np->advertising & ADVERTISE_10FULL)
                ecmd->advertising |= ADVERTISED_10baseT_Full;
-       if (tmp & ADVERTISE_100HALF)
+       if (np->advertising & ADVERTISE_100HALF)
                ecmd->advertising |= ADVERTISED_100baseT_Half;
-       if (tmp & ADVERTISE_100FULL)
+       if (np->advertising & ADVERTISE_100FULL)
                ecmd->advertising |= ADVERTISED_100baseT_Full;
+       ecmd->supported   = (SUPPORTED_Autoneg |
+               SUPPORTED_10baseT_Half  | SUPPORTED_10baseT_Full  |
+               SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+               SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE);
+       ecmd->phy_address = np->phy_addr_external;
+       /*
+        * We intentionally report the phy address of the external
+        * phy, even if the internal phy is used. This is necessary
+        * to work around a deficiency of the ethtool interface:
+        * It's only possible to query the settings of the active
+        * port. Therefore
+        * # ethtool -s ethX port mii
+        * actually sends an ioctl to switch to port mii with the
+        * settings that are used for the current active port.
+        * If we would report a different phy address in this
+        * command, then
+        * # ethtool -s ethX port tp;ethtool -s ethX port mii
+        * would unintentionally change the phy address.
+        *
+        * Fortunately the phy address doesn't matter with the
+        * internal phy...
+        */
 
-       tmp = mdio_read(dev, 1, MII_BMCR);
-       if (tmp & BMCR_ANENABLE) {
+       /* set information based on active port type */
+       switch (ecmd->port) {
+       default:
+       case PORT_TP:
+               ecmd->advertising |= ADVERTISED_TP;
+               ecmd->transceiver = XCVR_INTERNAL;
+               break;
+       case PORT_MII:
+               ecmd->advertising |= ADVERTISED_MII;
+               ecmd->transceiver = XCVR_EXTERNAL;
+               break;
+       case PORT_FIBRE:
+               ecmd->advertising |= ADVERTISED_FIBRE;
+               ecmd->transceiver = XCVR_EXTERNAL;
+               break;
+       }
+
+       /* if autonegotiation is on, try to return the active speed/duplex */
+       if (ecmd->autoneg == AUTONEG_ENABLE) {
                ecmd->advertising |= ADVERTISED_Autoneg;
-               ecmd->autoneg = AUTONEG_ENABLE;
-       } else {
-               ecmd->autoneg = AUTONEG_DISABLE;
-       }
-
-       tmp = readl(dev->base_addr + ChipConfig);
-       if (tmp & CfgSpeed100) {
-               ecmd->speed = SPEED_100;
-       } else {
-               ecmd->speed = SPEED_10;
-       }
-
-       if (tmp & CfgFullDuplex) {
-               ecmd->duplex = DUPLEX_FULL;
-       } else {
-               ecmd->duplex = DUPLEX_HALF;
+               tmp = mii_nway_result(
+                       np->advertising & mdio_read(dev, MII_LPA));
+               if (tmp == LPA_100FULL || tmp == LPA_100HALF)
+                       ecmd->speed  = SPEED_100;
+               else
+                       ecmd->speed  = SPEED_10;
+               if (tmp == LPA_100FULL || tmp == LPA_10FULL)
+                       ecmd->duplex = DUPLEX_FULL;
+               else
+                       ecmd->duplex = DUPLEX_HALF;
        }
 
        /* ignore maxtxpkt, maxrxpkt for now */
@@ -2316,39 +2796,75 @@ static int netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
 
 static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
 {
-       struct netdev_private *np = dev->priv;
-       u32 tmp;
+       struct netdev_private *np = netdev_priv(dev);
 
-       if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)
-               return -EINVAL;
-       if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+       if (ecmd->port != PORT_TP && ecmd->port != PORT_MII && ecmd->port != PORT_FIBRE)
                return -EINVAL;
-       if (ecmd->port != PORT_TP && ecmd->port != PORT_MII)
+       if (ecmd->transceiver != XCVR_INTERNAL && ecmd->transceiver != XCVR_EXTERNAL)
                return -EINVAL;
-       if (ecmd->transceiver != XCVR_INTERNAL)
-               return -EINVAL;
-       if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
+       if (ecmd->autoneg == AUTONEG_ENABLE) {
+               if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
+                                         ADVERTISED_10baseT_Full |
+                                         ADVERTISED_100baseT_Half |
+                                         ADVERTISED_100baseT_Full)) == 0) {
+                       return -EINVAL;
+               }
+       } else if (ecmd->autoneg == AUTONEG_DISABLE) {
+               if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)
+                       return -EINVAL;
+               if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+                       return -EINVAL;
+       } else {
                return -EINVAL;
-       /* ignore phy_address, maxtxpkt, maxrxpkt for now */
+       }
+
+       /*
+        * maxtxpkt, maxrxpkt: ignored for now.
+        *
+        * transceiver:
+        * PORT_TP is always XCVR_INTERNAL, PORT_MII and PORT_FIBRE are always
+        * XCVR_EXTERNAL. The implementation thus ignores ecmd->transceiver and
+        * selects based on ecmd->port.
+        *
+        * Actually PORT_FIBRE is nearly identical to PORT_MII: it's for fibre
+        * phys that are connected to the mii bus. It's used to apply fibre
+        * specific updates.
+        */
 
        /* WHEW! now lets bang some bits */
 
-       tmp = mdio_read(dev, 1, MII_BMCR);
-       if (ecmd->autoneg == AUTONEG_ENABLE) {
-               /* turn on autonegotiation */
-               tmp |= BMCR_ANENABLE;
-               np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
+       /* save the parms */
+       dev->if_port          = ecmd->port;
+       np->autoneg           = ecmd->autoneg;
+       np->phy_addr_external = ecmd->phy_address & PhyAddrMask;
+       if (np->autoneg == AUTONEG_ENABLE) {
+               /* advertise only what has been requested */
+               np->advertising &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+               if (ecmd->advertising & ADVERTISED_10baseT_Half)
+                       np->advertising |= ADVERTISE_10HALF;
+               if (ecmd->advertising & ADVERTISED_10baseT_Full)
+                       np->advertising |= ADVERTISE_10FULL;
+               if (ecmd->advertising & ADVERTISED_100baseT_Half)
+                       np->advertising |= ADVERTISE_100HALF;
+               if (ecmd->advertising & ADVERTISED_100baseT_Full)
+                       np->advertising |= ADVERTISE_100FULL;
        } else {
-               /* turn off auto negotiation, set speed and duplexity */
-               tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
-               if (ecmd->speed == SPEED_100)
-                       tmp |= BMCR_SPEED100;
-               if (ecmd->duplex == DUPLEX_FULL)
-                       tmp |= BMCR_FULLDPLX;
-               else
+               np->speed  = ecmd->speed;
+               np->duplex = ecmd->duplex;
+               /* user overriding the initial full duplex parm? */
+               if (np->duplex == DUPLEX_HALF)
                        np->full_duplex = 0;
        }
-       mdio_write(dev, 1, MII_BMCR, tmp);
+
+       /* get the right phy enabled */
+       if (ecmd->port == PORT_TP)
+               switch_port_internal(dev);
+       else
+               switch_port_external(dev);
+
+       /* set parms and see how this affected our link status */
+       init_phy_fixup(dev);
+       check_link(dev);
        return 0;
 }
 
@@ -2358,27 +2874,32 @@ static int netdev_get_regs(struct net_device *dev, u8 *buf)
        int j;
        u32 rfcr;
        u32 *rbuf = (u32 *)buf;
+       void __iomem * ioaddr = ns_ioaddr(dev);
 
-       /* read all of page 0 of registers */
-       for (i = 0; i < NATSEMI_PG0_NREGS; i++) {
-               rbuf[i] = readl(dev->base_addr + i*4);
+       /* read non-mii page 0 of registers */
+       for (i = 0; i < NATSEMI_PG0_NREGS/2; i++) {
+               rbuf[i] = readl(ioaddr + i*4);
        }
 
+       /* read current mii registers */
+       for (i = NATSEMI_PG0_NREGS/2; i < NATSEMI_PG0_NREGS; i++)
+               rbuf[i] = mdio_read(dev, i & 0x1f);
+
        /* read only the 'magic' registers from page 1 */
-       writew(1, dev->base_addr + PGSEL);
-       rbuf[i++] = readw(dev->base_addr + PMDCSR);
-       rbuf[i++] = readw(dev->base_addr + TSTDAT);
-       rbuf[i++] = readw(dev->base_addr + DSPCFG);
-       rbuf[i++] = readw(dev->base_addr + SDCFG);
-       writew(0, dev->base_addr + PGSEL);
+       writew(1, ioaddr + PGSEL);
+       rbuf[i++] = readw(ioaddr + PMDCSR);
+       rbuf[i++] = readw(ioaddr + TSTDAT);
+       rbuf[i++] = readw(ioaddr + DSPCFG);
+       rbuf[i++] = readw(ioaddr + SDCFG);
+       writew(0, ioaddr + PGSEL);
 
        /* read RFCR indexed registers */
-       rfcr = readl(dev->base_addr + RxFilterAddr);
+       rfcr = readl(ioaddr + RxFilterAddr);
        for (j = 0; j < NATSEMI_RFDR_NREGS; j++) {
-               writel(j*2, dev->base_addr + RxFilterAddr);
-               rbuf[i++] = readw(dev->base_addr + RxFilterData);
+               writel(j*2, ioaddr + RxFilterAddr);
+               rbuf[i++] = readw(ioaddr + RxFilterData);
        }
-       writel(rfcr, dev->base_addr + RxFilterAddr);
+       writel(rfcr, ioaddr + RxFilterAddr);
 
        /* the interrupt status is clear-on-read - see if we missed any */
        if (rbuf[4] & rbuf[5]) {
@@ -2403,10 +2924,12 @@ 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++) {
-               ebuf[i] = eeprom_read(dev->base_addr, 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
                 * present it to userland as it is stored. */
@@ -2417,28 +2940,55 @@ static int netdev_get_eeprom(struct net_device *dev, u8 *buf)
 
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-       struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
+       struct mii_ioctl_data *data = if_mii(rq);
+       struct netdev_private *np = netdev_priv(dev);
 
        switch(cmd) {
-       case SIOCETHTOOL:
-               return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
        case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
        case SIOCDEVPRIVATE:            /* for binary compat, remove in 2.5 */
-               data->phy_id = 1;
+               data->phy_id = np->phy_addr_external;
                /* Fall Through */
 
        case SIOCGMIIREG:               /* Read MII PHY register. */
        case SIOCDEVPRIVATE+1:          /* for binary compat, remove in 2.5 */
-               data->val_out = mdio_read(dev, data->phy_id & 0x1f,
-                       data->reg_num & 0x1f);
+               /* The phy_id is not enough to uniquely identify
+                * the intended target. Therefore the command is sent to
+                * the given mii on the current port.
+                */
+               if (dev->if_port == PORT_TP) {
+                       if ((data->phy_id & 0x1f) == np->phy_addr_external)
+                               data->val_out = mdio_read(dev,
+                                                       data->reg_num & 0x1f);
+                       else
+                               data->val_out = 0;
+               } else {
+                       move_int_phy(dev, data->phy_id & 0x1f);
+                       data->val_out = miiport_read(dev, data->phy_id & 0x1f,
+                                                       data->reg_num & 0x1f);
+               }
                return 0;
 
        case SIOCSMIIREG:               /* Write MII PHY register. */
        case SIOCDEVPRIVATE+2:          /* for binary compat, remove in 2.5 */
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
-               mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f,
-                       data->val_in);
+               if (dev->if_port == PORT_TP) {
+                       if ((data->phy_id & 0x1f) == np->phy_addr_external) {
+                               if ((data->reg_num & 0x1f) == MII_ADVERTISE)
+                                       np->advertising = data->val_in;
+                               mdio_write(dev, data->reg_num & 0x1f,
+                                                       data->val_in);
+                       }
+               } else {
+                       if ((data->phy_id & 0x1f) == np->phy_addr_external) {
+                               if ((data->reg_num & 0x1f) == MII_ADVERTISE)
+                                       np->advertising = data->val_in;
+                       }
+                       move_int_phy(dev, data->phy_id & 0x1f);
+                       miiport_write(dev, data->phy_id & 0x1f,
+                                               data->reg_num & 0x1f,
+                                               data->val_in);
+               }
                return 0;
        default:
                return -EOPNOTSUPP;
@@ -2447,8 +2997,8 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 
 static void enable_wol_mode(struct net_device *dev, int enable_intr)
 {
-       long ioaddr = dev->base_addr;
-       struct netdev_private *np = dev->priv;
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       struct netdev_private *np = netdev_priv(dev);
 
        if (netif_msg_wol(np))
                printk(KERN_INFO "%s: remaining active for wake-on-lan\n",
@@ -2480,8 +3030,8 @@ static void enable_wol_mode(struct net_device *dev, int enable_intr)
 
 static int netdev_close(struct net_device *dev)
 {
-       long ioaddr = dev->base_addr;
-       struct netdev_private *np = dev->priv;
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       struct netdev_private *np = netdev_priv(dev);
 
        if (netif_msg_ifdown(np))
                printk(KERN_DEBUG
@@ -2503,9 +3053,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);
@@ -2557,10 +3105,11 @@ static int netdev_close(struct net_device *dev)
 static void __devexit natsemi_remove1 (struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
 
        unregister_netdev (dev);
        pci_release_regions (pdev);
-       iounmap ((char *) dev->base_addr);
+       iounmap(ioaddr);
        free_netdev (dev);
        pci_set_drvdata(pdev, NULL);
 }
@@ -2586,15 +3135,18 @@ 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 = dev->priv;
-       long ioaddr = dev->base_addr;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
 
        rtnl_lock();
        if (netif_running (dev)) {
@@ -2611,6 +3163,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);
 
@@ -2640,7 +3194,7 @@ static int natsemi_suspend (struct pci_dev *pdev, u32 state)
 static int natsemi_resume (struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata (pdev);
-       struct netdev_private *np = dev->priv;
+       struct netdev_private *np = netdev_priv(dev);
 
        rtnl_lock();
        if (netif_device_present(dev))
@@ -2663,6 +3217,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;
@@ -2688,7 +3243,7 @@ static int __init natsemi_init_mod (void)
        printk(version);
 #endif
 
-       return pci_module_init (&natsemi_driver);
+       return pci_register_driver(&natsemi_driver);
 }
 
 static void __exit natsemi_exit_mod (void)