*sonic/natsemi/ns83829: Move the National Semi-conductor drivers
authorJeff Kirsher <jeffrey.t.kirsher@intel.com>
Wed, 18 May 2011 12:14:22 +0000 (05:14 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Thu, 11 Aug 2011 23:29:21 +0000 (16:29 -0700)
Move the National Semi-conductor drivers into drivers/net/ethernet/natsemi/
and make the necessary Kconfig and Makefile changes.  Also moved the 8390
(National Semi-conductor) devices as a sub-menu of National Semi-conductor
devices.

- moved the ibmlana driver as well into this directory since it is a
  "SONIC" driver

CC: Alfred Arnold <alfred.arnold@lancom.de>
CC: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
CC: Harald Welte <laforge@gnumonks.org>
CC: Tim Hockin <thockin@hockin.org>
CC: <linux-ns83820@kvack.org>
CC: Kevin Chea <kchea@yahoo.com>
CC: Marc Gauthier <marc@linux-xtensa.org>
CC: Chris Zankel <chris@zankel.net>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Acked-by: Marc Gauthier <marc@tensilica.com>
26 files changed:
MAINTAINERS
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/ethernet/8390/Kconfig
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/natsemi/Kconfig [new file with mode: 0644]
drivers/net/ethernet/natsemi/Makefile [new file with mode: 0644]
drivers/net/ethernet/natsemi/ibmlana.c [new file with mode: 0644]
drivers/net/ethernet/natsemi/ibmlana.h [new file with mode: 0644]
drivers/net/ethernet/natsemi/jazzsonic.c [new file with mode: 0644]
drivers/net/ethernet/natsemi/macsonic.c [new file with mode: 0644]
drivers/net/ethernet/natsemi/natsemi.c [new file with mode: 0644]
drivers/net/ethernet/natsemi/ns83820.c [new file with mode: 0644]
drivers/net/ethernet/natsemi/sonic.c [new file with mode: 0644]
drivers/net/ethernet/natsemi/sonic.h [new file with mode: 0644]
drivers/net/ethernet/natsemi/xtsonic.c [new file with mode: 0644]
drivers/net/ibmlana.c [deleted file]
drivers/net/ibmlana.h [deleted file]
drivers/net/jazzsonic.c [deleted file]
drivers/net/macsonic.c [deleted file]
drivers/net/natsemi.c [deleted file]
drivers/net/ns83820.c [deleted file]
drivers/net/sonic.c [deleted file]
drivers/net/sonic.h [deleted file]
drivers/net/xtsonic.c [deleted file]

index 5dffc8ed4af68d069bbd0e03ca9a32c783ed0056..554529d11e99fc6f495e13ff0101e0d7d4ec4e3a 100644 (file)
@@ -4340,7 +4340,7 @@ F:        drivers/net/ethernet/myricom/myri10ge/
 NATSEMI ETHERNET DRIVER (DP8381x)
 M:     Tim Hockin <thockin@hockin.org>
 S:     Maintained
-F:     drivers/net/natsemi.c
+F:     drivers/net/ethernet/natsemi/natsemi.c
 
 NATIVE INSTRUMENTS USB SOUND INTERFACE DRIVER
 M:     Daniel Mack <zonque@gmail.com>
@@ -5943,7 +5943,7 @@ SONIC NETWORK DRIVER
 M:     Thomas Bogendoerfer <tsbogend@alpha.franken.de>
 L:     netdev@vger.kernel.org
 S:     Maintained
-F:     drivers/net/sonic.*
+F:     drivers/net/ethernet/natsemi/sonic.*
 
 SONICS SILICON BACKPLANE DRIVER (SSB)
 M:     Michael Buesch <m@bues.ch>
index d81f8f950124f816d8d5bcb7e0cb6a6bc59ce45f..8b1fae89898d3515cce7fba7027a494206f04de2 100644 (file)
@@ -237,19 +237,6 @@ config MACB
 
 source "drivers/net/arm/Kconfig"
 
-config MACSONIC
-       tristate "Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)"
-       depends on MAC
-       ---help---
-         Support for NatSemi SONIC based Ethernet devices.  This includes
-         the onboard Ethernet in many Quadras as well as some LC-PDS,
-         a few Nubus and all known Comm Slot Ethernet cards.  If you have
-         one of these say Y and read the Ethernet-HOWTO, available from
-         <http://www.tldp.org/docs.html#howto>.
-
-         To compile this driver as a module, choose M here. This module will
-         be called macsonic.
-
 config KORINA
        tristate "Korina (IDT RC32434) Ethernet support"
        depends on NET_ETHERNET && MIKROTIK_RB532
@@ -257,19 +244,6 @@ config KORINA
          If you have a Mikrotik RouterBoard 500 or IDT RC32434
          based system say Y. Otherwise say N.
 
-config MIPS_JAZZ_SONIC
-       tristate "MIPS JAZZ onboard SONIC Ethernet support"
-       depends on MACH_JAZZ
-       help
-         This is the driver for the onboard card of MIPS Magnum 4000,
-         Acer PICA, Olivetti M700-10 and a few other identical OEM systems.
-
-config XTENSA_XT2000_SONIC
-       tristate "Xtensa XT2000 onboard SONIC Ethernet support"
-       depends on XTENSA_PLATFORM_XT2000
-       help
-         This is the driver for the onboard card of the Xtensa XT2000 board.
-
 config SGI_IOC3_ETH
        bool "SGI IOC3 Ethernet"
        depends on PCI && SGI_IP27
@@ -556,21 +530,6 @@ config SEEQ8005
          To compile this driver as a module, choose M here. The module
          will be called seeq8005.
 
-config IBMLANA
-       tristate "IBM LAN Adapter/A support"
-       depends on MCA
-       ---help---
-         This is a Micro Channel Ethernet adapter.  You need to set
-         CONFIG_MCA to use this driver.  It is both available as an in-kernel
-         driver and as a module.
-
-         To compile this driver as a module, choose M here. The only
-         currently supported card is the IBM LAN Adapter/A for Ethernet.  It
-         will both support 16K and 32K memory windows, however a 32K window
-         gives a better security against packet losses.  Usage of multiple
-         boards with this driver should be possible, but has not been tested
-         up to now due to lack of hardware.
-
 config NET_PCI
        bool "EISA, VLB, PCI and on board controllers"
        depends on ISA || EISA || PCI
@@ -635,17 +594,6 @@ config FEALNX
          Say Y here to support the Myson MTD-800 family of PCI-based Ethernet 
          cards. <http://www.myson.com.tw/>
 
-config NATSEMI
-       tristate "National Semiconductor DP8381x series PCI Ethernet support"
-       depends on NET_PCI && PCI
-       select CRC32
-       help
-         This driver is for the National Semiconductor DP83810 series,
-         which is used in cards from PureData, NetGear, Linksys
-         and others, including the 83815 chip.
-         More specific information and updates are available from
-         <http://www.scyld.com/network/natsemi.html>.
-
 config 8139CP
        tristate "RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support (EXPERIMENTAL)"
        depends on NET_PCI && PCI && EXPERIMENTAL
@@ -1043,16 +991,6 @@ config IP1000
 
 source "drivers/net/ixp2000/Kconfig"
 
-config NS83820
-       tristate "National Semiconductor DP83820 support"
-       depends on PCI
-       help
-         This is a driver for the National Semiconductor DP83820 series
-         of gigabit ethernet MACs.  Cards using this chipset include
-         the D-Link DGE-500T, PureData's PDP8023Z-TG, SMC's SMC9462TX,
-         SOHO-GA2000T, SOHO-GA2500T.  The driver supports the use of
-         zero copy.
-
 config HAMACHI
        tristate "Packet Engines Hamachi GNIC-II support"
        depends on PCI
index 79f93f08c49094edde209cc71aa0e7414a2cdbed..843edb3eded763592c8ee564ac4affe594e4a6af 100644 (file)
@@ -43,8 +43,6 @@ obj-$(CONFIG_SIS190) += sis190.o
 obj-$(CONFIG_SIS900) += sis900.o
 obj-$(CONFIG_R6040) += r6040.o
 obj-$(CONFIG_YELLOWFIN) += yellowfin.o
-obj-$(CONFIG_NATSEMI) += natsemi.o
-obj-$(CONFIG_NS83820) += ns83820.o
 obj-$(CONFIG_FEALNX) += fealnx.o
 spidernet-y += spider_net.o spider_net_ethtool.o
 obj-$(CONFIG_SPIDER_NET) += spidernet.o ethernet/sun/sungem_phy.o
@@ -116,7 +114,6 @@ obj-$(CONFIG_DEFXX) += defxx.o
 obj-$(CONFIG_SGISEEQ) += sgiseeq.o
 obj-$(CONFIG_SGI_O2MACE_ETH) += meth.o
 obj-$(CONFIG_AT1700) += at1700.o
-obj-$(CONFIG_IBMLANA) += ibmlana.o
 obj-$(CONFIG_8139CP) += 8139cp.o
 obj-$(CONFIG_8139TOO) += 8139too.o
 obj-$(CONFIG_CPMAC) += cpmac.o
@@ -127,10 +124,8 @@ obj-$(CONFIG_SC92031) += sc92031.o
 obj-$(CONFIG_ETH16I) += eth16i.o
 obj-$(CONFIG_EQUALIZER) += eql.o
 obj-$(CONFIG_KORINA) += korina.o
-obj-$(CONFIG_MIPS_JAZZ_SONIC) += jazzsonic.o
 obj-$(CONFIG_MIPS_SIM_NET) += mipsnet.o
 obj-$(CONFIG_SGI_IOC3_ETH) += ioc3-eth.o
-obj-$(CONFIG_MACSONIC) += macsonic.o
 obj-$(CONFIG_TUN) += tun.o
 obj-$(CONFIG_VETH) += veth.o
 obj-$(CONFIG_NET_NETX) += netx-eth.o
@@ -144,8 +139,6 @@ obj-$(CONFIG_ETHOC) += ethoc.o
 obj-$(CONFIG_GRETH) += greth.o
 obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
 
-obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
-
 obj-$(CONFIG_DNET) += dnet.o
 obj-$(CONFIG_MACB) += macb.o
 obj-$(CONFIG_S6GMAC) += s6gmac.o
index f1b9bddc155068e1fce5ed3ef9c4674d6547c035..5d2169809b19e03c4f69bb98640caa01e8bddb9b 100644 (file)
@@ -4,9 +4,10 @@
 
 config NET_VENDOR_8390
        bool "National Semi-conductor 8390 devices"
-       depends on AMIGA_PCMCIA || PCI || SUPERH || ISA || MCA || EISA ||  \
-                  MAC || M32R || MACH_TX49XX || MCA_LEGACY || H8300 ||  \
-                  ARM || MIPS || ZORRO || PCMCIA || EXPERIMENTAL
+       depends on NET_VENDOR_NATSEMI && (AMIGA_PCMCIA || PCI || SUPERH || \
+                  ISA || MCA || EISA || MAC || M32R || MACH_TX49XX || \
+                  MCA_LEGACY || H8300 || ARM || MIPS || ZORRO || PCMCIA || \
+                  EXPERIMENTAL)
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y
          and read the Ethernet-HOWTO, available from
index c3c415d853b7f61e394a694b8c93c697f921b266..efc36651a2b94de635f6022487809088db04eb98 100644 (file)
@@ -12,7 +12,6 @@ menuconfig ETHERNET
 if ETHERNET
 
 source "drivers/net/ethernet/3com/Kconfig"
-source "drivers/net/ethernet/8390/Kconfig"
 source "drivers/net/ethernet/amd/Kconfig"
 source "drivers/net/ethernet/apple/Kconfig"
 source "drivers/net/ethernet/broadcom/Kconfig"
@@ -26,6 +25,8 @@ source "drivers/net/ethernet/intel/Kconfig"
 source "drivers/net/ethernet/i825xx/Kconfig"
 source "drivers/net/ethernet/mellanox/Kconfig"
 source "drivers/net/ethernet/myricom/Kconfig"
+source "drivers/net/ethernet/natsemi/Kconfig"
+source "drivers/net/ethernet/8390/Kconfig"
 source "drivers/net/ethernet/pasemi/Kconfig"
 source "drivers/net/ethernet/qlogic/Kconfig"
 source "drivers/net/ethernet/racal/Kconfig"
index b67b88d5afd9c113e1cc7a676717a5e168ae3918..668ca92b486312f6901a83f1db11d9b59fb3132e 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
 obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/
 obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
 obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
+obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
 obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/
 obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
 obj-$(CONFIG_NET_VENDOR_RACAL) += racal/
diff --git a/drivers/net/ethernet/natsemi/Kconfig b/drivers/net/ethernet/natsemi/Kconfig
new file mode 100644 (file)
index 0000000..1e5c1e1
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# National Semi-conductor device configuration
+#
+
+config NET_VENDOR_NATSEMI
+       bool "National Semi-conductor devices"
+       depends on MCA || MAC || MACH_JAZZ || PCI || XTENSA_PLATFORM_XT2000
+       ---help---
+         If you have a network (Ethernet) card belonging to this class, say Y
+         and read the Ethernet-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+
+         Note that the answer to this question doesn't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about National Semi-conductor devices. If you say Y,
+         you will be asked for your specific card in the following questions.
+
+if NET_VENDOR_NATSEMI
+
+config IBMLANA
+       tristate "IBM LAN Adapter/A support"
+       depends on MCA
+       ---help---
+         This is a Micro Channel Ethernet adapter.  You need to set
+         CONFIG_MCA to use this driver.  It is both available as an in-kernel
+         driver and as a module.
+
+         To compile this driver as a module, choose M here. The only
+         currently supported card is the IBM LAN Adapter/A for Ethernet.  It
+         will both support 16K and 32K memory windows, however a 32K window
+         gives a better security against packet losses.  Usage of multiple
+         boards with this driver should be possible, but has not been tested
+         up to now due to lack of hardware.
+
+config MACSONIC
+       tristate "Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)"
+       depends on MAC
+       ---help---
+         Support for NatSemi SONIC based Ethernet devices.  This includes
+         the onboard Ethernet in many Quadras as well as some LC-PDS,
+         a few Nubus and all known Comm Slot Ethernet cards.  If you have
+         one of these say Y and read the Ethernet-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+
+         To compile this driver as a module, choose M here. This module will
+         be called macsonic.
+
+config MIPS_JAZZ_SONIC
+       tristate "MIPS JAZZ onboard SONIC Ethernet support"
+       depends on MACH_JAZZ
+       ---help---
+         This is the driver for the onboard card of MIPS Magnum 4000,
+         Acer PICA, Olivetti M700-10 and a few other identical OEM systems.
+
+config NATSEMI
+       tristate "National Semiconductor DP8381x series PCI Ethernet support"
+       depends on PCI
+       select CRC32
+       ---help---
+         This driver is for the National Semiconductor DP83810 series,
+         which is used in cards from PureData, NetGear, Linksys
+         and others, including the 83815 chip.
+         More specific information and updates are available from
+         <http://www.scyld.com/network/natsemi.html>.
+
+config NS83820
+       tristate "National Semiconductor DP83820 support"
+       depends on PCI
+       ---help---
+         This is a driver for the National Semiconductor DP83820 series
+         of gigabit ethernet MACs.  Cards using this chipset include
+         the D-Link DGE-500T, PureData's PDP8023Z-TG, SMC's SMC9462TX,
+         SOHO-GA2000T, SOHO-GA2500T.  The driver supports the use of
+         zero copy.
+
+config XTENSA_XT2000_SONIC
+       tristate "Xtensa XT2000 onboard SONIC Ethernet support"
+       depends on XTENSA_PLATFORM_XT2000
+       ---help---
+         This is the driver for the onboard card of the Xtensa XT2000 board.
+
+endif # NET_VENDOR_NATSEMI
diff --git a/drivers/net/ethernet/natsemi/Makefile b/drivers/net/ethernet/natsemi/Makefile
new file mode 100644 (file)
index 0000000..9aa5dea
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for the National Semi-conductor Sonic devices.
+#
+
+obj-$(CONFIG_IBMLANA) += ibmlana.o
+obj-$(CONFIG_MACSONIC) += macsonic.o
+obj-$(CONFIG_MIPS_JAZZ_SONIC) += jazzsonic.o
+obj-$(CONFIG_NATSEMI) += natsemi.o
+obj-$(CONFIG_NS83820) += ns83820.o
+obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
diff --git a/drivers/net/ethernet/natsemi/ibmlana.c b/drivers/net/ethernet/natsemi/ibmlana.c
new file mode 100644 (file)
index 0000000..a7d6cad
--- /dev/null
@@ -0,0 +1,1075 @@
+/*
+net-3-driver for the IBM LAN Adapter/A
+
+This is an extension to the Linux operating system, and is covered by the
+same GNU General Public License that covers that work.
+
+Copyright 1999 by Alfred Arnold (alfred@ccac.rwth-aachen.de,
+                                 alfred.arnold@lancom.de)
+
+This driver is based both on the SK_MCA driver, which is itself based on the
+SK_G16 and 3C523 driver.
+
+paper sources:
+  'PC Hardware: Aufbau, Funktionsweise, Programmierung' by
+  Hans-Peter Messmer for the basic Microchannel stuff
+
+  'Linux Geraetetreiber' by Allesandro Rubini, Kalle Dalheimer
+  for help on Ethernet driver programming
+
+  'DP83934CVUL-20/25 MHz SONIC-T Ethernet Controller Datasheet' by National
+  Semiconductor for info on the MAC chip
+
+  'LAN Technical Reference Ethernet Adapter Interface Version 1 Release 1.0
+   Document Number SC30-3661-00' by IBM for info on the adapter itself
+
+  Also see http://www.national.com/analog 
+
+special acknowledgements to:
+  - Bob Eager for helping me out with documentation from IBM
+  - Jim Shorney for his endless patience with me while I was using
+    him as a beta tester to trace down the address filter bug ;-)
+
+  Missing things:
+
+  -> set debug level via ioctl instead of compile-time switches
+  -> I didn't follow the development of the 2.1.x kernels, so my
+     assumptions about which things changed with which kernel version
+     are probably nonsense
+
+History:
+  Nov 6th, 1999
+       startup from SK_MCA driver
+  Dec 6th, 1999
+       finally got docs about the card.  A big thank you to Bob Eager!
+  Dec 12th, 1999
+       first packet received
+  Dec 13th, 1999
+       recv queue done, tcpdump works
+  Dec 15th, 1999
+       transmission part works
+  Dec 28th, 1999
+       added usage of the isa_functions for Linux 2.3 .  Things should
+       still work with 2.0.x....
+  Jan 28th, 2000
+       in Linux 2.2.13, the version.h file mysteriously didn't get
+       included.  Added a workaround for this.  Furthermore, it now
+       not only compiles as a modules ;-)
+  Jan 30th, 2000
+       newer kernels automatically probe more than one board, so the
+       'startslot' as a variable is also needed here
+  Apr 12th, 2000
+       the interrupt mask register is not set 'hard' instead of individually
+       setting registers, since this seems to set bits that shouldn't be
+       set
+  May 21st, 2000
+       reset interrupt status immediately after CAM load
+       add a recovery delay after releasing the chip's reset line
+  May 24th, 2000
+       finally found the bug in the address filter setup - damned signed
+        chars!
+  June 1st, 2000
+       corrected version codes, added support for the latest 2.3 changes
+  Oct 28th, 2002
+       cleaned up for the 2.5 tree <alan@lxorguk.ukuu.org.uk>
+
+ *************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/mca.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/processor.h>
+#include <asm/io.h>
+
+#define _IBM_LANA_DRIVER_
+#include "ibmlana.h"
+
+#undef DEBUG
+
+#define DRV_NAME "ibmlana"
+
+/* ------------------------------------------------------------------------
+ * global static data - not more since we can handle multiple boards and
+ * have to pack all state info into the device struct!
+ * ------------------------------------------------------------------------ */
+
+static char *MediaNames[Media_Count] = {
+       "10BaseT", "10Base5", "Unknown", "10Base2"
+};
+
+/* ------------------------------------------------------------------------
+ * private subfunctions
+ * ------------------------------------------------------------------------ */
+
+#ifdef DEBUG
+  /* dump all registers */
+
+static void dumpregs(struct net_device *dev)
+{
+       int z;
+
+       for (z = 0; z < 160; z += 2) {
+               if (!(z & 15))
+                       printk("REGS: %04x:", z);
+               printk(" %04x", inw(dev->base_addr + z));
+               if ((z & 15) == 14)
+                       printk("\n");
+       }
+}
+
+/* dump parts of shared memory - only needed during debugging */
+
+static void dumpmem(struct net_device *dev, u32 start, u32 len)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+       int z;
+
+       printk("Address %04x:\n", start);
+       for (z = 0; z < len; z++) {
+               if ((z & 15) == 0)
+                       printk("%04x:", z);
+               printk(" %02x", readb(priv->base + start + z));
+               if ((z & 15) == 15)
+                       printk("\n");
+       }
+       if ((z & 15) != 0)
+               printk("\n");
+}
+
+/* print exact time - ditto */
+
+static void PrTime(void)
+{
+       struct timeval tv;
+
+       do_gettimeofday(&tv);
+       printk("%9d:%06d: ", (int) tv.tv_sec, (int) tv.tv_usec);
+}
+#endif                         /* DEBUG */
+
+/* deduce resources out of POS registers */
+
+static void getaddrs(struct mca_device *mdev, int *base, int *memlen,
+                    int *iobase, int *irq, ibmlana_medium *medium)
+{
+       u_char pos0, pos1;
+
+       pos0 = mca_device_read_stored_pos(mdev, 2);
+       pos1 = mca_device_read_stored_pos(mdev, 3);
+
+       *base = 0xc0000 + ((pos1 & 0xf0) << 9);
+       *memlen = (pos1 & 0x01) ? 0x8000 : 0x4000;
+       *iobase = (pos0 & 0xe0) << 7;
+       switch (pos0 & 0x06) {
+       case 0:
+               *irq = 5;
+               break;
+       case 2:
+               *irq = 15;
+               break;
+       case 4:
+               *irq = 10;
+               break;
+       case 6:
+               *irq = 11;
+               break;
+       }
+       *medium = (pos0 & 0x18) >> 3;
+}
+
+/* wait on register value with mask and timeout */
+
+static int wait_timeout(struct net_device *dev, int regoffs, u16 mask,
+                       u16 value, int timeout)
+{
+       unsigned long fin = jiffies + timeout;
+
+       while (time_before(jiffies,fin))
+               if ((inw(dev->base_addr + regoffs) & mask) == value)
+                       return 1;
+
+       return 0;
+}
+
+
+/* reset the whole board */
+
+static void ResetBoard(struct net_device *dev)
+{
+       unsigned char bcmval;
+
+       /* read original board control value */
+
+       bcmval = inb(dev->base_addr + BCMREG);
+
+       /* set reset bit for a while */
+
+       bcmval |= BCMREG_RESET;
+       outb(bcmval, dev->base_addr + BCMREG);
+       udelay(10);
+       bcmval &= ~BCMREG_RESET;
+       outb(bcmval, dev->base_addr + BCMREG);
+
+       /* switch over to RAM again */
+
+       bcmval |= BCMREG_RAMEN | BCMREG_RAMWIN;
+       outb(bcmval, dev->base_addr + BCMREG);
+}
+
+/* calculate RAM layout & set up descriptors in RAM */
+
+static void InitDscrs(struct net_device *dev)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+       u32 addr, baddr, raddr;
+       int z;
+       tda_t tda;
+       rda_t rda;
+       rra_t rra;
+
+       /* initialize RAM */
+
+       memset_io(priv->base, 0xaa,
+                     dev->mem_start - dev->mem_start); /* XXX: typo? */
+
+       /* setup n TX descriptors - independent of RAM size */
+
+       priv->tdastart = addr = 0;
+       priv->txbufstart = baddr = sizeof(tda_t) * TXBUFCNT;
+       for (z = 0; z < TXBUFCNT; z++) {
+               tda.status = 0;
+               tda.config = 0;
+               tda.length = 0;
+               tda.fragcount = 1;
+               tda.startlo = baddr;
+               tda.starthi = 0;
+               tda.fraglength = 0;
+               if (z == TXBUFCNT - 1)
+                       tda.link = priv->tdastart;
+               else
+                       tda.link = addr + sizeof(tda_t);
+               tda.link |= 1;
+               memcpy_toio(priv->base + addr, &tda, sizeof(tda_t));
+               addr += sizeof(tda_t);
+               baddr += PKTSIZE;
+       }
+
+       /* calculate how many receive buffers fit into remaining memory */
+
+       priv->rxbufcnt = (dev->mem_end - dev->mem_start - baddr) / (sizeof(rra_t) + sizeof(rda_t) + PKTSIZE);
+
+       /* calculate receive addresses */
+
+       priv->rrastart = raddr = priv->txbufstart + (TXBUFCNT * PKTSIZE);
+       priv->rdastart = addr = priv->rrastart + (priv->rxbufcnt * sizeof(rra_t));
+       priv->rxbufstart = baddr = priv->rdastart + (priv->rxbufcnt * sizeof(rda_t));
+
+       for (z = 0; z < priv->rxbufcnt; z++) {
+               rra.startlo = baddr;
+               rra.starthi = 0;
+               rra.cntlo = PKTSIZE >> 1;
+               rra.cnthi = 0;
+               memcpy_toio(priv->base + raddr, &rra, sizeof(rra_t));
+
+               rda.status = 0;
+               rda.length = 0;
+               rda.startlo = 0;
+               rda.starthi = 0;
+               rda.seqno = 0;
+               if (z < priv->rxbufcnt - 1)
+                       rda.link = addr + sizeof(rda_t);
+               else
+                       rda.link = 1;
+               rda.inuse = 1;
+               memcpy_toio(priv->base + addr, &rda, sizeof(rda_t));
+
+               baddr += PKTSIZE;
+               raddr += sizeof(rra_t);
+               addr += sizeof(rda_t);
+       }
+
+       /* initialize current pointers */
+
+       priv->nextrxdescr = 0;
+       priv->lastrxdescr = priv->rxbufcnt - 1;
+       priv->nexttxdescr = 0;
+       priv->currtxdescr = 0;
+       priv->txusedcnt = 0;
+       memset(priv->txused, 0, sizeof(priv->txused));
+}
+
+/* set up Rx + Tx descriptors in SONIC */
+
+static int InitSONIC(struct net_device *dev)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+
+       /* set up start & end of resource area */
+
+       outw(0, SONIC_URRA);
+       outw(priv->rrastart, dev->base_addr + SONIC_RSA);
+       outw(priv->rrastart + (priv->rxbufcnt * sizeof(rra_t)), dev->base_addr + SONIC_REA);
+       outw(priv->rrastart, dev->base_addr + SONIC_RRP);
+       outw(priv->rrastart, dev->base_addr + SONIC_RWP);
+
+       /* set EOBC so that only one packet goes into one buffer */
+
+       outw((PKTSIZE - 4) >> 1, dev->base_addr + SONIC_EOBC);
+
+       /* let SONIC read the first RRA descriptor */
+
+       outw(CMDREG_RRRA, dev->base_addr + SONIC_CMDREG);
+       if (!wait_timeout(dev, SONIC_CMDREG, CMDREG_RRRA, 0, 2)) {
+               printk(KERN_ERR "%s: SONIC did not respond on RRRA command - giving up.", dev->name);
+               return 0;
+       }
+
+       /* point SONIC to the first RDA */
+
+       outw(0, dev->base_addr + SONIC_URDA);
+       outw(priv->rdastart, dev->base_addr + SONIC_CRDA);
+
+       /* set upper half of TDA address */
+
+       outw(0, dev->base_addr + SONIC_UTDA);
+
+       return 1;
+}
+
+/* stop SONIC so we can reinitialize it */
+
+static void StopSONIC(struct net_device *dev)
+{
+       /* disable interrupts */
+
+       outb(inb(dev->base_addr + BCMREG) & (~BCMREG_IEN), dev->base_addr + BCMREG);
+       outb(0, dev->base_addr + SONIC_IMREG);
+
+       /* reset the SONIC */
+
+       outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
+       udelay(10);
+       outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
+}
+
+/* initialize card and SONIC for proper operation */
+
+static void putcam(camentry_t * cams, int *camcnt, char *addr)
+{
+       camentry_t *pcam = cams + (*camcnt);
+       u8 *uaddr = (u8 *) addr;
+
+       pcam->index = *camcnt;
+       pcam->addr0 = (((u16) uaddr[1]) << 8) | uaddr[0];
+       pcam->addr1 = (((u16) uaddr[3]) << 8) | uaddr[2];
+       pcam->addr2 = (((u16) uaddr[5]) << 8) | uaddr[4];
+       (*camcnt)++;
+}
+
+static void InitBoard(struct net_device *dev)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+       int camcnt;
+       camentry_t cams[16];
+       u32 cammask;
+       struct netdev_hw_addr *ha;
+       u16 rcrval;
+
+       /* reset the SONIC */
+
+       outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
+       udelay(10);
+
+       /* clear all spurious interrupts */
+
+       outw(inw(dev->base_addr + SONIC_ISREG), dev->base_addr + SONIC_ISREG);
+
+       /* set up the SONIC's bus interface - constant for this adapter -
+          must be done while the SONIC is in reset */
+
+       outw(DCREG_USR1 | DCREG_USR0 | DCREG_WC1 | DCREG_DW32, dev->base_addr + SONIC_DCREG);
+       outw(0, dev->base_addr + SONIC_DCREG2);
+
+       /* remove reset form the SONIC */
+
+       outw(0, dev->base_addr + SONIC_CMDREG);
+       udelay(10);
+
+       /* data sheet requires URRA to be programmed before setting up the CAM contents */
+
+       outw(0, dev->base_addr + SONIC_URRA);
+
+       /* program the CAM entry 0 to the device address */
+
+       camcnt = 0;
+       putcam(cams, &camcnt, dev->dev_addr);
+
+       /* start putting the multicast addresses into the CAM list.  Stop if
+          it is full. */
+
+       netdev_for_each_mc_addr(ha, dev) {
+               putcam(cams, &camcnt, ha->addr);
+               if (camcnt == 16)
+                       break;
+       }
+
+       /* calculate CAM mask */
+
+       cammask = (1 << camcnt) - 1;
+
+       /* feed CDA into SONIC, initialize RCR value (always get broadcasts) */
+
+       memcpy_toio(priv->base, cams, sizeof(camentry_t) * camcnt);
+       memcpy_toio(priv->base + (sizeof(camentry_t) * camcnt), &cammask, sizeof(cammask));
+
+#ifdef DEBUG
+       printk("CAM setup:\n");
+       dumpmem(dev, 0, sizeof(camentry_t) * camcnt + sizeof(cammask));
+#endif
+
+       outw(0, dev->base_addr + SONIC_CAMPTR);
+       outw(camcnt, dev->base_addr + SONIC_CAMCNT);
+       outw(CMDREG_LCAM, dev->base_addr + SONIC_CMDREG);
+       if (!wait_timeout(dev, SONIC_CMDREG, CMDREG_LCAM, 0, 2)) {
+               printk(KERN_ERR "%s:SONIC did not respond on LCAM command - giving up.", dev->name);
+               return;
+       } else {
+               /* clear interrupt condition */
+
+               outw(ISREG_LCD, dev->base_addr + SONIC_ISREG);
+
+#ifdef DEBUG
+               printk("Loading CAM done, address pointers %04x:%04x\n",
+                      inw(dev->base_addr + SONIC_URRA),
+                      inw(dev->base_addr + SONIC_CAMPTR));
+               {
+                       int z;
+
+                       printk("\n-->CAM: PTR %04x CNT %04x\n",
+                              inw(dev->base_addr + SONIC_CAMPTR),
+                              inw(dev->base_addr + SONIC_CAMCNT));
+                       outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
+                       for (z = 0; z < camcnt; z++) {
+                               outw(z, dev->base_addr + SONIC_CAMEPTR);
+                               printk("Entry %d: %04x %04x %04x\n", z,
+                                      inw(dev->base_addr + SONIC_CAMADDR0),
+                                      inw(dev->base_addr + SONIC_CAMADDR1),
+                                      inw(dev->base_addr + SONIC_CAMADDR2));
+                       }
+                       outw(0, dev->base_addr + SONIC_CMDREG);
+               }
+#endif
+       }
+
+       rcrval = RCREG_BRD | RCREG_LB_NONE;
+
+       /* if still multicast addresses left or ALLMULTI is set, set the multicast
+          enable bit */
+
+       if ((dev->flags & IFF_ALLMULTI) || netdev_mc_count(dev) > camcnt)
+               rcrval |= RCREG_AMC;
+
+       /* promiscuous mode ? */
+
+       if (dev->flags & IFF_PROMISC)
+               rcrval |= RCREG_PRO;
+
+       /* program receive mode */
+
+       outw(rcrval, dev->base_addr + SONIC_RCREG);
+#ifdef DEBUG
+       printk("\nRCRVAL: %04x\n", rcrval);
+#endif
+
+       /* set up descriptors in shared memory + feed them into SONIC registers */
+
+       InitDscrs(dev);
+       if (!InitSONIC(dev))
+               return;
+
+       /* reset all pending interrupts */
+
+       outw(0xffff, dev->base_addr + SONIC_ISREG);
+
+       /* enable transmitter + receiver interrupts */
+
+       outw(CMDREG_RXEN, dev->base_addr + SONIC_CMDREG);
+       outw(IMREG_PRXEN | IMREG_RBEEN | IMREG_PTXEN | IMREG_TXEREN, dev->base_addr + SONIC_IMREG);
+
+       /* turn on card interrupts */
+
+       outb(inb(dev->base_addr + BCMREG) | BCMREG_IEN, dev->base_addr + BCMREG);
+
+#ifdef DEBUG
+       printk("Register dump after initialization:\n");
+       dumpregs(dev);
+#endif
+}
+
+/* start transmission of a descriptor */
+
+static void StartTx(struct net_device *dev, int descr)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+       int addr;
+
+       addr = priv->tdastart + (descr * sizeof(tda_t));
+
+       /* put descriptor address into SONIC */
+
+       outw(addr, dev->base_addr + SONIC_CTDA);
+
+       /* trigger transmitter */
+
+       priv->currtxdescr = descr;
+       outw(CMDREG_TXP, dev->base_addr + SONIC_CMDREG);
+}
+
+/* ------------------------------------------------------------------------
+ * interrupt handler(s)
+ * ------------------------------------------------------------------------ */
+
+/* receive buffer area exhausted */
+
+static void irqrbe_handler(struct net_device *dev)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+
+       /* point the SONIC back to the RRA start */
+
+       outw(priv->rrastart, dev->base_addr + SONIC_RRP);
+       outw(priv->rrastart, dev->base_addr + SONIC_RWP);
+}
+
+/* receive interrupt */
+
+static void irqrx_handler(struct net_device *dev)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+       rda_t rda;
+       u32 rdaaddr, lrdaaddr;
+
+       /* loop until ... */
+
+       while (1) {
+               /* read descriptor that was next to be filled by SONIC */
+
+               rdaaddr = priv->rdastart + (priv->nextrxdescr * sizeof(rda_t));
+               lrdaaddr = priv->rdastart + (priv->lastrxdescr * sizeof(rda_t));
+               memcpy_fromio(&rda, priv->base + rdaaddr, sizeof(rda_t));
+
+               /* iron out upper word halves of fields we use - SONIC will duplicate
+                  bits 0..15 to 16..31 */
+
+               rda.status &= 0xffff;
+               rda.length &= 0xffff;
+               rda.startlo &= 0xffff;
+
+               /* stop if the SONIC still owns it, i.e. there is no data for us */
+
+               if (rda.inuse)
+                       break;
+
+               /* good packet? */
+
+               else if (rda.status & RCREG_PRX) {
+                       struct sk_buff *skb;
+
+                       /* fetch buffer */
+
+                       skb = dev_alloc_skb(rda.length + 2);
+                       if (skb == NULL)
+                               dev->stats.rx_dropped++;
+                       else {
+                               /* copy out data */
+
+                               memcpy_fromio(skb_put(skb, rda.length),
+                                              priv->base +
+                                              rda.startlo, rda.length);
+
+                               /* set up skb fields */
+
+                               skb->protocol = eth_type_trans(skb, dev);
+                               skb_checksum_none_assert(skb);
+
+                               /* bookkeeping */
+                               dev->stats.rx_packets++;
+                               dev->stats.rx_bytes += rda.length;
+
+                               /* pass to the upper layers */
+                               netif_rx(skb);
+                       }
+               }
+
+               /* otherwise check error status bits and increase statistics */
+
+               else {
+                       dev->stats.rx_errors++;
+                       if (rda.status & RCREG_FAER)
+                               dev->stats.rx_frame_errors++;
+                       if (rda.status & RCREG_CRCR)
+                               dev->stats.rx_crc_errors++;
+               }
+
+               /* descriptor processed, will become new last descriptor in queue */
+
+               rda.link = 1;
+               rda.inuse = 1;
+               memcpy_toio(priv->base + rdaaddr, &rda,
+                            sizeof(rda_t));
+
+               /* set up link and EOL = 0 in currently last descriptor. Only write
+                  the link field since the SONIC may currently already access the
+                  other fields. */
+
+               memcpy_toio(priv->base + lrdaaddr + 20, &rdaaddr, 4);
+
+               /* advance indices */
+
+               priv->lastrxdescr = priv->nextrxdescr;
+               if ((++priv->nextrxdescr) >= priv->rxbufcnt)
+                       priv->nextrxdescr = 0;
+       }
+}
+
+/* transmit interrupt */
+
+static void irqtx_handler(struct net_device *dev)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+       tda_t tda;
+
+       /* fetch descriptor (we forgot the size ;-) */
+       memcpy_fromio(&tda, priv->base + priv->tdastart + (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t));
+
+       /* update statistics */
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += tda.length;
+
+       /* update our pointers */
+       priv->txused[priv->currtxdescr] = 0;
+       priv->txusedcnt--;
+
+       /* if there are more descriptors present in RAM, start them */
+       if (priv->txusedcnt > 0)
+               StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT);
+
+       /* tell the upper layer we can go on transmitting */
+       netif_wake_queue(dev);
+}
+
+static void irqtxerr_handler(struct net_device *dev)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+       tda_t tda;
+
+       /* fetch descriptor to check status */
+       memcpy_fromio(&tda, priv->base + priv->tdastart + (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t));
+
+       /* update statistics */
+       dev->stats.tx_errors++;
+       if (tda.status & (TCREG_NCRS | TCREG_CRSL))
+               dev->stats.tx_carrier_errors++;
+       if (tda.status & TCREG_EXC)
+               dev->stats.tx_aborted_errors++;
+       if (tda.status & TCREG_OWC)
+               dev->stats.tx_window_errors++;
+       if (tda.status & TCREG_FU)
+               dev->stats.tx_fifo_errors++;
+
+       /* update our pointers */
+       priv->txused[priv->currtxdescr] = 0;
+       priv->txusedcnt--;
+
+       /* if there are more descriptors present in RAM, start them */
+       if (priv->txusedcnt > 0)
+               StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT);
+
+       /* tell the upper layer we can go on transmitting */
+       netif_wake_queue(dev);
+}
+
+/* general interrupt entry */
+
+static irqreturn_t irq_handler(int dummy, void *device)
+{
+       struct net_device *dev = device;
+       u16 ival;
+
+       /* in case we're not meant... */
+       if (!(inb(dev->base_addr + BCMREG) & BCMREG_IPEND))
+               return IRQ_NONE;
+
+       /* loop through the interrupt bits until everything is clear */
+       while (1) {
+               ival = inw(dev->base_addr + SONIC_ISREG);
+
+               if (ival & ISREG_RBE) {
+                       irqrbe_handler(dev);
+                       outw(ISREG_RBE, dev->base_addr + SONIC_ISREG);
+               }
+               if (ival & ISREG_PKTRX) {
+                       irqrx_handler(dev);
+                       outw(ISREG_PKTRX, dev->base_addr + SONIC_ISREG);
+               }
+               if (ival & ISREG_TXDN) {
+                       irqtx_handler(dev);
+                       outw(ISREG_TXDN, dev->base_addr + SONIC_ISREG);
+               }
+               if (ival & ISREG_TXER) {
+                       irqtxerr_handler(dev);
+                       outw(ISREG_TXER, dev->base_addr + SONIC_ISREG);
+               }
+               break;
+       }
+       return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------------------
+ * driver methods
+ * ------------------------------------------------------------------------ */
+
+/* MCA info */
+
+#if 0 /* info available elsewhere, but this is kept for reference */
+static int ibmlana_getinfo(char *buf, int slot, void *d)
+{
+       int len = 0, i;
+       struct net_device *dev = (struct net_device *) d;
+       ibmlana_priv *priv;
+
+       /* can't say anything about an uninitialized device... */
+
+       if (dev == NULL)
+               return len;
+       priv = netdev_priv(dev);
+
+       /* print info */
+
+       len += sprintf(buf + len, "IRQ: %d\n", priv->realirq);
+       len += sprintf(buf + len, "I/O: %#lx\n", dev->base_addr);
+       len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start, dev->mem_end - 1);
+       len += sprintf(buf + len, "Transceiver: %s\n", MediaNames[priv->medium]);
+       len += sprintf(buf + len, "Device: %s\n", dev->name);
+       len += sprintf(buf + len, "MAC address:");
+       for (i = 0; i < 6; i++)
+               len += sprintf(buf + len, " %02x", dev->dev_addr[i]);
+       buf[len++] = '\n';
+       buf[len] = 0;
+
+       return len;
+}
+#endif
+
+/* open driver.  Means also initialization and start of LANCE */
+
+static int ibmlana_open(struct net_device *dev)
+{
+       int result;
+       ibmlana_priv *priv = netdev_priv(dev);
+
+       /* register resources - only necessary for IRQ */
+
+       result = request_irq(priv->realirq, irq_handler, IRQF_SHARED,
+                            dev->name, dev);
+       if (result != 0) {
+               printk(KERN_ERR "%s: failed to register irq %d\n", dev->name, dev->irq);
+               return result;
+       }
+       dev->irq = priv->realirq;
+
+       /* set up the card and SONIC */
+       InitBoard(dev);
+
+       /* initialize operational flags */
+       netif_start_queue(dev);
+       return 0;
+}
+
+/* close driver.  Shut down board and free allocated resources */
+
+static int ibmlana_close(struct net_device *dev)
+{
+       /* turn off board */
+
+       /* release resources */
+       if (dev->irq != 0)
+               free_irq(dev->irq, dev);
+       dev->irq = 0;
+       return 0;
+}
+
+/* transmit a block. */
+
+static netdev_tx_t ibmlana_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       ibmlana_priv *priv = netdev_priv(dev);
+       int tmplen, addr;
+       unsigned long flags;
+       tda_t tda;
+       int baddr;
+
+       /* find out if there are free slots for a frame to transmit. If not,
+          the upper layer is in deep desperation and we simply ignore the frame. */
+
+       if (priv->txusedcnt >= TXBUFCNT) {
+               dev->stats.tx_dropped++;
+               goto tx_done;
+       }
+
+       /* copy the frame data into the next free transmit buffer - fillup missing */
+       tmplen = skb->len;
+       if (tmplen < 60)
+               tmplen = 60;
+       baddr = priv->txbufstart + (priv->nexttxdescr * PKTSIZE);
+       memcpy_toio(priv->base + baddr, skb->data, skb->len);
+
+       /* copy filler into RAM - in case we're filling up...
+          we're filling a bit more than necessary, but that doesn't harm
+          since the buffer is far larger...
+          Sorry Linus for the filler string but I couldn't resist ;-) */
+
+       if (tmplen > skb->len) {
+               char *fill = "NetBSD is a nice OS too! ";
+               unsigned int destoffs = skb->len, l = strlen(fill);
+
+               while (destoffs < tmplen) {
+                       memcpy_toio(priv->base + baddr + destoffs, fill, l);
+                       destoffs += l;
+               }
+       }
+
+       /* set up the new frame descriptor */
+       addr = priv->tdastart + (priv->nexttxdescr * sizeof(tda_t));
+       memcpy_fromio(&tda, priv->base + addr, sizeof(tda_t));
+       tda.length = tda.fraglength = tmplen;
+       memcpy_toio(priv->base + addr, &tda, sizeof(tda_t));
+
+       /* if there were no active descriptors, trigger the SONIC */
+       spin_lock_irqsave(&priv->lock, flags);
+
+       priv->txusedcnt++;
+       priv->txused[priv->nexttxdescr] = 1;
+
+       /* are all transmission slots used up ? */
+       if (priv->txusedcnt >= TXBUFCNT)
+               netif_stop_queue(dev);
+
+       if (priv->txusedcnt == 1)
+               StartTx(dev, priv->nexttxdescr);
+       priv->nexttxdescr = (priv->nexttxdescr + 1) % TXBUFCNT;
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+tx_done:
+       dev_kfree_skb(skb);
+       return NETDEV_TX_OK;
+}
+
+/* switch receiver mode. */
+
+static void ibmlana_set_multicast_list(struct net_device *dev)
+{
+       /* first stop the SONIC... */
+       StopSONIC(dev);
+       /* ...then reinit it with the new flags */
+       InitBoard(dev);
+}
+
+/* ------------------------------------------------------------------------
+ * hardware check
+ * ------------------------------------------------------------------------ */
+
+static int ibmlana_irq;
+static int ibmlana_io;
+static int startslot;          /* counts through slots when probing multiple devices */
+
+static short ibmlana_adapter_ids[] __initdata = {
+       IBM_LANA_ID,
+       0x0000
+};
+
+static char *ibmlana_adapter_names[] __devinitdata = {
+       "IBM LAN Adapter/A",
+       NULL
+};
+
+
+static const struct net_device_ops ibmlana_netdev_ops = {
+       .ndo_open               = ibmlana_open,
+       .ndo_stop               = ibmlana_close,
+       .ndo_start_xmit         = ibmlana_tx,
+       .ndo_set_multicast_list = ibmlana_set_multicast_list,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+static int __devinit ibmlana_init_one(struct device *kdev)
+{
+       struct mca_device *mdev = to_mca_device(kdev);
+       struct net_device *dev;
+       int slot = mdev->slot, z, rc;
+       int base = 0, irq = 0, iobase = 0, memlen = 0;
+       ibmlana_priv *priv;
+       ibmlana_medium medium;
+
+       dev = alloc_etherdev(sizeof(ibmlana_priv));
+       if (!dev)
+               return -ENOMEM;
+
+       dev->irq = ibmlana_irq;
+       dev->base_addr = ibmlana_io;
+
+       base = dev->mem_start;
+       irq = dev->irq;
+
+       /* deduce card addresses */
+       getaddrs(mdev, &base, &memlen, &iobase, &irq, &medium);
+
+       /* were we looking for something different ? */
+       if (dev->irq && dev->irq != irq) {
+               rc = -ENODEV;
+               goto err_out;
+       }
+       if (dev->mem_start && dev->mem_start != base) {
+               rc = -ENODEV;
+               goto err_out;
+       }
+
+       /* announce success */
+       printk(KERN_INFO "%s: IBM LAN Adapter/A found in slot %d\n", dev->name, slot + 1);
+
+       /* try to obtain I/O range */
+       if (!request_region(iobase, IBM_LANA_IORANGE, DRV_NAME)) {
+               printk(KERN_ERR "%s: cannot allocate I/O range at %#x!\n", DRV_NAME, iobase);
+               startslot = slot + 1;
+               rc = -EBUSY;
+               goto err_out;
+       }
+
+       priv = netdev_priv(dev);
+       priv->slot = slot;
+       priv->realirq = mca_device_transform_irq(mdev, irq);
+       priv->medium = medium;
+       spin_lock_init(&priv->lock);
+
+       /* set base + irq for this device (irq not allocated so far) */
+
+       dev->irq = 0;
+       dev->mem_start = base;
+       dev->mem_end = base + memlen;
+       dev->base_addr = iobase;
+
+       priv->base = ioremap(base, memlen);
+       if (!priv->base) {
+               printk(KERN_ERR "%s: cannot remap memory!\n", DRV_NAME);
+               startslot = slot + 1;
+               rc = -EBUSY;
+               goto err_out_reg;
+       }
+
+       mca_device_set_name(mdev, ibmlana_adapter_names[mdev->index]);
+       mca_device_set_claim(mdev, 1);
+
+       /* set methods */
+       dev->netdev_ops = &ibmlana_netdev_ops;
+       dev->flags |= IFF_MULTICAST;
+
+       /* copy out MAC address */
+
+       for (z = 0; z < ETH_ALEN; z++)
+               dev->dev_addr[z] = inb(dev->base_addr + MACADDRPROM + z);
+
+       /* print config */
+
+       printk(KERN_INFO "%s: IRQ %d, I/O %#lx, memory %#lx-%#lx, "
+              "MAC address %pM.\n",
+              dev->name, priv->realirq, dev->base_addr,
+              dev->mem_start, dev->mem_end - 1,
+              dev->dev_addr);
+       printk(KERN_INFO "%s: %s medium\n", dev->name, MediaNames[priv->medium]);
+
+       /* reset board */
+
+       ResetBoard(dev);
+
+       /* next probe will start at next slot */
+
+       startslot = slot + 1;
+
+       rc = register_netdev(dev);
+       if (rc)
+               goto err_out_claimed;
+
+       dev_set_drvdata(kdev, dev);
+       return 0;
+
+err_out_claimed:
+       mca_device_set_claim(mdev, 0);
+       iounmap(priv->base);
+err_out_reg:
+       release_region(iobase, IBM_LANA_IORANGE);
+err_out:
+       free_netdev(dev);
+       return rc;
+}
+
+static int ibmlana_remove_one(struct device *kdev)
+{
+       struct mca_device *mdev = to_mca_device(kdev);
+       struct net_device *dev = dev_get_drvdata(kdev);
+       ibmlana_priv *priv = netdev_priv(dev);
+
+       unregister_netdev(dev);
+       /*DeinitBoard(dev); */
+       release_region(dev->base_addr, IBM_LANA_IORANGE);
+       mca_device_set_claim(mdev, 0);
+       iounmap(priv->base);
+       free_netdev(dev);
+       return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * modularization support
+ * ------------------------------------------------------------------------ */
+
+module_param_named(irq, ibmlana_irq, int, 0);
+module_param_named(io, ibmlana_io, int, 0);
+MODULE_PARM_DESC(irq, "IBM LAN/A IRQ number");
+MODULE_PARM_DESC(io, "IBM LAN/A I/O base address");
+MODULE_LICENSE("GPL");
+
+static struct mca_driver ibmlana_driver = {
+       .id_table = ibmlana_adapter_ids,
+       .driver = {
+               .name   = "ibmlana",
+               .bus    = &mca_bus_type,
+               .probe  = ibmlana_init_one,
+               .remove = ibmlana_remove_one,
+       },
+};
+
+static int __init ibmlana_init_module(void)
+{
+       return mca_register_driver(&ibmlana_driver);
+}
+
+static void __exit ibmlana_cleanup_module(void)
+{
+       mca_unregister_driver(&ibmlana_driver);
+}
+
+module_init(ibmlana_init_module);
+module_exit(ibmlana_cleanup_module);
diff --git a/drivers/net/ethernet/natsemi/ibmlana.h b/drivers/net/ethernet/natsemi/ibmlana.h
new file mode 100644 (file)
index 0000000..accd5ef
--- /dev/null
@@ -0,0 +1,278 @@
+#ifndef _IBM_LANA_INCLUDE_
+#define _IBM_LANA_INCLUDE_
+
+#ifdef _IBM_LANA_DRIVER_
+
+/* maximum packet size */
+
+#define PKTSIZE 1524
+
+/* number of transmit buffers */
+
+#define TXBUFCNT 4
+
+/* Adapter ID's */
+#define IBM_LANA_ID 0xffe0
+
+/* media enumeration - defined in a way that it fits onto the LAN/A's
+   POS registers... */
+
+typedef enum {
+       Media_10BaseT, Media_10Base5,
+       Media_Unknown, Media_10Base2, Media_Count
+} ibmlana_medium;
+
+/* private structure */
+
+typedef struct {
+       unsigned int slot;              /* MCA-Slot-#                       */
+       int realirq;                    /* memorizes actual IRQ, even when
+                                          currently not allocated          */
+       ibmlana_medium medium;          /* physical cannector               */
+       u32     tdastart, txbufstart,   /* addresses                        */
+               rrastart, rxbufstart, rdastart, rxbufcnt, txusedcnt;
+       int     nextrxdescr,            /* next rx descriptor to be used    */
+               lastrxdescr,            /* last free rx descriptor          */
+               nexttxdescr,            /* last tx descriptor to be used    */
+               currtxdescr,            /* tx descriptor currently tx'ed    */
+               txused[TXBUFCNT];       /* busy flags                       */
+       void __iomem *base;
+       spinlock_t lock;
+} ibmlana_priv;
+
+/* this card uses quite a lot of I/O ports...luckily the MCA bus decodes
+   a full 64K I/O range... */
+
+#define IBM_LANA_IORANGE 0xa0
+
+/* Command Register: */
+
+#define SONIC_CMDREG     0x00
+#define CMDREG_HTX       0x0001        /* halt transmission                */
+#define CMDREG_TXP       0x0002        /* start transmission               */
+#define CMDREG_RXDIS     0x0004        /* disable receiver                 */
+#define CMDREG_RXEN      0x0008        /* enable receiver                  */
+#define CMDREG_STP       0x0010        /* stop timer                       */
+#define CMDREG_ST        0x0020        /* start timer                      */
+#define CMDREG_RST       0x0080        /* software reset                   */
+#define CMDREG_RRRA      0x0100        /* force SONIC to read first RRA    */
+#define CMDREG_LCAM      0x0200        /* force SONIC to read CAM descrs   */
+
+/* Data Configuration Register */
+
+#define SONIC_DCREG      0x02
+#define DCREG_EXBUS      0x8000        /* Extended Bus Mode                */
+#define DCREG_LBR        0x2000        /* Latched Bus Retry                */
+#define DCREG_PO1        0x1000        /* Programmable Outputs             */
+#define DCREG_PO0        0x0800
+#define DCREG_SBUS       0x0400        /* Synchronous Bus Mode             */
+#define DCREG_USR1       0x0200        /* User Definable Pins              */
+#define DCREG_USR0       0x0100
+#define DCREG_WC0        0x0000        /* 0..3 Wait States                 */
+#define DCREG_WC1        0x0040
+#define DCREG_WC2        0x0080
+#define DCREG_WC3        0x00c0
+#define DCREG_DW16       0x0000        /* 16 bit Bus Mode                  */
+#define DCREG_DW32       0x0020        /* 32 bit Bus Mode                  */
+#define DCREG_BMS        0x0010        /* Block Mode Select                */
+#define DCREG_RFT4       0x0000        /* 4/8/16/24 bytes RX  Threshold    */
+#define DCREG_RFT8       0x0004
+#define DCREG_RFT16      0x0008
+#define DCREG_RFT24      0x000c
+#define DCREG_TFT8       0x0000        /* 8/16/24/28 bytes TX Threshold    */
+#define DCREG_TFT16      0x0001
+#define DCREG_TFT24      0x0002
+#define DCREG_TFT28      0x0003
+
+/* Receive Control Register */
+
+#define SONIC_RCREG      0x04
+#define RCREG_ERR        0x8000        /* accept damaged and collided pkts */
+#define RCREG_RNT        0x4000        /* accept packets that are < 64     */
+#define RCREG_BRD        0x2000        /* accept broadcasts                */
+#define RCREG_PRO        0x1000        /* promiscuous mode                  */
+#define RCREG_AMC        0x0800        /* accept all multicasts            */
+#define RCREG_LB_NONE    0x0000        /* no loopback                      */
+#define RCREG_LB_MAC     0x0200        /* MAC loopback                     */
+#define RCREG_LB_ENDEC   0x0400        /* ENDEC loopback                   */
+#define RCREG_LB_XVR     0x0600        /* Transceiver loopback             */
+#define RCREG_MC         0x0100        /* Multicast received               */
+#define RCREG_BC         0x0080        /* Broadcast received               */
+#define RCREG_LPKT       0x0040        /* last packet in RBA               */
+#define RCREG_CRS        0x0020        /* carrier sense present            */
+#define RCREG_COL        0x0010        /* recv'd packet with collision     */
+#define RCREG_CRCR       0x0008        /* recv'd packet with CRC error     */
+#define RCREG_FAER       0x0004        /* recv'd packet with inv. framing  */
+#define RCREG_LBK        0x0002        /* recv'd loopback packet           */
+#define RCREG_PRX        0x0001        /* recv'd packet is OK              */
+
+/* Transmit Control Register */
+
+#define SONIC_TCREG      0x06
+#define TCREG_PINT       0x8000        /* generate interrupt after TDA read */
+#define TCREG_POWC       0x4000        /* timer start out of window detect */
+#define TCREG_CRCI       0x2000        /* inhibit CRC generation           */
+#define TCREG_EXDIS      0x1000        /* disable excessive deferral timer */
+#define TCREG_EXD        0x0400        /* excessive deferral occurred       */
+#define TCREG_DEF        0x0200        /* single deferral occurred          */
+#define TCREG_NCRS       0x0100        /* no carrier detected              */
+#define TCREG_CRSL       0x0080        /* carrier lost                     */
+#define TCREG_EXC        0x0040        /* excessive collisions occurred     */
+#define TCREG_OWC        0x0020        /* out of window collision occurred  */
+#define TCREG_PMB        0x0008        /* packet monitored bad             */
+#define TCREG_FU         0x0004        /* FIFO underrun                    */
+#define TCREG_BCM        0x0002        /* byte count mismatch of fragments */
+#define TCREG_PTX        0x0001        /* packet transmitted OK            */
+
+/* Interrupt Mask Register */
+
+#define SONIC_IMREG      0x08
+#define IMREG_BREN       0x4000        /* interrupt when bus retry occurred */
+#define IMREG_HBLEN      0x2000        /* interrupt when heartbeat lost    */
+#define IMREG_LCDEN      0x1000        /* interrupt when CAM loaded        */
+#define IMREG_PINTEN     0x0800        /* interrupt when PINT in TDA set   */
+#define IMREG_PRXEN      0x0400        /* interrupt when packet received   */
+#define IMREG_PTXEN      0x0200        /* interrupt when packet was sent   */
+#define IMREG_TXEREN     0x0100        /* interrupt when send failed       */
+#define IMREG_TCEN       0x0080        /* interrupt when timer completed   */
+#define IMREG_RDEEN      0x0040        /* interrupt when RDA exhausted     */
+#define IMREG_RBEEN      0x0020        /* interrupt when RBA exhausted     */
+#define IMREG_RBAEEN     0x0010        /* interrupt when RBA too short     */
+#define IMREG_CRCEN      0x0008        /* interrupt when CRC counter rolls */
+#define IMREG_FAEEN      0x0004        /* interrupt when FAE counter rolls */
+#define IMREG_MPEN       0x0002        /* interrupt when MP counter rolls  */
+#define IMREG_RFOEN      0x0001        /* interrupt when Rx FIFO overflows */
+
+/* Interrupt Status Register */
+
+#define SONIC_ISREG      0x0a
+#define ISREG_BR         0x4000        /* bus retry occurred                */
+#define ISREG_HBL        0x2000        /* heartbeat lost                   */
+#define ISREG_LCD        0x1000        /* CAM loaded                       */
+#define ISREG_PINT       0x0800        /* PINT in TDA set                  */
+#define ISREG_PKTRX      0x0400        /* packet received                  */
+#define ISREG_TXDN       0x0200        /* packet was sent                  */
+#define ISREG_TXER       0x0100        /* send failed                      */
+#define ISREG_TC         0x0080        /* timer completed                  */
+#define ISREG_RDE        0x0040        /* RDA exhausted                    */
+#define ISREG_RBE        0x0020        /* RBA exhausted                    */
+#define ISREG_RBAE       0x0010        /* RBA too short for received frame */
+#define ISREG_CRC        0x0008        /* CRC counter rolls over           */
+#define ISREG_FAE        0x0004        /* FAE counter rolls over           */
+#define ISREG_MP         0x0002        /* MP counter rolls  over           */
+#define ISREG_RFO        0x0001        /* Rx FIFO overflows                */
+
+#define SONIC_UTDA       0x0c  /* current transmit descr address   */
+#define SONIC_CTDA       0x0e
+
+#define SONIC_URDA       0x1a  /* current receive descr address    */
+#define SONIC_CRDA       0x1c
+
+#define SONIC_CRBA0      0x1e  /* current receive buffer address   */
+#define SONIC_CRBA1      0x20
+
+#define SONIC_RBWC0      0x22  /* word count in receive buffer     */
+#define SONIC_RBWC1      0x24
+
+#define SONIC_EOBC       0x26  /* minimum space to be free in RBA  */
+
+#define SONIC_URRA       0x28  /* upper address of CDA & Recv Area */
+
+#define SONIC_RSA        0x2a  /* start of receive resource area   */
+
+#define SONIC_REA        0x2c  /* end of receive resource area     */
+
+#define SONIC_RRP        0x2e  /* resource read pointer            */
+
+#define SONIC_RWP        0x30  /* resource write pointer           */
+
+#define SONIC_CAMEPTR    0x42  /* CAM entry pointer                */
+
+#define SONIC_CAMADDR2   0x44  /* CAM address ports                */
+#define SONIC_CAMADDR1   0x46
+#define SONIC_CAMADDR0   0x48
+
+#define SONIC_CAMPTR     0x4c  /* lower address of CDA             */
+
+#define SONIC_CAMCNT     0x4e  /* # of CAM descriptors to load     */
+
+/* Data Configuration Register 2    */
+
+#define SONIC_DCREG2     0x7e
+#define DCREG2_EXPO3     0x8000        /* extended programmable outputs    */
+#define DCREG2_EXPO2     0x4000
+#define DCREG2_EXPO1     0x2000
+#define DCREG2_EXPO0     0x1000
+#define DCREG2_HD        0x0800        /* heartbeat disable                */
+#define DCREG2_JD        0x0200        /* jabber timer disable             */
+#define DCREG2_AUTO      0x0100        /* enable AUI/TP auto selection     */
+#define DCREG2_XWRAP     0x0040        /* TP transceiver loopback          */
+#define DCREG2_PH        0x0010        /* HOLD request timing              */
+#define DCREG2_PCM       0x0004        /* packet compress when matched     */
+#define DCREG2_PCNM      0x0002        /* packet compress when not matched */
+#define DCREG2_RJCM      0x0001        /* inverse packet match via CAM     */
+
+/* Board Control Register: Enable RAM, Interrupts... */
+
+#define BCMREG           0x80
+#define BCMREG_RAMEN     0x80  /* switch over to RAM               */
+#define BCMREG_IPEND     0x40  /* interrupt pending ?              */
+#define BCMREG_RESET     0x08  /* reset board                      */
+#define BCMREG_16BIT     0x04  /* adapter in 16-bit slot           */
+#define BCMREG_RAMWIN    0x02  /* enable RAM window                */
+#define BCMREG_IEN       0x01  /* interrupt enable                 */
+
+/* MAC Address PROM */
+
+#define MACADDRPROM      0x92
+
+/* structure of a CAM entry */
+
+typedef struct {
+       u32 index;              /* pointer into CAM area            */
+       u32 addr0;              /* address part (bits 0..15 used)   */
+       u32 addr1;
+       u32 addr2;
+} camentry_t;
+
+/* structure of a receive resource */
+
+typedef struct {
+       u32 startlo;            /* start address (bits 0..15 used)  */
+       u32 starthi;
+       u32 cntlo;              /* size in 16-bit quantities        */
+       u32 cnthi;
+} rra_t;
+
+/* structure of a receive descriptor */
+
+typedef struct {
+       u32 status;             /* packet status                    */
+       u32 length;             /* length in bytes                  */
+       u32 startlo;            /* start address                    */
+       u32 starthi;
+       u32 seqno;              /* frame sequence                   */
+       u32 link;               /* pointer to next descriptor       */
+       /* bit 0 = EOL                      */
+       u32 inuse;              /* !=0 --> free for SONIC to write  */
+} rda_t;
+
+/* structure of a transmit descriptor */
+
+typedef struct {
+       u32 status;             /* transmit status                  */
+       u32 config;             /* value for TCR                    */
+       u32 length;             /* total length                     */
+       u32 fragcount;          /* number of fragments              */
+       u32 startlo;            /* start address of fragment        */
+       u32 starthi;
+       u32 fraglength;         /* length of this fragment          */
+       /* more address/length triplets may */
+       /* follow here                      */
+       u32 link;               /* pointer to next descriptor       */
+       /* bit 0 = EOL                      */
+} tda_t;
+
+#endif                         /* _IBM_LANA_DRIVER_ */
+
+#endif /* _IBM_LANA_INCLUDE_ */
diff --git a/drivers/net/ethernet/natsemi/jazzsonic.c b/drivers/net/ethernet/natsemi/jazzsonic.c
new file mode 100644 (file)
index 0000000..949c1f9
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * jazzsonic.c
+ *
+ * (C) 2005 Finn Thain
+ *
+ * Converted to DMA API, and (from the mac68k project) introduced
+ * dhd's support for 16-bit cards.
+ *
+ * (C) 1996,1998 by Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * This driver is based on work from Andreas Busse, but most of
+ * the code is rewritten.
+ *
+ * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
+ *
+ * A driver for the onboard Sonic ethernet controller on Mips Jazz
+ * systems (Acer Pica-61, Mips Magnum 4000, Olivetti M700 and
+ * perhaps others, too)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#include <asm/bootinfo.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/jazz.h>
+#include <asm/jazzdma.h>
+
+static char jazz_sonic_string[] = "jazzsonic";
+
+#define SONIC_MEM_SIZE 0x100
+
+#include "sonic.h"
+
+/*
+ * Macros to access SONIC registers
+ */
+#define SONIC_READ(reg) (*((volatile unsigned int *)dev->base_addr+reg))
+
+#define SONIC_WRITE(reg,val)                                           \
+do {                                                                   \
+       *((volatile unsigned int *)dev->base_addr+(reg)) = (val);               \
+} while (0)
+
+
+/* use 0 for production, 1 for verification, >1 for debug */
+#ifdef SONIC_DEBUG
+static unsigned int sonic_debug = SONIC_DEBUG;
+#else
+static unsigned int sonic_debug = 1;
+#endif
+
+/*
+ * We cannot use station (ethernet) address prefixes to detect the
+ * sonic controller since these are board manufacturer depended.
+ * So we check for known Silicon Revision IDs instead.
+ */
+static unsigned short known_revisions[] =
+{
+       0x04,                   /* Mips Magnum 4000 */
+       0xffff                  /* end of list */
+};
+
+static int jazzsonic_open(struct net_device* dev)
+{
+       int retval;
+
+       retval = request_irq(dev->irq, sonic_interrupt, IRQF_DISABLED,
+                               "sonic", dev);
+       if (retval) {
+               printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+                               dev->name, dev->irq);
+               return retval;
+       }
+
+       retval = sonic_open(dev);
+       if (retval)
+               free_irq(dev->irq, dev);
+       return retval;
+}
+
+static int jazzsonic_close(struct net_device* dev)
+{
+       int err;
+       err = sonic_close(dev);
+       free_irq(dev->irq, dev);
+       return err;
+}
+
+static const struct net_device_ops sonic_netdev_ops = {
+       .ndo_open               = jazzsonic_open,
+       .ndo_stop               = jazzsonic_close,
+       .ndo_start_xmit         = sonic_send_packet,
+       .ndo_get_stats          = sonic_get_stats,
+       .ndo_set_multicast_list = sonic_multicast_list,
+       .ndo_tx_timeout         = sonic_tx_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+};
+
+static int __devinit sonic_probe1(struct net_device *dev)
+{
+       static unsigned version_printed;
+       unsigned int silicon_revision;
+       unsigned int val;
+       struct sonic_local *lp = netdev_priv(dev);
+       int err = -ENODEV;
+       int i;
+
+       if (!request_mem_region(dev->base_addr, SONIC_MEM_SIZE, jazz_sonic_string))
+               return -EBUSY;
+
+       /*
+        * get the Silicon Revision ID. If this is one of the known
+        * one assume that we found a SONIC ethernet controller at
+        * the expected location.
+        */
+       silicon_revision = SONIC_READ(SONIC_SR);
+       if (sonic_debug > 1)
+               printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);
+
+       i = 0;
+       while (known_revisions[i] != 0xffff &&
+              known_revisions[i] != silicon_revision)
+               i++;
+
+       if (known_revisions[i] == 0xffff) {
+               printk("SONIC ethernet controller not found (0x%4x)\n",
+                      silicon_revision);
+               goto out;
+       }
+
+       if (sonic_debug  &&  version_printed++ == 0)
+               printk(version);
+
+       printk(KERN_INFO "%s: Sonic ethernet found at 0x%08lx, ",
+              dev_name(lp->device), dev->base_addr);
+
+       /*
+        * Put the sonic into software reset, then
+        * retrieve and print the ethernet address.
+        */
+       SONIC_WRITE(SONIC_CMD,SONIC_CR_RST);
+       SONIC_WRITE(SONIC_CEP,0);
+       for (i=0; i<3; i++) {
+               val = SONIC_READ(SONIC_CAP0-i);
+               dev->dev_addr[i*2] = val;
+               dev->dev_addr[i*2+1] = val >> 8;
+       }
+
+       err = -ENOMEM;
+
+       /* Initialize the device structure. */
+
+       lp->dma_bitmode = SONIC_BITMODE32;
+
+       /* Allocate the entire chunk of memory for the descriptors.
+           Note that this cannot cross a 64K boundary. */
+       if ((lp->descriptors = dma_alloc_coherent(lp->device,
+                               SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                               &lp->descriptors_laddr, GFP_KERNEL)) == NULL) {
+               printk(KERN_ERR "%s: couldn't alloc DMA memory for descriptors.\n",
+                      dev_name(lp->device));
+               goto out;
+       }
+
+       /* Now set up the pointers to point to the appropriate places */
+       lp->cda = lp->descriptors;
+       lp->tda = lp->cda + (SIZEOF_SONIC_CDA
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+
+       lp->cda_laddr = lp->descriptors_laddr;
+       lp->tda_laddr = lp->cda_laddr + (SIZEOF_SONIC_CDA
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rda_laddr = lp->tda_laddr + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rra_laddr = lp->rda_laddr + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+
+       dev->netdev_ops = &sonic_netdev_ops;
+       dev->watchdog_timeo = TX_TIMEOUT;
+
+       /*
+        * clear tally counter
+        */
+       SONIC_WRITE(SONIC_CRCT,0xffff);
+       SONIC_WRITE(SONIC_FAET,0xffff);
+       SONIC_WRITE(SONIC_MPT,0xffff);
+
+       return 0;
+out:
+       release_mem_region(dev->base_addr, SONIC_MEM_SIZE);
+       return err;
+}
+
+/*
+ * Probe for a SONIC ethernet controller on a Mips Jazz board.
+ * Actually probing is superfluous but we're paranoid.
+ */
+static int __devinit jazz_sonic_probe(struct platform_device *pdev)
+{
+       struct net_device *dev;
+       struct sonic_local *lp;
+       struct resource *res;
+       int err = 0;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+
+       dev = alloc_etherdev(sizeof(struct sonic_local));
+       if (!dev)
+               return -ENOMEM;
+
+       lp = netdev_priv(dev);
+       lp->device = &pdev->dev;
+       SET_NETDEV_DEV(dev, &pdev->dev);
+       platform_set_drvdata(pdev, dev);
+
+       netdev_boot_setup_check(dev);
+
+       dev->base_addr = res->start;
+       dev->irq = platform_get_irq(pdev, 0);
+       err = sonic_probe1(dev);
+       if (err)
+               goto out;
+       err = register_netdev(dev);
+       if (err)
+               goto out1;
+
+       printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
+
+       return 0;
+
+out1:
+       release_mem_region(dev->base_addr, SONIC_MEM_SIZE);
+out:
+       free_netdev(dev);
+
+       return err;
+}
+
+MODULE_DESCRIPTION("Jazz SONIC ethernet driver");
+module_param(sonic_debug, int, 0);
+MODULE_PARM_DESC(sonic_debug, "jazzsonic debug level (1-4)");
+MODULE_ALIAS("platform:jazzsonic");
+
+#include "sonic.c"
+
+static int __devexit jazz_sonic_device_remove (struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct sonic_local* lp = netdev_priv(dev);
+
+       unregister_netdev(dev);
+       dma_free_coherent(lp->device, SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                         lp->descriptors, lp->descriptors_laddr);
+       release_mem_region(dev->base_addr, SONIC_MEM_SIZE);
+       free_netdev(dev);
+
+       return 0;
+}
+
+static struct platform_driver jazz_sonic_driver = {
+       .probe  = jazz_sonic_probe,
+       .remove = __devexit_p(jazz_sonic_device_remove),
+       .driver = {
+               .name   = jazz_sonic_string,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init jazz_sonic_init_module(void)
+{
+       return platform_driver_register(&jazz_sonic_driver);
+}
+
+static void __exit jazz_sonic_cleanup_module(void)
+{
+       platform_driver_unregister(&jazz_sonic_driver);
+}
+
+module_init(jazz_sonic_init_module);
+module_exit(jazz_sonic_cleanup_module);
diff --git a/drivers/net/ethernet/natsemi/macsonic.c b/drivers/net/ethernet/natsemi/macsonic.c
new file mode 100644 (file)
index 0000000..c93679e
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * macsonic.c
+ *
+ * (C) 2005 Finn Thain
+ *
+ * Converted to DMA API, converted to unified driver model, made it work as
+ * a module again, and from the mac68k project, introduced more 32-bit cards
+ * and dhd's support for 16-bit cards.
+ *
+ * (C) 1998 Alan Cox
+ *
+ * Debugging Andreas Ehliar, Michael Schmitz
+ *
+ * Based on code
+ * (C) 1996 by Thomas Bogendoerfer (tsbogend@bigbug.franken.de)
+ *
+ * This driver is based on work from Andreas Busse, but most of
+ * the code is rewritten.
+ *
+ * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
+ *
+ * A driver for the Mac onboard Sonic ethernet chip.
+ *
+ * 98/12/21 MSch: judged from tests on Q800, it's basically working,
+ *               but eating up both receive and transmit resources
+ *               and duplicating packets. Needs more testing.
+ *
+ * 99/01/03 MSch: upgraded to version 0.92 of the core driver, fixed.
+ *
+ * 00/10/31 sammy@oh.verio.com: Updated driver for 2.4 kernels, fixed problems
+ *          on centris.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/nubus.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitrev.h>
+#include <linux/slab.h>
+
+#include <asm/bootinfo.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/hwtest.h>
+#include <asm/dma.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_via.h>
+
+static char mac_sonic_string[] = "macsonic";
+
+#include "sonic.h"
+
+/* These should basically be bus-size and endian independent (since
+   the SONIC is at least smart enough that it uses the same endianness
+   as the host, unlike certain less enlightened Macintosh NICs) */
+#define SONIC_READ(reg) (nubus_readw(dev->base_addr + (reg * 4) \
+             + lp->reg_offset))
+#define SONIC_WRITE(reg,val) (nubus_writew(val, dev->base_addr + (reg * 4) \
+             + lp->reg_offset))
+
+/* use 0 for production, 1 for verification, >1 for debug */
+#ifdef SONIC_DEBUG
+static unsigned int sonic_debug = SONIC_DEBUG;
+#else
+static unsigned int sonic_debug = 1;
+#endif
+
+static int sonic_version_printed;
+
+/* For onboard SONIC */
+#define ONBOARD_SONIC_REGISTERS        0x50F0A000
+#define ONBOARD_SONIC_PROM_BASE        0x50f08000
+
+enum macsonic_type {
+       MACSONIC_DUODOCK,
+       MACSONIC_APPLE,
+       MACSONIC_APPLE16,
+       MACSONIC_DAYNA,
+       MACSONIC_DAYNALINK
+};
+
+/* For the built-in SONIC in the Duo Dock */
+#define DUODOCK_SONIC_REGISTERS 0xe10000
+#define DUODOCK_SONIC_PROM_BASE 0xe12000
+
+/* For Apple-style NuBus SONIC */
+#define APPLE_SONIC_REGISTERS  0
+#define APPLE_SONIC_PROM_BASE  0x40000
+
+/* Daynalink LC SONIC */
+#define DAYNALINK_PROM_BASE 0x400000
+
+/* For Dayna-style NuBus SONIC (haven't seen one yet) */
+#define DAYNA_SONIC_REGISTERS   0x180000
+/* This is what OpenBSD says.  However, this is definitely in NuBus
+   ROM space so we should be able to get it by walking the NuBus
+   resource directories */
+#define DAYNA_SONIC_MAC_ADDR   0xffe004
+
+#define SONIC_READ_PROM(addr) nubus_readb(prom_addr+addr)
+
+/*
+ * For reversing the PROM address
+ */
+
+static inline void bit_reverse_addr(unsigned char addr[6])
+{
+       int i;
+
+       for(i = 0; i < 6; i++)
+               addr[i] = bitrev8(addr[i]);
+}
+
+static irqreturn_t macsonic_interrupt(int irq, void *dev_id)
+{
+       irqreturn_t result;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       result = sonic_interrupt(irq, dev_id);
+       local_irq_restore(flags);
+       return result;
+}
+
+static int macsonic_open(struct net_device* dev)
+{
+       int retval;
+
+       retval = request_irq(dev->irq, sonic_interrupt, IRQ_FLG_FAST,
+                               "sonic", dev);
+       if (retval) {
+               printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+                               dev->name, dev->irq);
+               goto err;
+       }
+       /* Under the A/UX interrupt scheme, the onboard SONIC interrupt comes
+        * in at priority level 3. However, we sometimes get the level 2 inter-
+        * rupt as well, which must prevent re-entrance of the sonic handler.
+        */
+       if (dev->irq == IRQ_AUTO_3) {
+               retval = request_irq(IRQ_NUBUS_9, macsonic_interrupt,
+                                       IRQ_FLG_FAST, "sonic", dev);
+               if (retval) {
+                       printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+                                       dev->name, IRQ_NUBUS_9);
+                       goto err_irq;
+               }
+       }
+       retval = sonic_open(dev);
+       if (retval)
+               goto err_irq_nubus;
+       return 0;
+
+err_irq_nubus:
+       if (dev->irq == IRQ_AUTO_3)
+               free_irq(IRQ_NUBUS_9, dev);
+err_irq:
+       free_irq(dev->irq, dev);
+err:
+       return retval;
+}
+
+static int macsonic_close(struct net_device* dev)
+{
+       int err;
+       err = sonic_close(dev);
+       free_irq(dev->irq, dev);
+       if (dev->irq == IRQ_AUTO_3)
+               free_irq(IRQ_NUBUS_9, dev);
+       return err;
+}
+
+static const struct net_device_ops macsonic_netdev_ops = {
+       .ndo_open               = macsonic_open,
+       .ndo_stop               = macsonic_close,
+       .ndo_start_xmit         = sonic_send_packet,
+       .ndo_set_multicast_list = sonic_multicast_list,
+       .ndo_tx_timeout         = sonic_tx_timeout,
+       .ndo_get_stats          = sonic_get_stats,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+};
+
+static int __devinit macsonic_init(struct net_device *dev)
+{
+       struct sonic_local* lp = netdev_priv(dev);
+
+       /* Allocate the entire chunk of memory for the descriptors.
+           Note that this cannot cross a 64K boundary. */
+       if ((lp->descriptors = dma_alloc_coherent(lp->device,
+                   SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                   &lp->descriptors_laddr, GFP_KERNEL)) == NULL) {
+               printk(KERN_ERR "%s: couldn't alloc DMA memory for descriptors.\n",
+                      dev_name(lp->device));
+               return -ENOMEM;
+       }
+
+       /* Now set up the pointers to point to the appropriate places */
+       lp->cda = lp->descriptors;
+       lp->tda = lp->cda + (SIZEOF_SONIC_CDA
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+
+       lp->cda_laddr = lp->descriptors_laddr;
+       lp->tda_laddr = lp->cda_laddr + (SIZEOF_SONIC_CDA
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rda_laddr = lp->tda_laddr + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rra_laddr = lp->rda_laddr + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+
+       dev->netdev_ops = &macsonic_netdev_ops;
+       dev->watchdog_timeo = TX_TIMEOUT;
+
+       /*
+        * clear tally counter
+        */
+       SONIC_WRITE(SONIC_CRCT, 0xffff);
+       SONIC_WRITE(SONIC_FAET, 0xffff);
+       SONIC_WRITE(SONIC_MPT, 0xffff);
+
+       return 0;
+}
+
+#define INVALID_MAC(mac) (memcmp(mac, "\x08\x00\x07", 3) && \
+                          memcmp(mac, "\x00\xA0\x40", 3) && \
+                          memcmp(mac, "\x00\x80\x19", 3) && \
+                          memcmp(mac, "\x00\x05\x02", 3))
+
+static void __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       const int prom_addr = ONBOARD_SONIC_PROM_BASE;
+       unsigned short val;
+
+       /*
+        * On NuBus boards we can sometimes look in the ROM resources.
+        * No such luck for comm-slot/onboard.
+        * On the PowerBook 520, the PROM base address is a mystery.
+        */
+       if (hwreg_present((void *)prom_addr)) {
+               int i;
+
+               for (i = 0; i < 6; i++)
+                       dev->dev_addr[i] = SONIC_READ_PROM(i);
+               if (!INVALID_MAC(dev->dev_addr))
+                       return;
+
+               /*
+                * Most of the time, the address is bit-reversed. The NetBSD
+                * source has a rather long and detailed historical account of
+                * why this is so.
+                */
+               bit_reverse_addr(dev->dev_addr);
+               if (!INVALID_MAC(dev->dev_addr))
+                       return;
+
+               /*
+                * If we still have what seems to be a bogus address, we'll
+                * look in the CAM. The top entry should be ours.
+                */
+               printk(KERN_WARNING "macsonic: MAC address in PROM seems "
+                                   "to be invalid, trying CAM\n");
+       } else {
+               printk(KERN_WARNING "macsonic: cannot read MAC address from "
+                                   "PROM, trying CAM\n");
+       }
+
+       /* This only works if MacOS has already initialized the card. */
+
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+       SONIC_WRITE(SONIC_CEP, 15);
+
+       val = SONIC_READ(SONIC_CAP2);
+       dev->dev_addr[5] = val >> 8;
+       dev->dev_addr[4] = val & 0xff;
+       val = SONIC_READ(SONIC_CAP1);
+       dev->dev_addr[3] = val >> 8;
+       dev->dev_addr[2] = val & 0xff;
+       val = SONIC_READ(SONIC_CAP0);
+       dev->dev_addr[1] = val >> 8;
+       dev->dev_addr[0] = val & 0xff;
+
+       if (!INVALID_MAC(dev->dev_addr))
+               return;
+
+       /* Still nonsense ... messed up someplace! */
+
+       printk(KERN_WARNING "macsonic: MAC address in CAM entry 15 "
+                           "seems invalid, will use a random MAC\n");
+       random_ether_addr(dev->dev_addr);
+}
+
+static int __devinit mac_onboard_sonic_probe(struct net_device *dev)
+{
+       /* Bwahahaha */
+       static int once_is_more_than_enough;
+       struct sonic_local* lp = netdev_priv(dev);
+       int sr;
+       int commslot = 0;
+
+       if (once_is_more_than_enough)
+               return -ENODEV;
+       once_is_more_than_enough = 1;
+
+       if (!MACH_IS_MAC)
+               return -ENODEV;
+
+       if (macintosh_config->ether_type != MAC_ETHER_SONIC)
+               return -ENODEV;
+
+       printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. ");
+
+       /* Bogus probing, on the models which may or may not have
+          Ethernet (BTW, the Ethernet *is* always at the same
+          address, and nothing else lives there, at least if Apple's
+          documentation is to be believed) */
+       if (macintosh_config->ident == MAC_MODEL_Q630 ||
+           macintosh_config->ident == MAC_MODEL_P588 ||
+           macintosh_config->ident == MAC_MODEL_P575 ||
+           macintosh_config->ident == MAC_MODEL_C610) {
+               unsigned long flags;
+               int card_present;
+
+               local_irq_save(flags);
+               card_present = hwreg_present((void*)ONBOARD_SONIC_REGISTERS);
+               local_irq_restore(flags);
+
+               if (!card_present) {
+                       printk("none.\n");
+                       return -ENODEV;
+               }
+               commslot = 1;
+       }
+
+       printk("yes\n");
+
+       /* Danger!  My arms are flailing wildly!  You *must* set lp->reg_offset
+        * and dev->base_addr before using SONIC_READ() or SONIC_WRITE() */
+       dev->base_addr = ONBOARD_SONIC_REGISTERS;
+       if (via_alt_mapping)
+               dev->irq = IRQ_AUTO_3;
+       else
+               dev->irq = IRQ_NUBUS_9;
+
+       if (!sonic_version_printed) {
+               printk(KERN_INFO "%s", version);
+               sonic_version_printed = 1;
+       }
+       printk(KERN_INFO "%s: onboard / comm-slot SONIC at 0x%08lx\n",
+              dev_name(lp->device), dev->base_addr);
+
+       /* The PowerBook's SONIC is 16 bit always. */
+       if (macintosh_config->ident == MAC_MODEL_PB520) {
+               lp->reg_offset = 0;
+               lp->dma_bitmode = SONIC_BITMODE16;
+               sr = SONIC_READ(SONIC_SR);
+       } else if (commslot) {
+               /* Some of the comm-slot cards are 16 bit.  But some
+                  of them are not.  The 32-bit cards use offset 2 and
+                  have known revisions, we try reading the revision
+                  register at offset 2, if we don't get a known revision
+                  we assume 16 bit at offset 0.  */
+               lp->reg_offset = 2;
+               lp->dma_bitmode = SONIC_BITMODE16;
+
+               sr = SONIC_READ(SONIC_SR);
+               if (sr == 0x0004 || sr == 0x0006 || sr == 0x0100 || sr == 0x0101)
+                       /* 83932 is 0x0004 or 0x0006, 83934 is 0x0100 or 0x0101 */
+                       lp->dma_bitmode = SONIC_BITMODE32;
+               else {
+                       lp->dma_bitmode = SONIC_BITMODE16;
+                       lp->reg_offset = 0;
+                       sr = SONIC_READ(SONIC_SR);
+               }
+       } else {
+               /* All onboard cards are at offset 2 with 32 bit DMA. */
+               lp->reg_offset = 2;
+               lp->dma_bitmode = SONIC_BITMODE32;
+               sr = SONIC_READ(SONIC_SR);
+       }
+       printk(KERN_INFO
+              "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
+              dev_name(lp->device), sr, lp->dma_bitmode?32:16, lp->reg_offset);
+
+#if 0 /* This is sometimes useful to find out how MacOS configured the card. */
+       printk(KERN_INFO "%s: DCR: 0x%04x, DCR2: 0x%04x\n", dev_name(lp->device),
+              SONIC_READ(SONIC_DCR) & 0xffff, SONIC_READ(SONIC_DCR2) & 0xffff);
+#endif
+
+       /* Software reset, then initialize control registers. */
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+
+       SONIC_WRITE(SONIC_DCR, SONIC_DCR_EXBUS | SONIC_DCR_BMS |
+                              SONIC_DCR_RFT1  | SONIC_DCR_TFT0 |
+                              (lp->dma_bitmode ? SONIC_DCR_DW : 0));
+
+       /* This *must* be written back to in order to restore the
+        * extended programmable output bits, as it may not have been
+        * initialised since the hardware reset. */
+       SONIC_WRITE(SONIC_DCR2, 0);
+
+       /* Clear *and* disable interrupts to be on the safe side */
+       SONIC_WRITE(SONIC_IMR, 0);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+
+       /* Now look for the MAC address. */
+       mac_onboard_sonic_ethernet_addr(dev);
+
+       /* Shared init code */
+       return macsonic_init(dev);
+}
+
+static int __devinit mac_nubus_sonic_ethernet_addr(struct net_device *dev,
+                                               unsigned long prom_addr,
+                                               int id)
+{
+       int i;
+       for(i = 0; i < 6; i++)
+               dev->dev_addr[i] = SONIC_READ_PROM(i);
+
+       /* Some of the addresses are bit-reversed */
+       if (id != MACSONIC_DAYNA)
+               bit_reverse_addr(dev->dev_addr);
+
+       return 0;
+}
+
+static int __devinit macsonic_ident(struct nubus_dev *ndev)
+{
+       if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC &&
+           ndev->dr_sw == NUBUS_DRSW_SONIC_LC)
+               return MACSONIC_DAYNALINK;
+       if (ndev->dr_hw == NUBUS_DRHW_SONIC &&
+           ndev->dr_sw == NUBUS_DRSW_APPLE) {
+               /* There has to be a better way to do this... */
+               if (strstr(ndev->board->name, "DuoDock"))
+                       return MACSONIC_DUODOCK;
+               else
+                       return MACSONIC_APPLE;
+       }
+
+       if (ndev->dr_hw == NUBUS_DRHW_SMC9194 &&
+           ndev->dr_sw == NUBUS_DRSW_DAYNA)
+               return MACSONIC_DAYNA;
+
+       if (ndev->dr_hw == NUBUS_DRHW_APPLE_SONIC_LC &&
+           ndev->dr_sw == 0) { /* huh? */
+               return MACSONIC_APPLE16;
+       }
+       return -1;
+}
+
+static int __devinit mac_nubus_sonic_probe(struct net_device *dev)
+{
+       static int slots;
+       struct nubus_dev* ndev = NULL;
+       struct sonic_local* lp = netdev_priv(dev);
+       unsigned long base_addr, prom_addr;
+       u16 sonic_dcr;
+       int id = -1;
+       int reg_offset, dma_bitmode;
+
+       /* Find the first SONIC that hasn't been initialized already */
+       while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK,
+                                      NUBUS_TYPE_ETHERNET, ndev)) != NULL)
+       {
+               /* Have we seen it already? */
+               if (slots & (1<<ndev->board->slot))
+                       continue;
+               slots |= 1<<ndev->board->slot;
+
+               /* Is it one of ours? */
+               if ((id = macsonic_ident(ndev)) != -1)
+                       break;
+       }
+
+       if (ndev == NULL)
+               return -ENODEV;
+
+       switch (id) {
+       case MACSONIC_DUODOCK:
+               base_addr = ndev->board->slot_addr + DUODOCK_SONIC_REGISTERS;
+               prom_addr = ndev->board->slot_addr + DUODOCK_SONIC_PROM_BASE;
+               sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT0 | SONIC_DCR_RFT1 |
+                           SONIC_DCR_TFT0;
+               reg_offset = 2;
+               dma_bitmode = SONIC_BITMODE32;
+               break;
+       case MACSONIC_APPLE:
+               base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
+               prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
+               sonic_dcr = SONIC_DCR_BMS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0;
+               reg_offset = 0;
+               dma_bitmode = SONIC_BITMODE32;
+               break;
+       case MACSONIC_APPLE16:
+               base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
+               prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
+               sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 |
+                           SONIC_DCR_PO1 | SONIC_DCR_BMS;
+               reg_offset = 0;
+               dma_bitmode = SONIC_BITMODE16;
+               break;
+       case MACSONIC_DAYNALINK:
+               base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
+               prom_addr = ndev->board->slot_addr + DAYNALINK_PROM_BASE;
+               sonic_dcr = SONIC_DCR_RFT1 | SONIC_DCR_TFT0 |
+                           SONIC_DCR_PO1 | SONIC_DCR_BMS;
+               reg_offset = 0;
+               dma_bitmode = SONIC_BITMODE16;
+               break;
+       case MACSONIC_DAYNA:
+               base_addr = ndev->board->slot_addr + DAYNA_SONIC_REGISTERS;
+               prom_addr = ndev->board->slot_addr + DAYNA_SONIC_MAC_ADDR;
+               sonic_dcr = SONIC_DCR_BMS |
+                           SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_PO1;
+               reg_offset = 0;
+               dma_bitmode = SONIC_BITMODE16;
+               break;
+       default:
+               printk(KERN_ERR "macsonic: WTF, id is %d\n", id);
+               return -ENODEV;
+       }
+
+       /* Danger!  My arms are flailing wildly!  You *must* set lp->reg_offset
+        * and dev->base_addr before using SONIC_READ() or SONIC_WRITE() */
+       dev->base_addr = base_addr;
+       lp->reg_offset = reg_offset;
+       lp->dma_bitmode = dma_bitmode;
+       dev->irq = SLOT2IRQ(ndev->board->slot);
+
+       if (!sonic_version_printed) {
+               printk(KERN_INFO "%s", version);
+               sonic_version_printed = 1;
+       }
+       printk(KERN_INFO "%s: %s in slot %X\n",
+              dev_name(lp->device), ndev->board->name, ndev->board->slot);
+       printk(KERN_INFO "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
+              dev_name(lp->device), SONIC_READ(SONIC_SR), dma_bitmode?32:16, reg_offset);
+
+#if 0 /* This is sometimes useful to find out how MacOS configured the card. */
+       printk(KERN_INFO "%s: DCR: 0x%04x, DCR2: 0x%04x\n", dev_name(lp->device),
+              SONIC_READ(SONIC_DCR) & 0xffff, SONIC_READ(SONIC_DCR2) & 0xffff);
+#endif
+
+       /* Software reset, then initialize control registers. */
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+       SONIC_WRITE(SONIC_DCR, sonic_dcr | (dma_bitmode ? SONIC_DCR_DW : 0));
+       /* This *must* be written back to in order to restore the
+        * extended programmable output bits, since it may not have been
+        * initialised since the hardware reset. */
+       SONIC_WRITE(SONIC_DCR2, 0);
+
+       /* Clear *and* disable interrupts to be on the safe side */
+       SONIC_WRITE(SONIC_IMR, 0);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+
+       /* Now look for the MAC address. */
+       if (mac_nubus_sonic_ethernet_addr(dev, prom_addr, id) != 0)
+               return -ENODEV;
+
+       /* Shared init code */
+       return macsonic_init(dev);
+}
+
+static int __devinit mac_sonic_probe(struct platform_device *pdev)
+{
+       struct net_device *dev;
+       struct sonic_local *lp;
+       int err;
+
+       dev = alloc_etherdev(sizeof(struct sonic_local));
+       if (!dev)
+               return -ENOMEM;
+
+       lp = netdev_priv(dev);
+       lp->device = &pdev->dev;
+       SET_NETDEV_DEV(dev, &pdev->dev);
+       platform_set_drvdata(pdev, dev);
+
+       /* This will catch fatal stuff like -ENOMEM as well as success */
+       err = mac_onboard_sonic_probe(dev);
+       if (err == 0)
+               goto found;
+       if (err != -ENODEV)
+               goto out;
+       err = mac_nubus_sonic_probe(dev);
+       if (err)
+               goto out;
+found:
+       err = register_netdev(dev);
+       if (err)
+               goto out;
+
+       printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
+
+       return 0;
+
+out:
+       free_netdev(dev);
+
+       return err;
+}
+
+MODULE_DESCRIPTION("Macintosh SONIC ethernet driver");
+module_param(sonic_debug, int, 0);
+MODULE_PARM_DESC(sonic_debug, "macsonic debug level (1-4)");
+MODULE_ALIAS("platform:macsonic");
+
+#include "sonic.c"
+
+static int __devexit mac_sonic_device_remove (struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct sonic_local* lp = netdev_priv(dev);
+
+       unregister_netdev(dev);
+       dma_free_coherent(lp->device, SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                         lp->descriptors, lp->descriptors_laddr);
+       free_netdev(dev);
+
+       return 0;
+}
+
+static struct platform_driver mac_sonic_driver = {
+       .probe  = mac_sonic_probe,
+       .remove = __devexit_p(mac_sonic_device_remove),
+       .driver = {
+               .name   = mac_sonic_string,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init mac_sonic_init_module(void)
+{
+       return platform_driver_register(&mac_sonic_driver);
+}
+
+static void __exit mac_sonic_cleanup_module(void)
+{
+       platform_driver_unregister(&mac_sonic_driver);
+}
+
+module_init(mac_sonic_init_module);
+module_exit(mac_sonic_cleanup_module);
diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c
new file mode 100644 (file)
index 0000000..2962cc6
--- /dev/null
@@ -0,0 +1,3370 @@
+/* natsemi.c: A Linux PCI Ethernet driver for the NatSemi DP8381x series. */
+/*
+       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.
+       Drivers based on or derived from this code fall under the GPL and must
+       retain the authorship, copyright and license notice.  This file is not
+       a complete program and may only be used when the entire operating
+       system is licensed under the GPL.  License for under other terms may be
+       available.  Contact the original author for details.
+
+       The original author may be reached as becker@scyld.com, or at
+       Scyld Computing Corporation
+       410 Severn Ave., Suite 210
+       Annapolis MD 21403
+
+       Support information and updates available at
+       http://www.scyld.com/network/netsemi.html
+       [link no longer provides useful info -jgarzik]
+
+
+       TODO:
+       * big endian support with CFG:BEM instead of cpu_to_le32
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/delay.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/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define DRV_NAME       "natsemi"
+#define DRV_VERSION    "2.1"
+#define DRV_RELDATE    "Sept 11, 2006"
+
+#define RX_OFFSET      2
+
+/* Updated to recommendations in pci-skeleton v2.03. */
+
+/* The user-configurable values.
+   These may be modified when a driver module is loaded.*/
+
+#define NATSEMI_DEF_MSG                (NETIF_MSG_DRV          | \
+                                NETIF_MSG_LINK         | \
+                                NETIF_MSG_WOL          | \
+                                NETIF_MSG_RX_ERR       | \
+                                NETIF_MSG_TX_ERR)
+static int debug = -1;
+
+static int mtu;
+
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+   This chip uses a 512 element hash table based on the Ethernet CRC.  */
+static const int multicast_filter_limit = 100;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1518 effectively disables this feature. */
+static int rx_copybreak;
+
+static int dspcfg_workaround = 1;
+
+/* Used to pass the media type, etc.
+   Both 'options[]' and 'full_duplex[]' should exist for driver
+   interoperability.
+   The media type is usually passed in 'options[]'.
+*/
+#define MAX_UNITS 8            /* More are supported, limit only on options */
+static int options[MAX_UNITS];
+static int full_duplex[MAX_UNITS];
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE   16
+#define TX_QUEUE_LEN   10 /* Limit ring entries actually used, min 4. */
+#define RX_RING_SIZE   32
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (2*HZ)
+
+#define NATSEMI_HW_TIMEOUT     400
+#define NATSEMI_TIMER_FREQ     5*HZ
+#define NATSEMI_PG0_NREGS      64
+#define NATSEMI_RFDR_NREGS     8
+#define NATSEMI_PG1_NREGS      4
+#define NATSEMI_NREGS          (NATSEMI_PG0_NREGS + NATSEMI_RFDR_NREGS + \
+                                NATSEMI_PG1_NREGS)
+#define NATSEMI_REGS_VER       1 /* v1 added RFDR registers */
+#define NATSEMI_REGS_SIZE      (NATSEMI_NREGS * sizeof(u32))
+
+/* 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 const char version[] __devinitconst =
+  KERN_INFO DRV_NAME " dp8381x driver, version "
+      DRV_VERSION ", " DRV_RELDATE "\n"
+  "  originally by Donald Becker <becker@scyld.com>\n"
+  "  2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n";
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
+MODULE_LICENSE("GPL");
+
+module_param(mtu, int, 0);
+module_param(debug, int, 0);
+module_param(rx_copybreak, int, 0);
+module_param(dspcfg_workaround, 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,
+       "DP8381x copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(dspcfg_workaround, "DP8381x: control DspCfg workaround");
+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)");
+
+/*
+                               Theory of Operation
+
+I. Board Compatibility
+
+This driver is designed for National Semiconductor DP83815 PCI Ethernet NIC.
+It also works with other chips in in the DP83810 series.
+
+II. Board-specific settings
+
+This driver requires the PCI interrupt line to be valid.
+It honors the EEPROM-set values.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE.
+The NatSemi design uses a 'next descriptor' pointer that the driver forms
+into a list.
+
+IIIb/c. Transmit/Receive Structure
+
+This driver uses a zero-copy receive and transmit scheme.
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the chip as receive data
+buffers.  When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack.  Buffers consumed this way are replaced by newly allocated
+skbuffs in a later phase of receives.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  New boards are typically used in generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets.  When copying is done, the cost is usually mitigated by using
+a combined copy/checksum routine.  Copying also preloads the cache, which is
+most useful with small frames.
+
+A subtle aspect of the operation is that unaligned buffers are not permitted
+by the hardware.  Thus the IP header at offset 14 in an ethernet frame isn't
+longword aligned for further processing.  On copies frames are put into the
+skbuff at an offset of "+2", 16-byte aligning the IP header.
+
+IIId. Synchronization
+
+Most operations are synchronized on the np->lock irq spinlock, except the
+receive and transmit paths which are synchronised using a combination of
+hardware descriptor ownership, disabling interrupts and NAPI poll scheduling.
+
+IVb. References
+
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
+Datasheet is available from:
+http://www.national.com/pf/DP/DP83815.html
+
+IVc. Errata
+
+None characterised.
+*/
+
+
+
+/*
+ * 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
+
+enum {
+       MII_MCTRL       = 0x15,         /* mode control register */
+       MII_FX_SEL      = 0x0001,       /* 100BASE-FX (fiber) */
+       MII_EN_SCRM     = 0x0004,       /* enable scrambler (tp) */
+};
+
+enum {
+       NATSEMI_FLAG_IGNORE_PHY         = 0x1,
+};
+
+/* array of board data directly indexed by pci_tbl[x].driver_data */
+static struct {
+       const char *name;
+       unsigned long flags;
+       unsigned int eeprom_size;
+} natsemi_pci_info[] __devinitdata = {
+       { "Aculab E1/T1 PMXc cPCI carrier card", NATSEMI_FLAG_IGNORE_PHY, 128 },
+       { "NatSemi DP8381[56]", 0, 24 },
+};
+
+static DEFINE_PCI_DEVICE_TABLE(natsemi_pci_tbl) = {
+       { PCI_VENDOR_ID_NS, 0x0020, 0x12d9,     0x000c,     0, 0, 0 },
+       { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
+       { }     /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl);
+
+/* Offsets to the device registers.
+   Unlike software-only systems, device drivers interact with complex hardware.
+   It's not useful to define symbolic names for every register bit in the
+   device.
+*/
+enum register_offsets {
+       ChipCmd                 = 0x00,
+       ChipConfig              = 0x04,
+       EECtrl                  = 0x08,
+       PCIBusCfg               = 0x0C,
+       IntrStatus              = 0x10,
+       IntrMask                = 0x14,
+       IntrEnable              = 0x18,
+       IntrHoldoff             = 0x1C, /* DP83816 only */
+       TxRingPtr               = 0x20,
+       TxConfig                = 0x24,
+       RxRingPtr               = 0x30,
+       RxConfig                = 0x34,
+       ClkRun                  = 0x3C,
+       WOLCmd                  = 0x40,
+       PauseCmd                = 0x44,
+       RxFilterAddr            = 0x48,
+       RxFilterData            = 0x4C,
+       BootRomAddr             = 0x50,
+       BootRomData             = 0x54,
+       SiliconRev              = 0x58,
+       StatsCtrl               = 0x5C,
+       StatsData               = 0x60,
+       RxPktErrs               = 0x60,
+       RxMissed                = 0x68,
+       RxCRCErrs               = 0x64,
+       BasicControl            = 0x80,
+       BasicStatus             = 0x84,
+       AnegAdv                 = 0x90,
+       AnegPeer                = 0x94,
+       PhyStatus               = 0xC0,
+       MIntrCtrl               = 0xC4,
+       MIntrStatus             = 0xC8,
+       PhyCtrl                 = 0xE4,
+
+       /* These are from the spec, around page 78... on a separate table.
+        * The meaning of these registers depend on the value of PGSEL. */
+       PGSEL                   = 0xCC,
+       PMDCSR                  = 0xE4,
+       TSTDAT                  = 0xFC,
+       DSPCFG                  = 0xF4,
+       SDCFG                   = 0xF8
+};
+/* the values for the 'magic' registers above (PGSEL=1) */
+#define PMDCSR_VAL     0x189c  /* enable preferred adaptation circuitry */
+#define TSTDAT_VAL     0x0
+#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 */
+enum pci_register_offsets {
+       PCIPM                   = 0x44,
+};
+
+enum ChipCmd_bits {
+       ChipReset               = 0x100,
+       RxReset                 = 0x20,
+       TxReset                 = 0x10,
+       RxOff                   = 0x08,
+       RxOn                    = 0x04,
+       TxOff                   = 0x02,
+       TxOn                    = 0x01,
+};
+
+enum ChipConfig_bits {
+       CfgPhyDis               = 0x200,
+       CfgPhyRst               = 0x400,
+       CfgExtPhy               = 0x1000,
+       CfgAnegEnable           = 0x2000,
+       CfgAneg100              = 0x4000,
+       CfgAnegFull             = 0x8000,
+       CfgAnegDone             = 0x8000000,
+       CfgFullDuplex           = 0x20000000,
+       CfgSpeed100             = 0x40000000,
+       CfgLink                 = 0x80000000,
+};
+
+enum EECtrl_bits {
+       EE_ShiftClk             = 0x04,
+       EE_DataIn               = 0x01,
+       EE_ChipSelect           = 0x08,
+       EE_DataOut              = 0x02,
+       MII_Data                = 0x10,
+       MII_Write               = 0x20,
+       MII_ShiftClk            = 0x40,
+};
+
+enum PCIBusCfg_bits {
+       EepromReload            = 0x4,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum IntrStatus_bits {
+       IntrRxDone              = 0x0001,
+       IntrRxIntr              = 0x0002,
+       IntrRxErr               = 0x0004,
+       IntrRxEarly             = 0x0008,
+       IntrRxIdle              = 0x0010,
+       IntrRxOverrun           = 0x0020,
+       IntrTxDone              = 0x0040,
+       IntrTxIntr              = 0x0080,
+       IntrTxErr               = 0x0100,
+       IntrTxIdle              = 0x0200,
+       IntrTxUnderrun          = 0x0400,
+       StatsMax                = 0x0800,
+       SWInt                   = 0x1000,
+       WOLPkt                  = 0x2000,
+       LinkChange              = 0x4000,
+       IntrHighBits            = 0x8000,
+       RxStatusFIFOOver        = 0x10000,
+       IntrPCIErr              = 0xf00000,
+       RxResetDone             = 0x1000000,
+       TxResetDone             = 0x2000000,
+       IntrAbnormalSummary     = 0xCD20,
+};
+
+/*
+ * Default Interrupts:
+ * Rx OK, Rx Packet Error, Rx Overrun,
+ * Tx OK, Tx Packet Error, Tx Underrun,
+ * MIB Service, Phy Interrupt, High Bits,
+ * Rx Status FIFO overrun,
+ * Received Target Abort, Received Master Abort,
+ * Signalled System Error, Received Parity Error
+ */
+#define DEFAULT_INTR 0x00f1cd65
+
+enum TxConfig_bits {
+       TxDrthMask              = 0x3f,
+       TxFlthMask              = 0x3f00,
+       TxMxdmaMask             = 0x700000,
+       TxMxdma_512             = 0x0,
+       TxMxdma_4               = 0x100000,
+       TxMxdma_8               = 0x200000,
+       TxMxdma_16              = 0x300000,
+       TxMxdma_32              = 0x400000,
+       TxMxdma_64              = 0x500000,
+       TxMxdma_128             = 0x600000,
+       TxMxdma_256             = 0x700000,
+       TxCollRetry             = 0x800000,
+       TxAutoPad               = 0x10000000,
+       TxMacLoop               = 0x20000000,
+       TxHeartIgn              = 0x40000000,
+       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,
+       RxMxdma_512             = 0x0,
+       RxMxdma_4               = 0x100000,
+       RxMxdma_8               = 0x200000,
+       RxMxdma_16              = 0x300000,
+       RxMxdma_32              = 0x400000,
+       RxMxdma_64              = 0x500000,
+       RxMxdma_128             = 0x600000,
+       RxMxdma_256             = 0x700000,
+       RxAcceptLong            = 0x8000000,
+       RxAcceptTx              = 0x10000000,
+       RxAcceptRunt            = 0x40000000,
+       RxAcceptErr             = 0x80000000
+};
+#define RX_DRTH_VAL            (128/8)
+
+enum ClkRun_bits {
+       PMEEnable               = 0x100,
+       PMEStatus               = 0x8000,
+};
+
+enum WolCmd_bits {
+       WakePhy                 = 0x1,
+       WakeUnicast             = 0x2,
+       WakeMulticast           = 0x4,
+       WakeBroadcast           = 0x8,
+       WakeArp                 = 0x10,
+       WakePMatch0             = 0x20,
+       WakePMatch1             = 0x40,
+       WakePMatch2             = 0x80,
+       WakePMatch3             = 0x100,
+       WakeMagic               = 0x200,
+       WakeMagicSecure         = 0x400,
+       SecureHack              = 0x100000,
+       WokePhy                 = 0x400000,
+       WokeUnicast             = 0x800000,
+       WokeMulticast           = 0x1000000,
+       WokeBroadcast           = 0x2000000,
+       WokeArp                 = 0x4000000,
+       WokePMatch0             = 0x8000000,
+       WokePMatch1             = 0x10000000,
+       WokePMatch2             = 0x20000000,
+       WokePMatch3             = 0x40000000,
+       WokeMagic               = 0x80000000,
+       WakeOptsSummary         = 0x7ff
+};
+
+enum RxFilterAddr_bits {
+       RFCRAddressMask         = 0x3ff,
+       AcceptMulticast         = 0x00200000,
+       AcceptMyPhys            = 0x08000000,
+       AcceptAllPhys           = 0x10000000,
+       AcceptAllMulticast      = 0x20000000,
+       AcceptBroadcast         = 0x40000000,
+       RxFilterEnable          = 0x80000000
+};
+
+enum StatsCtrl_bits {
+       StatsWarn               = 0x1,
+       StatsFreeze             = 0x2,
+       StatsClear              = 0x4,
+       StatsStrobe             = 0x8,
+};
+
+enum MIntrCtrl_bits {
+       MICRIntEn               = 0x2,
+};
+
+enum PhyCtrl_bits {
+       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
+#define SRR_DP83816_A4 0x0504
+#define SRR_DP83816_A5 0x0505
+
+/* The Rx and Tx buffer descriptors. */
+/* Note that using only 32 bit fields simplifies conversion to big-endian
+   architectures. */
+struct netdev_desc {
+       __le32 next_desc;
+       __le32 cmd_status;
+       __le32 addr;
+       __le32 software_use;
+};
+
+/* Bits in network_desc.status */
+enum desc_status_bits {
+       DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000,
+       DescNoCRC=0x10000000, DescPktOK=0x08000000,
+       DescSizeMask=0xfff,
+
+       DescTxAbort=0x04000000, DescTxFIFO=0x02000000,
+       DescTxCarrier=0x01000000, DescTxDefer=0x00800000,
+       DescTxExcDefer=0x00400000, DescTxOOWCol=0x00200000,
+       DescTxExcColl=0x00100000, DescTxCollCount=0x000f0000,
+
+       DescRxAbort=0x04000000, DescRxOver=0x02000000,
+       DescRxDest=0x01800000, DescRxLong=0x00400000,
+       DescRxRunt=0x00200000, DescRxInvalid=0x00100000,
+       DescRxCRC=0x00080000, DescRxAlign=0x00040000,
+       DescRxLoop=0x00020000, DesRxColl=0x00010000,
+};
+
+struct netdev_private {
+       /* Descriptor rings first for alignment */
+       dma_addr_t ring_dma;
+       struct netdev_desc *rx_ring;
+       struct netdev_desc *tx_ring;
+       /* The addresses of receive-in-place skbuffs */
+       struct sk_buff *rx_skbuff[RX_RING_SIZE];
+       dma_addr_t rx_dma[RX_RING_SIZE];
+       /* address of a sent-in-place packet/buffer, for later free() */
+       struct sk_buff *tx_skbuff[TX_RING_SIZE];
+       dma_addr_t tx_dma[TX_RING_SIZE];
+       struct net_device *dev;
+       struct napi_struct napi;
+       /* Media monitoring timer */
+       struct timer_list timer;
+       /* Frequently used values: keep some adjacent for cache effect */
+       struct pci_dev *pci_dev;
+       struct netdev_desc *rx_head_desc;
+       /* Producer/consumer ring indices */
+       unsigned int cur_rx, dirty_rx;
+       unsigned int cur_tx, dirty_tx;
+       /* 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;
+       /* Don't pay attention to the reported link state. */
+       int ignore_phy;
+       /* 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;
+       u32 rx_filter[16];
+       /* FIFO and PCI burst thresholds */
+       u32 tx_config, rx_config;
+       /* original contents of ClkRun register */
+       u32 SavedClkRun;
+       /* silicon revision */
+       u32 srr;
+       /* expected DSPCFG value */
+       u16 dspcfg;
+       int dspcfg_workaround;
+       /* 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 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);
+static int netdev_open(struct net_device *dev);
+static void do_cable_magic(struct net_device *dev);
+static void undo_cable_magic(struct net_device *dev);
+static void check_link(struct net_device *dev);
+static void netdev_timer(unsigned long data);
+static void dump_ring(struct net_device *dev);
+static void ns_tx_timeout(struct net_device *dev);
+static int alloc_ring(struct net_device *dev);
+static void refill_rx(struct net_device *dev);
+static void init_ring(struct net_device *dev);
+static void drain_tx(struct net_device *dev);
+static void drain_ring(struct net_device *dev);
+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 netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t intr_handler(int irq, void *dev_instance);
+static void netdev_error(struct net_device *dev, int intr_status);
+static int natsemi_poll(struct napi_struct *napi, 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);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int netdev_set_wol(struct net_device *dev, u32 newval);
+static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur);
+static int netdev_set_sopass(struct net_device *dev, u8 *newval);
+static int netdev_get_sopass(struct net_device *dev, u8 *data);
+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);
+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;
+
+#define NATSEMI_ATTR(_name) \
+static ssize_t natsemi_show_##_name(struct device *dev, \
+         struct device_attribute *attr, char *buf); \
+        static ssize_t natsemi_set_##_name(struct device *dev, \
+               struct device_attribute *attr, \
+               const char *buf, size_t count); \
+        static DEVICE_ATTR(_name, 0644, natsemi_show_##_name, natsemi_set_##_name)
+
+#define NATSEMI_CREATE_FILE(_dev, _name) \
+         device_create_file(&_dev->dev, &dev_attr_##_name)
+#define NATSEMI_REMOVE_FILE(_dev, _name) \
+         device_remove_file(&_dev->dev, &dev_attr_##_name)
+
+NATSEMI_ATTR(dspcfg_workaround);
+
+static ssize_t natsemi_show_dspcfg_workaround(struct device *dev,
+                                             struct device_attribute *attr,
+                                             char *buf)
+{
+       struct netdev_private *np = netdev_priv(to_net_dev(dev));
+
+       return sprintf(buf, "%s\n", np->dspcfg_workaround ? "on" : "off");
+}
+
+static ssize_t natsemi_set_dspcfg_workaround(struct device *dev,
+                                            struct device_attribute *attr,
+                                            const char *buf, size_t count)
+{
+       struct netdev_private *np = netdev_priv(to_net_dev(dev));
+       int new_setting;
+       unsigned long flags;
+
+        /* Find out the new setting */
+        if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
+                new_setting = 1;
+        else if (!strncmp("off", buf, count - 1) ||
+                 !strncmp("0", buf, count - 1))
+               new_setting = 0;
+       else
+                 return count;
+
+       spin_lock_irqsave(&np->lock, flags);
+
+       np->dspcfg_workaround = new_setting;
+
+       spin_unlock_irqrestore(&np->lock, flags);
+
+       return count;
+}
+
+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, independent 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;
+
+       if (np->ignore_phy)
+               netif_carrier_on(dev);
+       else
+               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 const struct net_device_ops natsemi_netdev_ops = {
+       .ndo_open               = netdev_open,
+       .ndo_stop               = netdev_close,
+       .ndo_start_xmit         = start_tx,
+       .ndo_get_stats          = get_stats,
+       .ndo_set_multicast_list = set_rx_mode,
+       .ndo_change_mtu         = natsemi_change_mtu,
+       .ndo_do_ioctl           = netdev_ioctl,
+       .ndo_tx_timeout         = ns_tx_timeout,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = natsemi_poll_controller,
+#endif
+};
+
+static int __devinit natsemi_probe1 (struct pci_dev *pdev,
+       const struct pci_device_id *ent)
+{
+       struct net_device *dev;
+       struct netdev_private *np;
+       int i, option, irq, chip_idx = ent->driver_data;
+       static int find_cnt = -1;
+       resource_size_t iostart;
+       unsigned long iosize;
+       void __iomem *ioaddr;
+       const int pcibar = 1; /* PCI base address register */
+       int prev_eedata;
+       u32 tmp;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+       static int printed_version;
+       if (!printed_version++)
+               printk(version);
+#endif
+
+       i = pci_enable_device(pdev);
+       if (i) return i;
+
+       /* natsemi has a non-standard PM control register
+        * in PCI config space.  Some boards apparently need
+        * to be brought to D0 in this manner.
+        */
+       pci_read_config_dword(pdev, PCIPM, &tmp);
+       if (tmp & PCI_PM_CTRL_STATE_MASK) {
+               /* D0 state, disable PME assertion */
+               u32 newtmp = tmp & ~PCI_PM_CTRL_STATE_MASK;
+               pci_write_config_dword(pdev, PCIPM, newtmp);
+       }
+
+       find_cnt++;
+       iostart = pci_resource_start(pdev, pcibar);
+       iosize = pci_resource_len(pdev, pcibar);
+       irq = pdev->irq;
+
+       pci_set_master(pdev);
+
+       dev = alloc_etherdev(sizeof (struct netdev_private));
+       if (!dev)
+               return -ENOMEM;
+       SET_NETDEV_DEV(dev, &pdev->dev);
+
+       i = pci_request_regions(pdev, DRV_NAME);
+       if (i)
+               goto err_pci_request_regions;
+
+       ioaddr = ioremap(iostart, iosize);
+       if (!ioaddr) {
+               i = -ENOMEM;
+               goto err_ioremap;
+       }
+
+       /* Work around the dropped serial bit. */
+       prev_eedata = eeprom_read(ioaddr, 6);
+       for (i = 0; i < 3; i++) {
+               int eedata = eeprom_read(ioaddr, i + 7);
+               dev->dev_addr[i*2] = (eedata << 1) + (prev_eedata >> 15);
+               dev->dev_addr[i*2+1] = eedata >> 7;
+               prev_eedata = eedata;
+       }
+
+       /* Store MAC Address in perm_addr */
+       memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN);
+
+       dev->base_addr = (unsigned long __force) ioaddr;
+       dev->irq = irq;
+
+       np = netdev_priv(dev);
+       netif_napi_add(dev, &np->napi, natsemi_poll, 64);
+       np->dev = dev;
+
+       np->pci_dev = pdev;
+       pci_set_drvdata(pdev, dev);
+       np->iosize = iosize;
+       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;
+       if (natsemi_pci_info[chip_idx].flags & NATSEMI_FLAG_IGNORE_PHY)
+               np->ignore_phy = 1;
+       else
+               np->ignore_phy = 0;
+       np->dspcfg_workaround = dspcfg_workaround;
+
+       /* Initial port:
+        * - If configured to ignore the PHY set up for external.
+        * - 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 (np->ignore_phy || 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 we're ignoring the PHY it doesn't matter if we can't
+                * find one. */
+               if (!np->ignore_phy && 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;
+
+       /* The lower four bits are the media type. */
+       if (option) {
+               if (option & 0x200)
+                       np->full_duplex = 1;
+               if (option & 15)
+                       printk(KERN_INFO
+                               "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;
+
+       dev->netdev_ops = &natsemi_netdev_ops;
+       dev->watchdog_timeo = TX_TIMEOUT;
+
+       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;
+
+       if (NATSEMI_CREATE_FILE(pdev, dspcfg_workaround))
+               goto err_create_file;
+
+       if (netif_msg_drv(np)) {
+               printk(KERN_INFO "natsemi %s: %s at %#08llx "
+                      "(%s), %pM, IRQ %d",
+                      dev->name, natsemi_pci_info[chip_idx].name,
+                      (unsigned long long)iostart, pci_name(np->pci_dev),
+                      dev->dev_addr, irq);
+               if (dev->if_port == PORT_TP)
+                       printk(", port TP.\n");
+               else if (np->ignore_phy)
+                       printk(", port MII, ignoring PHY\n");
+               else
+                       printk(", port MII, phy ad %d.\n", np->phy_addr_external);
+       }
+       return 0;
+
+ err_create_file:
+       unregister_netdev(dev);
+
+ err_register_netdev:
+       iounmap(ioaddr);
+
+ err_ioremap:
+       pci_release_regions(pdev);
+       pci_set_drvdata(pdev, NULL);
+
+ err_pci_request_regions:
+       free_netdev(dev);
+       return i;
+}
+
+
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.
+   The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses. */
+
+/* Delay between EEPROM clock transitions.
+   No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
+   a delay.  Note that pre-2.0.34 kernels had a cache-alignment bug that
+   made udelay() unreliable.
+   The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
+   deprecated.
+*/
+#define eeprom_delay(ee_addr)  readl(ee_addr)
+
+#define EE_Write0 (EE_ChipSelect)
+#define EE_Write1 (EE_ChipSelect | EE_DataIn)
+
+/* The EEPROM commands include the alway-set leading bit. */
+enum EEPROM_Cmds {
+       EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
+};
+
+static int eeprom_read(void __iomem *addr, int location)
+{
+       int i;
+       int retval = 0;
+       void __iomem *ee_addr = addr + EECtrl;
+       int read_cmd = location | EE_ReadCmd;
+
+       writel(EE_Write0, ee_addr);
+
+       /* Shift the read command bits out. */
+       for (i = 10; i >= 0; i--) {
+               short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
+               writel(dataval, ee_addr);
+               eeprom_delay(ee_addr);
+               writel(dataval | EE_ShiftClk, ee_addr);
+               eeprom_delay(ee_addr);
+       }
+       writel(EE_ChipSelect, ee_addr);
+       eeprom_delay(ee_addr);
+
+       for (i = 0; i < 16; i++) {
+               writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
+               eeprom_delay(ee_addr);
+               retval |= (readl(ee_addr) & EE_DataOut) ? 1 << i : 0;
+               writel(EE_ChipSelect, ee_addr);
+               eeprom_delay(ee_addr);
+       }
+
+       /* Terminate the EEPROM access. */
+       writel(EE_Write0, ee_addr);
+       writel(0, ee_addr);
+       return retval;
+}
+
+/* MII transceiver control section.
+ * The 83815 series has an internal transceiver, and we present the
+ * internal management registers as if they were MII connected.
+ * External Phy registers are referenced through the MII interface.
+ */
+
+/* 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)
+{
+       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 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;
+}
+
+/* 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 = 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] */
+#define CFG_RESET_SAVE 0xfde000
+/* WCSR bits [0:4] [9:10] */
+#define WCSR_RESET_SAVE 0x61f
+/* RFCR bits [20] [22] [27:31] */
+#define RFCR_RESET_SAVE 0xf8500000
+
+static void natsemi_reset(struct net_device *dev)
+{
+       int i;
+       u32 cfg;
+       u32 wcsr;
+       u32 rfcr;
+       u16 pmatch[3];
+       u16 sopass[3];
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+
+       /*
+        * Resetting the chip causes some registers to be lost.
+        * Natsemi suggests NOT reloading the EEPROM while live, so instead
+        * we save the state that would have been loaded from EEPROM
+        * on a normal power-up (see the spec EEPROM map).  This assumes
+        * whoever calls this will follow up with init_registers() eventually.
+        */
+
+       /* CFG */
+       cfg = readl(ioaddr + ChipConfig) & CFG_RESET_SAVE;
+       /* WCSR */
+       wcsr = readl(ioaddr + WOLCmd) & WCSR_RESET_SAVE;
+       /* RFCR */
+       rfcr = readl(ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
+       /* PMATCH */
+       for (i = 0; i < 3; i++) {
+               writel(i*2, ioaddr + RxFilterAddr);
+               pmatch[i] = readw(ioaddr + RxFilterData);
+       }
+       /* SOPAS */
+       for (i = 0; i < 3; i++) {
+               writel(0xa+(i*2), ioaddr + RxFilterAddr);
+               sopass[i] = readw(ioaddr + RxFilterData);
+       }
+
+       /* now whack the chip */
+       writel(ChipReset, ioaddr + ChipCmd);
+       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+               if (!(readl(ioaddr + ChipCmd) & ChipReset))
+                       break;
+               udelay(5);
+       }
+       if (i==NATSEMI_HW_TIMEOUT) {
+               printk(KERN_WARNING "%s: reset did not complete in %d usec.\n",
+                       dev->name, i*5);
+       } else if (netif_msg_hw(np)) {
+               printk(KERN_DEBUG "%s: reset completed in %d usec.\n",
+                       dev->name, i*5);
+       }
+
+       /* restore CFG */
+       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(ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
+       writel(wcsr, ioaddr + WOLCmd);
+       /* read RFCR */
+       rfcr |= readl(ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
+       /* restore PMATCH */
+       for (i = 0; i < 3; i++) {
+               writel(i*2, ioaddr + RxFilterAddr);
+               writew(pmatch[i], ioaddr + RxFilterData);
+       }
+       for (i = 0; i < 3; i++) {
+               writel(0xa+(i*2), ioaddr + RxFilterAddr);
+               writew(sopass[i], ioaddr + RxFilterData);
+       }
+       /* restore RFCR */
+       writel(rfcr, ioaddr + RxFilterAddr);
+}
+
+static void reset_rx(struct net_device *dev)
+{
+       int i;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+
+       np->intr_status &= ~RxResetDone;
+
+       writel(RxReset, ioaddr + ChipCmd);
+
+       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+               np->intr_status |= readl(ioaddr + IntrStatus);
+               if (np->intr_status & RxResetDone)
+                       break;
+               udelay(15);
+       }
+       if (i==NATSEMI_HW_TIMEOUT) {
+               printk(KERN_WARNING "%s: RX reset did not complete in %d usec.\n",
+                      dev->name, i*15);
+       } else if (netif_msg_hw(np)) {
+               printk(KERN_WARNING "%s: RX reset took %d usec.\n",
+                      dev->name, i*15);
+       }
+}
+
+static void natsemi_reload_eeprom(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = ns_ioaddr(dev);
+       int i;
+
+       writel(EepromReload, ioaddr + PCIBusCfg);
+       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+               udelay(50);
+               if (!(readl(ioaddr + PCIBusCfg) & EepromReload))
+                       break;
+       }
+       if (i==NATSEMI_HW_TIMEOUT) {
+               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 "natsemi %s: EEPROM reloaded in %d usec.\n",
+                       pci_name(np->pci_dev), i*50);
+       }
+}
+
+static void natsemi_stop_rxtx(struct net_device *dev)
+{
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       struct netdev_private *np = netdev_priv(dev);
+       int i;
+
+       writel(RxOff | TxOff, ioaddr + ChipCmd);
+       for(i=0;i< NATSEMI_HW_TIMEOUT;i++) {
+               if ((readl(ioaddr + ChipCmd) & (TxOn|RxOn)) == 0)
+                       break;
+               udelay(5);
+       }
+       if (i==NATSEMI_HW_TIMEOUT) {
+               printk(KERN_WARNING "%s: Tx/Rx process did not stop in %d usec.\n",
+                       dev->name, i*5);
+       } else if (netif_msg_hw(np)) {
+               printk(KERN_DEBUG "%s: Tx/Rx process stopped in %d usec.\n",
+                       dev->name, i*5);
+       }
+}
+
+static int netdev_open(struct net_device *dev)
+{
+       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, IRQF_SHARED, dev->name, dev);
+       if (i) return i;
+
+       if (netif_msg_ifup(np))
+               printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
+                       dev->name, dev->irq);
+       i = alloc_ring(dev);
+       if (i < 0) {
+               free_irq(dev->irq, dev);
+               return i;
+       }
+       napi_enable(&np->napi);
+
+       init_ring(dev);
+       spin_lock_irq(&np->lock);
+       init_registers(dev);
+       /* now set the MAC address according to dev->dev_addr */
+       for (i = 0; i < 3; i++) {
+               u16 mac = (dev->dev_addr[2*i+1]<<8) + dev->dev_addr[2*i];
+
+               writel(i*2, ioaddr + RxFilterAddr);
+               writew(mac, ioaddr + RxFilterData);
+       }
+       writel(np->cur_rx_mode, ioaddr + RxFilterAddr);
+       spin_unlock_irq(&np->lock);
+
+       netif_start_queue(dev);
+
+       if (netif_msg_ifup(np))
+               printk(KERN_DEBUG "%s: Done netdev_open(), status: %#08x.\n",
+                       dev->name, (int)readl(ioaddr + ChipCmd));
+
+       /* Set the timer to check for link beat. */
+       init_timer(&np->timer);
+       np->timer.expires = round_jiffies(jiffies + NATSEMI_TIMER_FREQ);
+       np->timer.data = (unsigned long)dev;
+       np->timer.function = netdev_timer; /* timer handler */
+       add_timer(&np->timer);
+
+       return 0;
+}
+
+static void do_cable_magic(struct net_device *dev)
+{
+       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;
+
+       /*
+        * 100 MBit links with short cables can trip an issue with the chip.
+        * The problem manifests as lots of CRC errors and/or flickering
+        * activity LED while idle.  This process is based on instructions
+        * from engineers at National.
+        */
+       if (readl(ioaddr + ChipConfig) & CfgSpeed100) {
+               u16 data;
+
+               writew(1, ioaddr + PGSEL);
+               /*
+                * coefficient visibility should already be enabled via
+                * DSPCFG | 0x1000
+                */
+               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))) {
+                       np = netdev_priv(dev);
+
+                       /* the bug has been triggered - fix the coefficient */
+                       writew(TSTDAT_FIXED, ioaddr + TSTDAT);
+                       /* lock the value */
+                       data = readw(ioaddr + DSPCFG);
+                       np->dspcfg = data | DSPCFG_LOCK;
+                       writew(np->dspcfg, ioaddr + DSPCFG);
+               }
+               writew(0, ioaddr + PGSEL);
+       }
+}
+
+static void undo_cable_magic(struct net_device *dev)
+{
+       u16 data;
+       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, ioaddr + PGSEL);
+       /* make sure the lock bit is clear */
+       data = readw(ioaddr + DSPCFG);
+       np->dspcfg = data & ~DSPCFG_LOCK;
+       writew(np->dspcfg, ioaddr + DSPCFG);
+       writew(0, ioaddr + PGSEL);
+}
+
+static void check_link(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       int duplex = np->duplex;
+       u16 bmsr;
+
+       /* If we are ignoring the PHY then don't try reading it. */
+       if (np->ignore_phy)
+               goto propagate_state;
+
+       /* 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 (!(bmsr & BMSR_LSTATUS)) {
+               if (netif_carrier_ok(dev)) {
+                       if (netif_msg_link(np))
+                               printk(KERN_NOTICE "%s: link down.\n",
+                                      dev->name);
+                       netif_carrier_off(dev);
+                       undo_cable_magic(dev);
+               }
+               return;
+       }
+       if (!netif_carrier_ok(dev)) {
+               if (netif_msg_link(np))
+                       printk(KERN_NOTICE "%s: link up.\n", dev->name);
+               netif_carrier_on(dev);
+               do_cable_magic(dev);
+       }
+
+       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;
+       }
+
+propagate_state:
+       /* if duplex is set then bit 28 must be set, too */
+       if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
+               if (netif_msg_link(np))
+                       printk(KERN_INFO
+                               "%s: Setting %s-duplex based on negotiated "
+                               "link capability.\n", dev->name,
+                               duplex ? "full" : "half");
+               if (duplex) {
+                       np->rx_config |= RxAcceptTx;
+                       np->tx_config |= TxCarrierIgn | TxHeartIgn;
+               } else {
+                       np->rx_config &= ~RxAcceptTx;
+                       np->tx_config &= ~(TxCarrierIgn | TxHeartIgn);
+               }
+               writel(np->tx_config, ioaddr + TxConfig);
+               writel(np->rx_config, ioaddr + RxConfig);
+       }
+}
+
+static void init_registers(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       init_phy_fixup(dev);
+
+       /* clear any interrupts that are pending, such as wake events */
+       readl(ioaddr + IntrStatus);
+
+       writel(np->ring_dma, ioaddr + RxRingPtr);
+       writel(np->ring_dma + RX_RING_SIZE * sizeof(struct netdev_desc),
+               ioaddr + TxRingPtr);
+
+       /* Initialize other registers.
+        * Configure the PCI bus bursts and FIFO thresholds.
+        * Configure for standard, in-spec Ethernet.
+        * Start with half-duplex. check_link will update
+        * to the correct settings.
+        */
+
+       /* DRTH: 2: start tx if 64 bytes are in the fifo
+        * FLTH: 0x10: refill with next packet if 512 bytes are free
+        * MXDMA: 0: up to 256 byte bursts.
+        *      MXDMA must be <= FLTH
+        * ECRETRY=1
+        * ATP=1
+        */
+       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 | 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:
+        * The PME bit is initialized from the EEPROM contents.
+        * PCI cards probably have PME disabled, but motherboard
+        * implementations may have PME set to enable WakeOnLan.
+        * With PME set the chip will scan incoming packets but
+        * nothing will be written to memory. */
+       np->SavedClkRun = readl(ioaddr + ClkRun);
+       writel(np->SavedClkRun & ~PMEEnable, ioaddr + ClkRun);
+       if (np->SavedClkRun & PMEStatus && netif_msg_wol(np)) {
+               printk(KERN_NOTICE "%s: Wake-up event %#08x\n",
+                       dev->name, readl(ioaddr + WOLCmd));
+       }
+
+       check_link(dev);
+       __set_rx_mode(dev);
+
+       /* Enable interrupts by setting the interrupt mask. */
+       writel(DEFAULT_INTR, ioaddr + IntrMask);
+       natsemi_irq_enable(dev);
+
+       writel(RxOn | TxOn, ioaddr + ChipCmd);
+       writel(StatsClear, ioaddr + StatsCtrl); /* Clear Stats */
+}
+
+/*
+ * netdev_timer:
+ * Purpose:
+ * 1) check for link changes. Usually they are handled by the MII interrupt
+ *    but it doesn't hurt to check twice.
+ * 2) check for sudden death of the NIC:
+ *    It seems that a reference set for this chip went out with incorrect info,
+ *    and there exist boards that aren't quite right.  An unexpected voltage
+ *    drop can cause the PHY to get itself in a weird state (basically reset).
+ *    NOTE: this only seems to affect revC chips.  The user can disable
+ *    this check via dspcfg_workaround sysfs option.
+ * 3) check of death of the RX path due to OOM
+ */
+static void netdev_timer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       int next_tick = NATSEMI_TIMER_FREQ;
+
+       if (netif_msg_timer(np)) {
+               /* DO NOT read the IntrStatus register,
+                * a read clears any pending interrupts.
+                */
+               printk(KERN_DEBUG "%s: Media selection timer tick.\n",
+                       dev->name);
+       }
+
+       if (dev->if_port == PORT_TP) {
+               u16 dspcfg;
+
+               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 (np->dspcfg_workaround && dspcfg != np->dspcfg) {
+                       if (!netif_queue_stopped(dev)) {
+                               spin_unlock_irq(&np->lock);
+                               if (netif_msg_drv(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 {
+                       /* init_registers() calls check_link() for the above case */
+                       check_link(dev);
+                       spin_unlock_irq(&np->lock);
+               }
+       } else {
+               spin_lock_irq(&np->lock);
+               check_link(dev);
+               spin_unlock_irq(&np->lock);
+       }
+       if (np->oom) {
+               disable_irq(dev->irq);
+               np->oom = 0;
+               refill_rx(dev);
+               enable_irq(dev->irq);
+               if (!np->oom) {
+                       writel(RxOn, ioaddr + ChipCmd);
+               } else {
+                       next_tick = 1;
+               }
+       }
+
+       if (next_tick > 1)
+               mod_timer(&np->timer, round_jiffies(jiffies + next_tick));
+       else
+               mod_timer(&np->timer, jiffies + next_tick);
+}
+
+static void dump_ring(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+
+       if (netif_msg_pktdata(np)) {
+               int i;
+               printk(KERN_DEBUG "  Tx ring at %p:\n", np->tx_ring);
+               for (i = 0; i < TX_RING_SIZE; i++) {
+                       printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
+                               i, np->tx_ring[i].next_desc,
+                               np->tx_ring[i].cmd_status,
+                               np->tx_ring[i].addr);
+               }
+               printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
+               for (i = 0; i < RX_RING_SIZE; i++) {
+                       printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
+                               i, np->rx_ring[i].next_desc,
+                               np->rx_ring[i].cmd_status,
+                               np->rx_ring[i].addr);
+               }
+       }
+}
+
+static void ns_tx_timeout(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       disable_irq(dev->irq);
+       spin_lock_irq(&np->lock);
+       if (!np->hands_off) {
+               if (netif_msg_tx_err(np))
+                       printk(KERN_WARNING
+                               "%s: Transmit timed out, status %#08x,"
+                               " resetting...\n",
+                               dev->name, readl(ioaddr + IntrStatus));
+               dump_ring(dev);
+
+               natsemi_reset(dev);
+               reinit_ring(dev);
+               init_registers(dev);
+       } else {
+               printk(KERN_WARNING
+                       "%s: tx_timeout while in hands_off state?\n",
+                       dev->name);
+       }
+       spin_unlock_irq(&np->lock);
+       enable_irq(dev->irq);
+
+       dev->trans_start = jiffies; /* prevent tx timeout */
+       dev->stats.tx_errors++;
+       netif_wake_queue(dev);
+}
+
+static int alloc_ring(struct net_device *dev)
+{
+       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);
+       if (!np->rx_ring)
+               return -ENOMEM;
+       np->tx_ring = &np->rx_ring[RX_RING_SIZE];
+       return 0;
+}
+
+static void refill_rx(struct net_device *dev)
+{
+       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+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->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);
+       }
+       if (np->cur_rx - np->dirty_rx == RX_RING_SIZE) {
+               if (netif_msg_rx_err(np))
+                       printk(KERN_WARNING "%s: going OOM.\n", dev->name);
+               np->oom = 1;
+       }
+}
+
+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 = netdev_priv(dev);
+       int i;
+
+       /* 1) TX ring */
+       np->dirty_tx = np->cur_tx = 0;
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               np->tx_skbuff[i] = NULL;
+               np->tx_ring[i].next_desc = cpu_to_le32(np->ring_dma
+                       +sizeof(struct netdev_desc)
+                       *((i+1)%TX_RING_SIZE+RX_RING_SIZE));
+               np->tx_ring[i].cmd_status = 0;
+       }
+
+       /* 2) RX ring */
+       np->dirty_rx = 0;
+       np->cur_rx = RX_RING_SIZE;
+       np->oom = 0;
+       set_bufsize(dev);
+
+       np->rx_head_desc = &np->rx_ring[0];
+
+       /* Please be careful before changing this loop - at least gcc-2.95.1
+        * miscompiles it otherwise.
+        */
+       /* Initialize all Rx descriptors. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].next_desc = cpu_to_le32(np->ring_dma
+                               +sizeof(struct netdev_desc)
+                               *((i+1)%RX_RING_SIZE));
+               np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
+               np->rx_skbuff[i] = NULL;
+       }
+       refill_rx(dev);
+       dump_ring(dev);
+}
+
+static void drain_tx(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       int i;
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               if (np->tx_skbuff[i]) {
+                       pci_unmap_single(np->pci_dev,
+                               np->tx_dma[i], np->tx_skbuff[i]->len,
+                               PCI_DMA_TODEVICE);
+                       dev_kfree_skb(np->tx_skbuff[i]);
+                       dev->stats.tx_dropped++;
+               }
+               np->tx_skbuff[i] = NULL;
+       }
+}
+
+static void drain_rx(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       unsigned int buflen = np->rx_buf_sz;
+       int i;
+
+       /* Free all the skbuffs in the Rx queue. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].cmd_status = 0;
+               np->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
+               if (np->rx_skbuff[i]) {
+                       pci_unmap_single(np->pci_dev, np->rx_dma[i],
+                               buflen + NATSEMI_PADDING,
+                               PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb(np->rx_skbuff[i]);
+               }
+               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 = 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_rx(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       int i;
+
+       /* RX Ring */
+       np->dirty_rx = 0;
+       np->cur_rx = RX_RING_SIZE;
+       np->rx_head_desc = &np->rx_ring[0];
+       /* Initialize all Rx descriptors. */
+       for (i = 0; i < RX_RING_SIZE; i++)
+               np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
+
+       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 netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       unsigned entry;
+       unsigned long flags;
+
+       /* Note: Ordering is important here, set the field with the
+          "ownership" bit last, and only then increment cur_tx. */
+
+       /* Calculate the next Tx descriptor entry. */
+       entry = np->cur_tx % TX_RING_SIZE;
+
+       np->tx_skbuff[entry] = skb;
+       np->tx_dma[entry] = pci_map_single(np->pci_dev,
+                               skb->data,skb->len, PCI_DMA_TODEVICE);
+
+       np->tx_ring[entry].addr = cpu_to_le32(np->tx_dma[entry]);
+
+       spin_lock_irqsave(&np->lock, flags);
+
+       if (!np->hands_off) {
+               np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn | skb->len);
+               /* StrongARM: Explicitly cache flush np->tx_ring and
+                * skb->data,skb->len. */
+               wmb();
+               np->cur_tx++;
+               if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) {
+                       netdev_tx_done(dev);
+                       if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1)
+                               netif_stop_queue(dev);
+               }
+               /* Wake the potentially-idle transmit channel. */
+               writel(TxOn, ioaddr + ChipCmd);
+       } else {
+               dev_kfree_skb_irq(skb);
+               dev->stats.tx_dropped++;
+       }
+       spin_unlock_irqrestore(&np->lock, flags);
+
+       if (netif_msg_tx_queued(np)) {
+               printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
+                       dev->name, np->cur_tx, entry);
+       }
+       return NETDEV_TX_OK;
+}
+
+static void netdev_tx_done(struct net_device *dev)
+{
+       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;
+               if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescOwn))
+                       break;
+               if (netif_msg_tx_done(np))
+                       printk(KERN_DEBUG
+                               "%s: tx frame #%d finished, status %#08x.\n",
+                                       dev->name, np->dirty_tx,
+                                       le32_to_cpu(np->tx_ring[entry].cmd_status));
+               if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescPktOK)) {
+                       dev->stats.tx_packets++;
+                       dev->stats.tx_bytes += np->tx_skbuff[entry]->len;
+               } else { /* Various Tx errors */
+                       int tx_status =
+                               le32_to_cpu(np->tx_ring[entry].cmd_status);
+                       if (tx_status & (DescTxAbort|DescTxExcColl))
+                               dev->stats.tx_aborted_errors++;
+                       if (tx_status & DescTxFIFO)
+                               dev->stats.tx_fifo_errors++;
+                       if (tx_status & DescTxCarrier)
+                               dev->stats.tx_carrier_errors++;
+                       if (tx_status & DescTxOOWCol)
+                               dev->stats.tx_window_errors++;
+                       dev->stats.tx_errors++;
+               }
+               pci_unmap_single(np->pci_dev,np->tx_dma[entry],
+                                       np->tx_skbuff[entry]->len,
+                                       PCI_DMA_TODEVICE);
+               /* Free the original skb. */
+               dev_kfree_skb_irq(np->tx_skbuff[entry]);
+               np->tx_skbuff[entry] = NULL;
+       }
+       if (netif_queue_stopped(dev) &&
+           np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
+               /* The ring is no longer full, wake queue. */
+               netif_wake_queue(dev);
+       }
+}
+
+/* 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 = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       /* Reading IntrStatus automatically acknowledges so don't do
+        * that while interrupts are disabled, (for example, while a
+        * poll is scheduled).  */
+       if (np->hands_off || !readl(ioaddr + IntrEnable))
+               return IRQ_NONE;
+
+       np->intr_status = readl(ioaddr + IntrStatus);
+
+       if (!np->intr_status)
+               return IRQ_NONE;
+
+       if (netif_msg_intr(np))
+               printk(KERN_DEBUG
+                      "%s: Interrupt, status %#08x, mask %#08x.\n",
+                      dev->name, np->intr_status,
+                      readl(ioaddr + IntrMask));
+
+       prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
+
+       if (napi_schedule_prep(&np->napi)) {
+               /* Disable interrupts and register for poll */
+               natsemi_irq_disable(dev);
+               __napi_schedule(&np->napi);
+       } else
+               printk(KERN_WARNING
+                      "%s: Ignoring interrupt, status %#08x, mask %#08x.\n",
+                      dev->name, np->intr_status,
+                      readl(ioaddr + IntrMask));
+
+       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 napi_struct *napi, int budget)
+{
+       struct netdev_private *np = container_of(napi, struct netdev_private, napi);
+       struct net_device *dev = np->dev;
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       int work_done = 0;
+
+       do {
+               if (netif_msg_intr(np))
+                       printk(KERN_DEBUG
+                              "%s: Poll, status %#08x, mask %#08x.\n",
+                              dev->name, np->intr_status,
+                              readl(ioaddr + IntrMask));
+
+               /* netdev_rx() may read IntrStatus again if the RX state
+                * machine falls over so do it first. */
+               if (np->intr_status &
+                   (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
+                    IntrRxErr | IntrRxOverrun)) {
+                       netdev_rx(dev, &work_done, budget);
+               }
+
+               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 (np->intr_status & IntrAbnormalSummary)
+                       netdev_error(dev, np->intr_status);
+
+               if (work_done >= budget)
+                       return work_done;
+
+               np->intr_status = readl(ioaddr + IntrStatus);
+       } while (np->intr_status);
+
+       napi_complete(napi);
+
+       /* Reenable interrupts providing nothing is trying to shut
+        * the chip down. */
+       spin_lock(&np->lock);
+       if (!np->hands_off)
+               natsemi_irq_enable(dev);
+       spin_unlock(&np->lock);
+
+       return work_done;
+}
+
+/* This routine is logically part of the interrupt handler, but separated
+   for clarity and better register allocation. */
+static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
+{
+       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;
+       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) {
+                               unsigned long flags;
+
+                               if (netif_msg_rx_err(np))
+                                       printk(KERN_WARNING
+                                               "%s: Oversized(?) Ethernet "
+                                               "frame spanned multiple "
+                                               "buffers, entry %#08x "
+                                               "status %#08x.\n", dev->name,
+                                               np->cur_rx, desc_status);
+                               dev->stats.rx_length_errors++;
+
+                               /* The RX state machine has probably
+                                * locked up beneath us.  Follow the
+                                * reset procedure documented in
+                                * AN-1287. */
+
+                               spin_lock_irqsave(&np->lock, flags);
+                               reset_rx(dev);
+                               reinit_rx(dev);
+                               writel(np->ring_dma, ioaddr + RxRingPtr);
+                               check_link(dev);
+                               spin_unlock_irqrestore(&np->lock, flags);
+
+                               /* We'll enable RX on exit from this
+                                * function. */
+                               break;
+
+                       } else {
+                               /* There was an error. */
+                               dev->stats.rx_errors++;
+                               if (desc_status & (DescRxAbort|DescRxOver))
+                                       dev->stats.rx_over_errors++;
+                               if (desc_status & (DescRxLong|DescRxRunt))
+                                       dev->stats.rx_length_errors++;
+                               if (desc_status & (DescRxInvalid|DescRxAlign))
+                                       dev->stats.rx_frame_errors++;
+                               if (desc_status & DescRxCRC)
+                                       dev->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. */
+                       /* Check if the packet is long enough to accept
+                        * without copying to a minimally-sized skbuff. */
+                       if (pkt_len < rx_copybreak &&
+                           (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
+                               /* 16 byte align the IP header */
+                               skb_reserve(skb, RX_OFFSET);
+                               pci_dma_sync_single_for_cpu(np->pci_dev,
+                                       np->rx_dma[entry],
+                                       buflen,
+                                       PCI_DMA_FROMDEVICE);
+                               skb_copy_to_linear_data(skb,
+                                       np->rx_skbuff[entry]->data, pkt_len);
+                               skb_put(skb, pkt_len);
+                               pci_dma_sync_single_for_device(np->pci_dev,
+                                       np->rx_dma[entry],
+                                       buflen,
+                                       PCI_DMA_FROMDEVICE);
+                       } else {
+                               pci_unmap_single(np->pci_dev, np->rx_dma[entry],
+                                                buflen + NATSEMI_PADDING,
+                                                PCI_DMA_FROMDEVICE);
+                               skb_put(skb = np->rx_skbuff[entry], pkt_len);
+                               np->rx_skbuff[entry] = NULL;
+                       }
+                       skb->protocol = eth_type_trans(skb, dev);
+                       netif_receive_skb(skb);
+                       dev->stats.rx_packets++;
+                       dev->stats.rx_bytes += pkt_len;
+               }
+               entry = (++np->cur_rx) % RX_RING_SIZE;
+               np->rx_head_desc = &np->rx_ring[entry];
+               desc_status = le32_to_cpu(np->rx_head_desc->cmd_status);
+       }
+       refill_rx(dev);
+
+       /* Restart Rx engine if stopped. */
+       if (np->oom)
+               mod_timer(&np->timer, jiffies + 1);
+       else
+               writel(RxOn, ioaddr + ChipCmd);
+}
+
+static void netdev_error(struct net_device *dev, int intr_status)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       spin_lock(&np->lock);
+       if (intr_status & LinkChange) {
+               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,
+                               np->advertising, lpa);
+               }
+
+               /* read MII int status to clear the flag */
+               readw(ioaddr + MIntrStatus);
+               check_link(dev);
+       }
+       if (intr_status & StatsMax) {
+               __get_stats(dev);
+       }
+       if (intr_status & IntrTxUnderrun) {
+               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)) {
+               int wol_status = readl(ioaddr + WOLCmd);
+               printk(KERN_NOTICE "%s: Link wake-up event %#08x\n",
+                       dev->name, wol_status);
+       }
+       if (intr_status & RxStatusFIFOOver) {
+               if (netif_msg_rx_err(np) && netif_msg_intr(np)) {
+                       printk(KERN_NOTICE "%s: Rx status FIFO overrun\n",
+                               dev->name);
+               }
+               dev->stats.rx_fifo_errors++;
+               dev->stats.rx_errors++;
+       }
+       /* Hmmmmm, it's not clear how to recover from PCI faults. */
+       if (intr_status & IntrPCIErr) {
+               printk(KERN_NOTICE "%s: PCI error %#08x\n", dev->name,
+                       intr_status & IntrPCIErr);
+               dev->stats.tx_fifo_errors++;
+               dev->stats.tx_errors++;
+               dev->stats.rx_fifo_errors++;
+               dev->stats.rx_errors++;
+       }
+       spin_unlock(&np->lock);
+}
+
+static void __get_stats(struct net_device *dev)
+{
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       /* The chip only need report frame silently dropped. */
+       dev->stats.rx_crc_errors += readl(ioaddr + RxCRCErrs);
+       dev->stats.rx_missed_errors += readl(ioaddr + RxMissed);
+}
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+
+       /* The chip only need report frame silently dropped. */
+       spin_lock_irq(&np->lock);
+       if (netif_running(dev) && !np->hands_off)
+               __get_stats(dev);
+       spin_unlock_irq(&np->lock);
+
+       return &dev->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)
+{
+       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. */
+               rx_mode = RxFilterEnable | AcceptBroadcast
+                       | AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
+       } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
+                  (dev->flags & IFF_ALLMULTI)) {
+               rx_mode = RxFilterEnable | AcceptBroadcast
+                       | AcceptAllMulticast | AcceptMyPhys;
+       } else {
+               struct netdev_hw_addr *ha;
+               int i;
+
+               memset(mc_filter, 0, sizeof(mc_filter));
+               netdev_for_each_mc_addr(ha, dev) {
+                       int b = (ether_crc(ETH_ALEN, ha->addr) >> 23) & 0x1ff;
+                       mc_filter[b/8] |= (1 << (b & 0x07));
+               }
+               rx_mode = RxFilterEnable | AcceptBroadcast
+                       | AcceptMulticast | AcceptMyPhys;
+               for (i = 0; i < 64; i += 2) {
+                       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 = netdev_priv(dev);
+       spin_lock_irq(&np->lock);
+       if (!np->hands_off)
+               __set_rx_mode(dev);
+       spin_unlock_irq(&np->lock);
+}
+
+static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       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);
+}
+
+static int get_regs_len(struct net_device *dev)
+{
+       return NATSEMI_REGS_SIZE;
+}
+
+static int get_eeprom_len(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       return np->eeprom_size;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+}
+
+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;
+}
+
+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);
+}
+
+static u32 get_msglevel(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       return np->msg_enable;
+}
+
+static void set_msglevel(struct net_device *dev, u32 val)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       np->msg_enable = val;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+
+       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 = 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)
+               data |= WakePhy;
+       if (newval & WAKE_UCAST)
+               data |= WakeUnicast;
+       if (newval & WAKE_MCAST)
+               data |= WakeMulticast;
+       if (newval & WAKE_BCAST)
+               data |= WakeBroadcast;
+       if (newval & WAKE_ARP)
+               data |= WakeArp;
+       if (newval & WAKE_MAGIC)
+               data |= WakeMagic;
+       if (np->srr >= SRR_DP83815_D) {
+               if (newval & WAKE_MAGICSECURE) {
+                       data |= WakeMagicSecure;
+               }
+       }
+
+       writel(data, ioaddr + WOLCmd);
+
+       return 0;
+}
+
+static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur)
+{
+       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);
+
+       if (np->srr >= SRR_DP83815_D) {
+               /* SOPASS works on revD and higher */
+               *supported |= WAKE_MAGICSECURE;
+       }
+       *cur = 0;
+
+       /* translate from chip bitmasks */
+       if (regval & WakePhy)
+               *cur |= WAKE_PHY;
+       if (regval & WakeUnicast)
+               *cur |= WAKE_UCAST;
+       if (regval & WakeMulticast)
+               *cur |= WAKE_MCAST;
+       if (regval & WakeBroadcast)
+               *cur |= WAKE_BCAST;
+       if (regval & WakeArp)
+               *cur |= WAKE_ARP;
+       if (regval & WakeMagic)
+               *cur |= WAKE_MAGIC;
+       if (regval & WakeMagicSecure) {
+               /* this can be on in revC, but it's broken */
+               *cur |= WAKE_MAGICSECURE;
+       }
+
+       return 0;
+}
+
+static int netdev_set_sopass(struct net_device *dev, u8 *newval)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       u16 *sval = (u16 *)newval;
+       u32 addr;
+
+       if (np->srr < SRR_DP83815_D) {
+               return 0;
+       }
+
+       /* enable writing to these registers by disabling the RX filter */
+       addr = readl(ioaddr + RxFilterAddr) & ~RFCRAddressMask;
+       addr &= ~RxFilterEnable;
+       writel(addr, ioaddr + RxFilterAddr);
+
+       /* write the three words to (undocumented) RFCR vals 0xa, 0xc, 0xe */
+       writel(addr | 0xa, ioaddr + RxFilterAddr);
+       writew(sval[0], ioaddr + RxFilterData);
+
+       writel(addr | 0xc, ioaddr + RxFilterAddr);
+       writew(sval[1], ioaddr + RxFilterData);
+
+       writel(addr | 0xe, ioaddr + RxFilterAddr);
+       writew(sval[2], ioaddr + RxFilterData);
+
+       /* re-enable the RX filter */
+       writel(addr | RxFilterEnable, ioaddr + RxFilterAddr);
+
+       return 0;
+}
+
+static int netdev_get_sopass(struct net_device *dev, u8 *data)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       u16 *sval = (u16 *)data;
+       u32 addr;
+
+       if (np->srr < SRR_DP83815_D) {
+               sval[0] = sval[1] = sval[2] = 0;
+               return 0;
+       }
+
+       /* read the three words from (undocumented) RFCR vals 0xa, 0xc, 0xe */
+       addr = readl(ioaddr + RxFilterAddr) & ~RFCRAddressMask;
+
+       writel(addr | 0xa, ioaddr + RxFilterAddr);
+       sval[0] = readw(ioaddr + RxFilterData);
+
+       writel(addr | 0xc, ioaddr + RxFilterAddr);
+       sval[1] = readw(ioaddr + RxFilterData);
+
+       writel(addr | 0xe, ioaddr + RxFilterAddr);
+       sval[2] = readw(ioaddr + RxFilterData);
+
+       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->port        = dev->if_port;
+       ethtool_cmd_speed_set(ecmd, np->speed);
+       ecmd->duplex      = np->duplex;
+       ecmd->autoneg     = np->autoneg;
+       ecmd->advertising = 0;
+       if (np->advertising & ADVERTISE_10HALF)
+               ecmd->advertising |= ADVERTISED_10baseT_Half;
+       if (np->advertising & ADVERTISE_10FULL)
+               ecmd->advertising |= ADVERTISED_10baseT_Full;
+       if (np->advertising & ADVERTISE_100HALF)
+               ecmd->advertising |= ADVERTISED_100baseT_Half;
+       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...
+        */
+
+       /* 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;
+               tmp = mii_nway_result(
+                       np->advertising & mdio_read(dev, MII_LPA));
+               if (tmp == LPA_100FULL || tmp == LPA_100HALF)
+                       ethtool_cmd_speed_set(ecmd, SPEED_100);
+               else
+                       ethtool_cmd_speed_set(ecmd, SPEED_10);
+               if (tmp == LPA_100FULL || tmp == LPA_10FULL)
+                       ecmd->duplex = DUPLEX_FULL;
+               else
+                       ecmd->duplex = DUPLEX_HALF;
+       }
+
+       /* ignore maxtxpkt, maxrxpkt for now */
+
+       return 0;
+}
+
+static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+       struct netdev_private *np = netdev_priv(dev);
+
+       if (ecmd->port != PORT_TP && ecmd->port != PORT_MII && ecmd->port != PORT_FIBRE)
+               return -EINVAL;
+       if (ecmd->transceiver != XCVR_INTERNAL && ecmd->transceiver != XCVR_EXTERNAL)
+               return -EINVAL;
+       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) {
+               u32 speed = ethtool_cmd_speed(ecmd);
+               if (speed != SPEED_10 && speed != SPEED_100)
+                       return -EINVAL;
+               if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+                       return -EINVAL;
+       } else {
+               return -EINVAL;
+       }
+
+       /*
+        * If we're ignoring the PHY then autoneg and the internal
+        * transceiver are really not going to work so don't let the
+        * user select them.
+        */
+       if (np->ignore_phy && (ecmd->autoneg == AUTONEG_ENABLE ||
+                              ecmd->port == PORT_TP))
+               return -EINVAL;
+
+       /*
+        * 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 */
+
+       /* 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 {
+               np->speed  = ethtool_cmd_speed(ecmd);
+               np->duplex = ecmd->duplex;
+               /* user overriding the initial full duplex parm? */
+               if (np->duplex == DUPLEX_HALF)
+                       np->full_duplex = 0;
+       }
+
+       /* 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;
+}
+
+static int netdev_get_regs(struct net_device *dev, u8 *buf)
+{
+       int i;
+       int j;
+       u32 rfcr;
+       u32 *rbuf = (u32 *)buf;
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       /* 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, 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(ioaddr + RxFilterAddr);
+       for (j = 0; j < NATSEMI_RFDR_NREGS; j++) {
+               writel(j*2, ioaddr + RxFilterAddr);
+               rbuf[i++] = readw(ioaddr + RxFilterData);
+       }
+       writel(rfcr, ioaddr + RxFilterAddr);
+
+       /* the interrupt status is clear-on-read - see if we missed any */
+       if (rbuf[4] & rbuf[5]) {
+               printk(KERN_WARNING
+                       "%s: shoot, we dropped an interrupt (%#08x)\n",
+                       dev->name, rbuf[4] & rbuf[5]);
+       }
+
+       return 0;
+}
+
+#define SWAP_BITS(x)   ( (((x) & 0x0001) << 15) | (((x) & 0x0002) << 13) \
+                       | (((x) & 0x0004) << 11) | (((x) & 0x0008) << 9)  \
+                       | (((x) & 0x0010) << 7)  | (((x) & 0x0020) << 5)  \
+                       | (((x) & 0x0040) << 3)  | (((x) & 0x0080) << 1)  \
+                       | (((x) & 0x0100) >> 1)  | (((x) & 0x0200) >> 3)  \
+                       | (((x) & 0x0400) >> 5)  | (((x) & 0x0800) >> 7)  \
+                       | (((x) & 0x1000) >> 9)  | (((x) & 0x2000) >> 11) \
+                       | (((x) & 0x4000) >> 13) | (((x) & 0x8000) >> 15) )
+
+static int netdev_get_eeprom(struct net_device *dev, u8 *buf)
+{
+       int i;
+       u16 *ebuf = (u16 *)buf;
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       struct netdev_private *np = netdev_priv(dev);
+
+       /* eeprom_read reads 16 bits, and indexes by 16 bits */
+       for (i = 0; i < np->eeprom_size/2; i++) {
+               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. */
+               ebuf[i] = SWAP_BITS(ebuf[i]);
+       }
+       return 0;
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct mii_ioctl_data *data = if_mii(rq);
+       struct netdev_private *np = netdev_priv(dev);
+
+       switch(cmd) {
+       case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
+               data->phy_id = np->phy_addr_external;
+               /* Fall Through */
+
+       case SIOCGMIIREG:               /* Read MII PHY register. */
+               /* 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. */
+               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;
+       }
+}
+
+static void enable_wol_mode(struct net_device *dev, int enable_intr)
+{
+       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",
+                       dev->name);
+
+       /* For WOL we must restart the rx process in silent mode.
+        * Write NULL to the RxRingPtr. Only possible if
+        * rx process is stopped
+        */
+       writel(0, ioaddr + RxRingPtr);
+
+       /* read WoL status to clear */
+       readl(ioaddr + WOLCmd);
+
+       /* PME on, clear status */
+       writel(np->SavedClkRun | PMEEnable | PMEStatus, ioaddr + ClkRun);
+
+       /* and restart the rx process */
+       writel(RxOn, ioaddr + ChipCmd);
+
+       if (enable_intr) {
+               /* enable the WOL interrupt.
+                * Could be used to send a netlink message.
+                */
+               writel(WOLPkt | LinkChange, ioaddr + IntrMask);
+               natsemi_irq_enable(dev);
+       }
+}
+
+static int netdev_close(struct net_device *dev)
+{
+       void __iomem * ioaddr = ns_ioaddr(dev);
+       struct netdev_private *np = netdev_priv(dev);
+
+       if (netif_msg_ifdown(np))
+               printk(KERN_DEBUG
+                       "%s: Shutting down ethercard, status was %#04x.\n",
+                       dev->name, (int)readl(ioaddr + ChipCmd));
+       if (netif_msg_pktdata(np))
+               printk(KERN_DEBUG
+                       "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
+                       dev->name, np->cur_tx, np->dirty_tx,
+                       np->cur_rx, np->dirty_rx);
+
+       napi_disable(&np->napi);
+
+       /*
+        * FIXME: what if someone tries to close a device
+        * that is suspended?
+        * Should we reenable the nic to switch to
+        * the final WOL settings?
+        */
+
+       del_timer_sync(&np->timer);
+       disable_irq(dev->irq);
+       spin_lock_irq(&np->lock);
+       natsemi_irq_disable(dev);
+       np->hands_off = 1;
+       spin_unlock_irq(&np->lock);
+       enable_irq(dev->irq);
+
+       free_irq(dev->irq, dev);
+
+       /* Interrupt disabled, interrupt handler released,
+        * queue stopped, timer deleted, rtnl_lock held
+        * All async codepaths that access the driver are disabled.
+        */
+       spin_lock_irq(&np->lock);
+       np->hands_off = 0;
+       readl(ioaddr + IntrMask);
+       readw(ioaddr + MIntrStatus);
+
+       /* Freeze Stats */
+       writel(StatsFreeze, ioaddr + StatsCtrl);
+
+       /* Stop the chip's Tx and Rx processes. */
+       natsemi_stop_rxtx(dev);
+
+       __get_stats(dev);
+       spin_unlock_irq(&np->lock);
+
+       /* clear the carrier last - an interrupt could reenable it otherwise */
+       netif_carrier_off(dev);
+       netif_stop_queue(dev);
+
+       dump_ring(dev);
+       drain_ring(dev);
+       free_ring(dev);
+
+       {
+               u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
+               if (wol) {
+                       /* restart the NIC in WOL mode.
+                        * The nic must be stopped for this.
+                        */
+                       enable_wol_mode(dev, 0);
+               } else {
+                       /* Restore PME enable bit unmolested */
+                       writel(np->SavedClkRun, ioaddr + ClkRun);
+               }
+       }
+       return 0;
+}
+
+
+static void __devexit natsemi_remove1 (struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       NATSEMI_REMOVE_FILE(pdev, dspcfg_workaround);
+       unregister_netdev (dev);
+       pci_release_regions (pdev);
+       iounmap(ioaddr);
+       free_netdev (dev);
+       pci_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+
+/*
+ * The ns83815 chip doesn't have explicit RxStop bits.
+ * Kicking the Rx or Tx process for a new packet reenables the Rx process
+ * of the nic, thus this function must be very careful:
+ *
+ * suspend/resume synchronization:
+ * entry points:
+ *   netdev_open, netdev_close, netdev_ioctl, set_rx_mode, intr_handler,
+ *   start_tx, ns_tx_timeout
+ *
+ * No function accesses the hardware without checking np->hands_off.
+ *     the check occurs under spin_lock_irq(&np->lock);
+ * exceptions:
+ *     * netdev_ioctl: noncritical access.
+ *     * netdev_open: cannot happen due to the device_detach
+ *     * netdev_close: doesn't hurt.
+ *     * 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
+ *              napi_disable().
+ *
+ * Interrupts must be disabled, otherwise hands_off can cause irq storms.
+ */
+
+static int natsemi_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+       struct net_device *dev = pci_get_drvdata (pdev);
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem * ioaddr = ns_ioaddr(dev);
+
+       rtnl_lock();
+       if (netif_running (dev)) {
+               del_timer_sync(&np->timer);
+
+               disable_irq(dev->irq);
+               spin_lock_irq(&np->lock);
+
+               natsemi_irq_disable(dev);
+               np->hands_off = 1;
+               natsemi_stop_rxtx(dev);
+               netif_stop_queue(dev);
+
+               spin_unlock_irq(&np->lock);
+               enable_irq(dev->irq);
+
+               napi_disable(&np->napi);
+
+               /* Update the error counts. */
+               __get_stats(dev);
+
+               /* pci_power_off(pdev, -1); */
+               drain_ring(dev);
+               {
+                       u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
+                       /* Restore PME enable bit */
+                       if (wol) {
+                               /* restart the NIC in WOL mode.
+                                * The nic must be stopped for this.
+                                * FIXME: use the WOL interrupt
+                                */
+                               enable_wol_mode(dev, 0);
+                       } else {
+                               /* Restore PME enable bit unmolested */
+                               writel(np->SavedClkRun, ioaddr + ClkRun);
+                       }
+               }
+       }
+       netif_device_detach(dev);
+       rtnl_unlock();
+       return 0;
+}
+
+
+static int natsemi_resume (struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata (pdev);
+       struct netdev_private *np = netdev_priv(dev);
+       int ret = 0;
+
+       rtnl_lock();
+       if (netif_device_present(dev))
+               goto out;
+       if (netif_running(dev)) {
+               BUG_ON(!np->hands_off);
+               ret = pci_enable_device(pdev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev,
+                               "pci_enable_device() failed: %d\n", ret);
+                       goto out;
+               }
+       /*      pci_power_on(pdev); */
+
+               napi_enable(&np->napi);
+
+               natsemi_reset(dev);
+               init_ring(dev);
+               disable_irq(dev->irq);
+               spin_lock_irq(&np->lock);
+               np->hands_off = 0;
+               init_registers(dev);
+               netif_device_attach(dev);
+               spin_unlock_irq(&np->lock);
+               enable_irq(dev->irq);
+
+               mod_timer(&np->timer, round_jiffies(jiffies + 1*HZ));
+       }
+       netif_device_attach(dev);
+out:
+       rtnl_unlock();
+       return ret;
+}
+
+#endif /* CONFIG_PM */
+
+static struct pci_driver natsemi_driver = {
+       .name           = DRV_NAME,
+       .id_table       = natsemi_pci_tbl,
+       .probe          = natsemi_probe1,
+       .remove         = __devexit_p(natsemi_remove1),
+#ifdef CONFIG_PM
+       .suspend        = natsemi_suspend,
+       .resume         = natsemi_resume,
+#endif
+};
+
+static int __init natsemi_init_mod (void)
+{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+       printk(version);
+#endif
+
+       return pci_register_driver(&natsemi_driver);
+}
+
+static void __exit natsemi_exit_mod (void)
+{
+       pci_unregister_driver (&natsemi_driver);
+}
+
+module_init(natsemi_init_mod);
+module_exit(natsemi_exit_mod);
+
diff --git a/drivers/net/ethernet/natsemi/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c
new file mode 100644 (file)
index 0000000..e736aec
--- /dev/null
@@ -0,0 +1,2312 @@
+#define VERSION "0.23"
+/* ns83820.c by Benjamin LaHaise with contributions.
+ *
+ * Questions/comments/discussion to linux-ns83820@kvack.org.
+ *
+ * $Revision: 1.34.2.23 $
+ *
+ * Copyright 2001 Benjamin LaHaise.
+ * Copyright 2001, 2002 Red Hat.
+ *
+ * Mmmm, chocolate vanilla mocha...
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * ChangeLog
+ * =========
+ *     20010414        0.1 - created
+ *     20010622        0.2 - basic rx and tx.
+ *     20010711        0.3 - added duplex and link state detection support.
+ *     20010713        0.4 - zero copy, no hangs.
+ *                     0.5 - 64 bit dma support (davem will hate me for this)
+ *                         - disable jumbo frames to avoid tx hangs
+ *                         - work around tx deadlocks on my 1.02 card via
+ *                           fiddling with TXCFG
+ *     20010810        0.6 - use pci dma api for ringbuffers, work on ia64
+ *     20010816        0.7 - misc cleanups
+ *     20010826        0.8 - fix critical zero copy bugs
+ *                     0.9 - internal experiment
+ *     20010827        0.10 - fix ia64 unaligned access.
+ *     20010906        0.11 - accept all packets with checksum errors as
+ *                            otherwise fragments get lost
+ *                          - fix >> 32 bugs
+ *                     0.12 - add statistics counters
+ *                          - add allmulti/promisc support
+ *     20011009        0.13 - hotplug support, other smaller pci api cleanups
+ *     20011204        0.13a - optical transceiver support added
+ *                             by Michael Clark <michael@metaparadigm.com>
+ *     20011205        0.13b - call register_netdev earlier in initialization
+ *                             suppress duplicate link status messages
+ *     20011117        0.14 - ethtool GDRVINFO, GLINK support from jgarzik
+ *     20011204        0.15    get ppc (big endian) working
+ *     20011218        0.16    various cleanups
+ *     20020310        0.17    speedups
+ *     20020610        0.18 -  actually use the pci dma api for highmem
+ *                          -  remove pci latency register fiddling
+ *                     0.19 -  better bist support
+ *                          -  add ihr and reset_phy parameters
+ *                          -  gmii bus probing
+ *                          -  fix missed txok introduced during performance
+ *                             tuning
+ *                     0.20 -  fix stupid RFEN thinko.  i am such a smurf.
+ *     20040828        0.21 -  add hardware vlan accleration
+ *                             by Neil Horman <nhorman@redhat.com>
+ *     20050406        0.22 -  improved DAC ifdefs from Andi Kleen
+ *                          -  removal of dead code from Adrian Bunk
+ *                          -  fix half duplex collision behaviour
+ * Driver Overview
+ * ===============
+ *
+ * This driver was originally written for the National Semiconductor
+ * 83820 chip, a 10/100/1000 Mbps 64 bit PCI ethernet NIC.  Hopefully
+ * this code will turn out to be a) clean, b) correct, and c) fast.
+ * With that in mind, I'm aiming to split the code up as much as
+ * reasonably possible.  At present there are X major sections that
+ * break down into a) packet receive, b) packet transmit, c) link
+ * management, d) initialization and configuration.  Where possible,
+ * these code paths are designed to run in parallel.
+ *
+ * This driver has been tested and found to work with the following
+ * cards (in no particular order):
+ *
+ *     Cameo           SOHO-GA2000T    SOHO-GA2500T
+ *     D-Link          DGE-500T
+ *     PureData        PDP8023Z-TG
+ *     SMC             SMC9452TX       SMC9462TX
+ *     Netgear         GA621
+ *
+ * Special thanks to SMC for providing hardware to test this driver on.
+ *
+ * Reports of success or failure would be greatly appreciated.
+ */
+//#define dprintk              printk
+#define dprintk(x...)          do { } while (0)
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>  /* for iph */
+#include <linux/in.h>  /* for IPPROTO_... */
+#include <linux/compiler.h>
+#include <linux/prefetch.h>
+#include <linux/ethtool.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/if_vlan.h>
+#include <linux/rtnetlink.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#define DRV_NAME "ns83820"
+
+/* Global parameters.  See module_param near the bottom. */
+static int ihr = 2;
+static int reset_phy = 0;
+static int lnksts = 0;         /* CFG_LNKSTS bit polarity */
+
+/* Dprintk is used for more interesting debug events */
+#undef Dprintk
+#define        Dprintk                 dprintk
+
+/* tunables */
+#define RX_BUF_SIZE    1500    /* 8192 */
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define NS83820_VLAN_ACCEL_SUPPORT
+#endif
+
+/* Must not exceed ~65000. */
+#define NR_RX_DESC     64
+#define NR_TX_DESC     128
+
+/* not tunable */
+#define REAL_RX_BUF_SIZE (RX_BUF_SIZE + 14)    /* rx/tx mac addr + type */
+
+#define MIN_TX_DESC_FREE       8
+
+/* register defines */
+#define CFGCS          0x04
+
+#define CR_TXE         0x00000001
+#define CR_TXD         0x00000002
+/* Ramit : Here's a tip, don't do a RXD immediately followed by an RXE
+ * The Receive engine skips one descriptor and moves
+ * onto the next one!! */
+#define CR_RXE         0x00000004
+#define CR_RXD         0x00000008
+#define CR_TXR         0x00000010
+#define CR_RXR         0x00000020
+#define CR_SWI         0x00000080
+#define CR_RST         0x00000100
+
+#define PTSCR_EEBIST_FAIL       0x00000001
+#define PTSCR_EEBIST_EN         0x00000002
+#define PTSCR_EELOAD_EN         0x00000004
+#define PTSCR_RBIST_FAIL        0x000001b8
+#define PTSCR_RBIST_DONE        0x00000200
+#define PTSCR_RBIST_EN          0x00000400
+#define PTSCR_RBIST_RST         0x00002000
+
+#define MEAR_EEDI              0x00000001
+#define MEAR_EEDO              0x00000002
+#define MEAR_EECLK             0x00000004
+#define MEAR_EESEL             0x00000008
+#define MEAR_MDIO              0x00000010
+#define MEAR_MDDIR             0x00000020
+#define MEAR_MDC               0x00000040
+
+#define ISR_TXDESC3    0x40000000
+#define ISR_TXDESC2    0x20000000
+#define ISR_TXDESC1    0x10000000
+#define ISR_TXDESC0    0x08000000
+#define ISR_RXDESC3    0x04000000
+#define ISR_RXDESC2    0x02000000
+#define ISR_RXDESC1    0x01000000
+#define ISR_RXDESC0    0x00800000
+#define ISR_TXRCMP     0x00400000
+#define ISR_RXRCMP     0x00200000
+#define ISR_DPERR      0x00100000
+#define ISR_SSERR      0x00080000
+#define ISR_RMABT      0x00040000
+#define ISR_RTABT      0x00020000
+#define ISR_RXSOVR     0x00010000
+#define ISR_HIBINT     0x00008000
+#define ISR_PHY                0x00004000
+#define ISR_PME                0x00002000
+#define ISR_SWI                0x00001000
+#define ISR_MIB                0x00000800
+#define ISR_TXURN      0x00000400
+#define ISR_TXIDLE     0x00000200
+#define ISR_TXERR      0x00000100
+#define ISR_TXDESC     0x00000080
+#define ISR_TXOK       0x00000040
+#define ISR_RXORN      0x00000020
+#define ISR_RXIDLE     0x00000010
+#define ISR_RXEARLY    0x00000008
+#define ISR_RXERR      0x00000004
+#define ISR_RXDESC     0x00000002
+#define ISR_RXOK       0x00000001
+
+#define TXCFG_CSI      0x80000000
+#define TXCFG_HBI      0x40000000
+#define TXCFG_MLB      0x20000000
+#define TXCFG_ATP      0x10000000
+#define TXCFG_ECRETRY  0x00800000
+#define TXCFG_BRST_DIS 0x00080000
+#define TXCFG_MXDMA1024        0x00000000
+#define TXCFG_MXDMA512 0x00700000
+#define TXCFG_MXDMA256 0x00600000
+#define TXCFG_MXDMA128 0x00500000
+#define TXCFG_MXDMA64  0x00400000
+#define TXCFG_MXDMA32  0x00300000
+#define TXCFG_MXDMA16  0x00200000
+#define TXCFG_MXDMA8   0x00100000
+
+#define CFG_LNKSTS     0x80000000
+#define CFG_SPDSTS     0x60000000
+#define CFG_SPDSTS1    0x40000000
+#define CFG_SPDSTS0    0x20000000
+#define CFG_DUPSTS     0x10000000
+#define CFG_TBI_EN     0x01000000
+#define CFG_MODE_1000  0x00400000
+/* Ramit : Dont' ever use AUTO_1000, it never works and is buggy.
+ * Read the Phy response and then configure the MAC accordingly */
+#define CFG_AUTO_1000  0x00200000
+#define CFG_PINT_CTL   0x001c0000
+#define CFG_PINT_DUPSTS        0x00100000
+#define CFG_PINT_LNKSTS        0x00080000
+#define CFG_PINT_SPDSTS        0x00040000
+#define CFG_TMRTEST    0x00020000
+#define CFG_MRM_DIS    0x00010000
+#define CFG_MWI_DIS    0x00008000
+#define CFG_T64ADDR    0x00004000
+#define CFG_PCI64_DET  0x00002000
+#define CFG_DATA64_EN  0x00001000
+#define CFG_M64ADDR    0x00000800
+#define CFG_PHY_RST    0x00000400
+#define CFG_PHY_DIS    0x00000200
+#define CFG_EXTSTS_EN  0x00000100
+#define CFG_REQALG     0x00000080
+#define CFG_SB         0x00000040
+#define CFG_POW                0x00000020
+#define CFG_EXD                0x00000010
+#define CFG_PESEL      0x00000008
+#define CFG_BROM_DIS   0x00000004
+#define CFG_EXT_125    0x00000002
+#define CFG_BEM                0x00000001
+
+#define EXTSTS_UDPPKT  0x00200000
+#define EXTSTS_TCPPKT  0x00080000
+#define EXTSTS_IPPKT   0x00020000
+#define EXTSTS_VPKT    0x00010000
+#define EXTSTS_VTG_MASK        0x0000ffff
+
+#define SPDSTS_POLARITY        (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0))
+
+#define MIBC_MIBS      0x00000008
+#define MIBC_ACLR      0x00000004
+#define MIBC_FRZ       0x00000002
+#define MIBC_WRN       0x00000001
+
+#define PCR_PSEN       (1 << 31)
+#define PCR_PS_MCAST   (1 << 30)
+#define PCR_PS_DA      (1 << 29)
+#define PCR_STHI_8     (3 << 23)
+#define PCR_STLO_4     (1 << 23)
+#define PCR_FFHI_8K    (3 << 21)
+#define PCR_FFLO_4K    (1 << 21)
+#define PCR_PAUSE_CNT  0xFFFE
+
+#define RXCFG_AEP      0x80000000
+#define RXCFG_ARP      0x40000000
+#define RXCFG_STRIPCRC 0x20000000
+#define RXCFG_RX_FD    0x10000000
+#define RXCFG_ALP      0x08000000
+#define RXCFG_AIRL     0x04000000
+#define RXCFG_MXDMA512 0x00700000
+#define RXCFG_DRTH     0x0000003e
+#define RXCFG_DRTH0    0x00000002
+
+#define RFCR_RFEN      0x80000000
+#define RFCR_AAB       0x40000000
+#define RFCR_AAM       0x20000000
+#define RFCR_AAU       0x10000000
+#define RFCR_APM       0x08000000
+#define RFCR_APAT      0x07800000
+#define RFCR_APAT3     0x04000000
+#define RFCR_APAT2     0x02000000
+#define RFCR_APAT1     0x01000000
+#define RFCR_APAT0     0x00800000
+#define RFCR_AARP      0x00400000
+#define RFCR_MHEN      0x00200000
+#define RFCR_UHEN      0x00100000
+#define RFCR_ULM       0x00080000
+
+#define VRCR_RUDPE     0x00000080
+#define VRCR_RTCPE     0x00000040
+#define VRCR_RIPE      0x00000020
+#define VRCR_IPEN      0x00000010
+#define VRCR_DUTF      0x00000008
+#define VRCR_DVTF      0x00000004
+#define VRCR_VTREN     0x00000002
+#define VRCR_VTDEN     0x00000001
+
+#define VTCR_PPCHK     0x00000008
+#define VTCR_GCHK      0x00000004
+#define VTCR_VPPTI     0x00000002
+#define VTCR_VGTI      0x00000001
+
+#define CR             0x00
+#define CFG            0x04
+#define MEAR           0x08
+#define PTSCR          0x0c
+#define        ISR             0x10
+#define        IMR             0x14
+#define        IER             0x18
+#define        IHR             0x1c
+#define TXDP           0x20
+#define TXDP_HI                0x24
+#define TXCFG          0x28
+#define GPIOR          0x2c
+#define RXDP           0x30
+#define RXDP_HI                0x34
+#define RXCFG          0x38
+#define PQCR           0x3c
+#define WCSR           0x40
+#define PCR            0x44
+#define RFCR           0x48
+#define RFDR           0x4c
+
+#define SRR            0x58
+
+#define VRCR           0xbc
+#define VTCR           0xc0
+#define VDR            0xc4
+#define CCSR           0xcc
+
+#define TBICR          0xe0
+#define TBISR          0xe4
+#define TANAR          0xe8
+#define TANLPAR                0xec
+#define TANER          0xf0
+#define TESR           0xf4
+
+#define TBICR_MR_AN_ENABLE     0x00001000
+#define TBICR_MR_RESTART_AN    0x00000200
+
+#define TBISR_MR_LINK_STATUS   0x00000020
+#define TBISR_MR_AN_COMPLETE   0x00000004
+
+#define TANAR_PS2              0x00000100
+#define TANAR_PS1              0x00000080
+#define TANAR_HALF_DUP                 0x00000040
+#define TANAR_FULL_DUP                 0x00000020
+
+#define GPIOR_GP5_OE           0x00000200
+#define GPIOR_GP4_OE           0x00000100
+#define GPIOR_GP3_OE           0x00000080
+#define GPIOR_GP2_OE           0x00000040
+#define GPIOR_GP1_OE           0x00000020
+#define GPIOR_GP3_OUT          0x00000004
+#define GPIOR_GP1_OUT          0x00000001
+
+#define LINK_AUTONEGOTIATE     0x01
+#define LINK_DOWN              0x02
+#define LINK_UP                        0x04
+
+#define HW_ADDR_LEN    sizeof(dma_addr_t)
+#define desc_addr_set(desc, addr)                              \
+       do {                                                    \
+               ((desc)[0] = cpu_to_le32(addr));                \
+               if (HW_ADDR_LEN == 8)                           \
+                       (desc)[1] = cpu_to_le32(((u64)addr) >> 32);     \
+       } while(0)
+#define desc_addr_get(desc)                                    \
+       (le32_to_cpu((desc)[0]) | \
+       (HW_ADDR_LEN == 8 ? ((dma_addr_t)le32_to_cpu((desc)[1]))<<32 : 0))
+
+#define DESC_LINK              0
+#define DESC_BUFPTR            (DESC_LINK + HW_ADDR_LEN/4)
+#define DESC_CMDSTS            (DESC_BUFPTR + HW_ADDR_LEN/4)
+#define DESC_EXTSTS            (DESC_CMDSTS + 4/4)
+
+#define CMDSTS_OWN     0x80000000
+#define CMDSTS_MORE    0x40000000
+#define CMDSTS_INTR    0x20000000
+#define CMDSTS_ERR     0x10000000
+#define CMDSTS_OK      0x08000000
+#define CMDSTS_RUNT    0x00200000
+#define CMDSTS_LEN_MASK        0x0000ffff
+
+#define CMDSTS_DEST_MASK       0x01800000
+#define CMDSTS_DEST_SELF       0x00800000
+#define CMDSTS_DEST_MULTI      0x01000000
+
+#define DESC_SIZE      8               /* Should be cache line sized */
+
+struct rx_info {
+       spinlock_t      lock;
+       int             up;
+       unsigned long   idle;
+
+       struct sk_buff  *skbs[NR_RX_DESC];
+
+       __le32          *next_rx_desc;
+       u16             next_rx, next_empty;
+
+       __le32          *descs;
+       dma_addr_t      phy_descs;
+};
+
+
+struct ns83820 {
+       u8                      __iomem *base;
+
+       struct pci_dev          *pci_dev;
+       struct net_device       *ndev;
+
+       struct rx_info          rx_info;
+       struct tasklet_struct   rx_tasklet;
+
+       unsigned                ihr;
+       struct work_struct      tq_refill;
+
+       /* protects everything below.  irqsave when using. */
+       spinlock_t              misc_lock;
+
+       u32                     CFG_cache;
+
+       u32                     MEAR_cache;
+       u32                     IMR_cache;
+
+       unsigned                linkstate;
+
+       spinlock_t      tx_lock;
+
+       u16             tx_done_idx;
+       u16             tx_idx;
+       volatile u16    tx_free_idx;    /* idx of free desc chain */
+       u16             tx_intr_idx;
+
+       atomic_t        nr_tx_skbs;
+       struct sk_buff  *tx_skbs[NR_TX_DESC];
+
+       char            pad[16] __attribute__((aligned(16)));
+       __le32          *tx_descs;
+       dma_addr_t      tx_phy_descs;
+
+       struct timer_list       tx_watchdog;
+};
+
+static inline struct ns83820 *PRIV(struct net_device *dev)
+{
+       return netdev_priv(dev);
+}
+
+#define __kick_rx(dev) writel(CR_RXE, dev->base + CR)
+
+static inline void kick_rx(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       dprintk("kick_rx: maybe kicking\n");
+       if (test_and_clear_bit(0, &dev->rx_info.idle)) {
+               dprintk("actually kicking\n");
+               writel(dev->rx_info.phy_descs +
+                       (4 * DESC_SIZE * dev->rx_info.next_rx),
+                      dev->base + RXDP);
+               if (dev->rx_info.next_rx == dev->rx_info.next_empty)
+                       printk(KERN_DEBUG "%s: uh-oh: next_rx == next_empty???\n",
+                               ndev->name);
+               __kick_rx(dev);
+       }
+}
+
+//free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC
+#define start_tx_okay(dev)     \
+       (((NR_TX_DESC-2 + dev->tx_done_idx - dev->tx_free_idx) % NR_TX_DESC) > MIN_TX_DESC_FREE)
+
+/* Packet Receiver
+ *
+ * The hardware supports linked lists of receive descriptors for
+ * which ownership is transferred back and forth by means of an
+ * ownership bit.  While the hardware does support the use of a
+ * ring for receive descriptors, we only make use of a chain in
+ * an attempt to reduce bus traffic under heavy load scenarios.
+ * This will also make bugs a bit more obvious.  The current code
+ * only makes use of a single rx chain; I hope to implement
+ * priority based rx for version 1.0.  Goal: even under overload
+ * conditions, still route realtime traffic with as low jitter as
+ * possible.
+ */
+static inline void build_rx_desc(struct ns83820 *dev, __le32 *desc, dma_addr_t link, dma_addr_t buf, u32 cmdsts, u32 extsts)
+{
+       desc_addr_set(desc + DESC_LINK, link);
+       desc_addr_set(desc + DESC_BUFPTR, buf);
+       desc[DESC_EXTSTS] = cpu_to_le32(extsts);
+       mb();
+       desc[DESC_CMDSTS] = cpu_to_le32(cmdsts);
+}
+
+#define nr_rx_empty(dev) ((NR_RX_DESC-2 + dev->rx_info.next_rx - dev->rx_info.next_empty) % NR_RX_DESC)
+static inline int ns83820_add_rx_skb(struct ns83820 *dev, struct sk_buff *skb)
+{
+       unsigned next_empty;
+       u32 cmdsts;
+       __le32 *sg;
+       dma_addr_t buf;
+
+       next_empty = dev->rx_info.next_empty;
+
+       /* don't overrun last rx marker */
+       if (unlikely(nr_rx_empty(dev) <= 2)) {
+               kfree_skb(skb);
+               return 1;
+       }
+
+#if 0
+       dprintk("next_empty[%d] nr_used[%d] next_rx[%d]\n",
+               dev->rx_info.next_empty,
+               dev->rx_info.nr_used,
+               dev->rx_info.next_rx
+               );
+#endif
+
+       sg = dev->rx_info.descs + (next_empty * DESC_SIZE);
+       BUG_ON(NULL != dev->rx_info.skbs[next_empty]);
+       dev->rx_info.skbs[next_empty] = skb;
+
+       dev->rx_info.next_empty = (next_empty + 1) % NR_RX_DESC;
+       cmdsts = REAL_RX_BUF_SIZE | CMDSTS_INTR;
+       buf = pci_map_single(dev->pci_dev, skb->data,
+                            REAL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+       build_rx_desc(dev, sg, 0, buf, cmdsts, 0);
+       /* update link of previous rx */
+       if (likely(next_empty != dev->rx_info.next_rx))
+               dev->rx_info.descs[((NR_RX_DESC + next_empty - 1) % NR_RX_DESC) * DESC_SIZE] = cpu_to_le32(dev->rx_info.phy_descs + (next_empty * DESC_SIZE * 4));
+
+       return 0;
+}
+
+static inline int rx_refill(struct net_device *ndev, gfp_t gfp)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       unsigned i;
+       unsigned long flags = 0;
+
+       if (unlikely(nr_rx_empty(dev) <= 2))
+               return 0;
+
+       dprintk("rx_refill(%p)\n", ndev);
+       if (gfp == GFP_ATOMIC)
+               spin_lock_irqsave(&dev->rx_info.lock, flags);
+       for (i=0; i<NR_RX_DESC; i++) {
+               struct sk_buff *skb;
+               long res;
+
+               /* extra 16 bytes for alignment */
+               skb = __netdev_alloc_skb(ndev, REAL_RX_BUF_SIZE+16, gfp);
+               if (unlikely(!skb))
+                       break;
+
+               skb_reserve(skb, skb->data - PTR_ALIGN(skb->data, 16));
+               if (gfp != GFP_ATOMIC)
+                       spin_lock_irqsave(&dev->rx_info.lock, flags);
+               res = ns83820_add_rx_skb(dev, skb);
+               if (gfp != GFP_ATOMIC)
+                       spin_unlock_irqrestore(&dev->rx_info.lock, flags);
+               if (res) {
+                       i = 1;
+                       break;
+               }
+       }
+       if (gfp == GFP_ATOMIC)
+               spin_unlock_irqrestore(&dev->rx_info.lock, flags);
+
+       return i ? 0 : -ENOMEM;
+}
+
+static void rx_refill_atomic(struct net_device *ndev)
+{
+       rx_refill(ndev, GFP_ATOMIC);
+}
+
+/* REFILL */
+static inline void queue_refill(struct work_struct *work)
+{
+       struct ns83820 *dev = container_of(work, struct ns83820, tq_refill);
+       struct net_device *ndev = dev->ndev;
+
+       rx_refill(ndev, GFP_KERNEL);
+       if (dev->rx_info.up)
+               kick_rx(ndev);
+}
+
+static inline void clear_rx_desc(struct ns83820 *dev, unsigned i)
+{
+       build_rx_desc(dev, dev->rx_info.descs + (DESC_SIZE * i), 0, 0, CMDSTS_OWN, 0);
+}
+
+static void phy_intr(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       static const char *speeds[] = { "10", "100", "1000", "1000(?)", "1000F" };
+       u32 cfg, new_cfg;
+       u32 tbisr, tanar, tanlpar;
+       int speed, fullduplex, newlinkstate;
+
+       cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
+
+       if (dev->CFG_cache & CFG_TBI_EN) {
+               /* we have an optical transceiver */
+               tbisr = readl(dev->base + TBISR);
+               tanar = readl(dev->base + TANAR);
+               tanlpar = readl(dev->base + TANLPAR);
+               dprintk("phy_intr: tbisr=%08x, tanar=%08x, tanlpar=%08x\n",
+                       tbisr, tanar, tanlpar);
+
+               if ( (fullduplex = (tanlpar & TANAR_FULL_DUP) &&
+                     (tanar & TANAR_FULL_DUP)) ) {
+
+                       /* both of us are full duplex */
+                       writel(readl(dev->base + TXCFG)
+                              | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
+                              dev->base + TXCFG);
+                       writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
+                              dev->base + RXCFG);
+                       /* Light up full duplex LED */
+                       writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT,
+                              dev->base + GPIOR);
+
+               } else if (((tanlpar & TANAR_HALF_DUP) &&
+                           (tanar & TANAR_HALF_DUP)) ||
+                          ((tanlpar & TANAR_FULL_DUP) &&
+                           (tanar & TANAR_HALF_DUP)) ||
+                          ((tanlpar & TANAR_HALF_DUP) &&
+                           (tanar & TANAR_FULL_DUP))) {
+
+                       /* one or both of us are half duplex */
+                       writel((readl(dev->base + TXCFG)
+                               & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP,
+                              dev->base + TXCFG);
+                       writel(readl(dev->base + RXCFG) & ~RXCFG_RX_FD,
+                              dev->base + RXCFG);
+                       /* Turn off full duplex LED */
+                       writel(readl(dev->base + GPIOR) & ~GPIOR_GP1_OUT,
+                              dev->base + GPIOR);
+               }
+
+               speed = 4; /* 1000F */
+
+       } else {
+               /* we have a copper transceiver */
+               new_cfg = dev->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS);
+
+               if (cfg & CFG_SPDSTS1)
+                       new_cfg |= CFG_MODE_1000;
+               else
+                       new_cfg &= ~CFG_MODE_1000;
+
+               speed = ((cfg / CFG_SPDSTS0) & 3);
+               fullduplex = (cfg & CFG_DUPSTS);
+
+               if (fullduplex) {
+                       new_cfg |= CFG_SB;
+                       writel(readl(dev->base + TXCFG)
+                                       | TXCFG_CSI | TXCFG_HBI,
+                              dev->base + TXCFG);
+                       writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
+                              dev->base + RXCFG);
+               } else {
+                       writel(readl(dev->base + TXCFG)
+                                       & ~(TXCFG_CSI | TXCFG_HBI),
+                              dev->base + TXCFG);
+                       writel(readl(dev->base + RXCFG) & ~(RXCFG_RX_FD),
+                              dev->base + RXCFG);
+               }
+
+               if ((cfg & CFG_LNKSTS) &&
+                   ((new_cfg ^ dev->CFG_cache) != 0)) {
+                       writel(new_cfg, dev->base + CFG);
+                       dev->CFG_cache = new_cfg;
+               }
+
+               dev->CFG_cache &= ~CFG_SPDSTS;
+               dev->CFG_cache |= cfg & CFG_SPDSTS;
+       }
+
+       newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN;
+
+       if (newlinkstate & LINK_UP &&
+           dev->linkstate != newlinkstate) {
+               netif_start_queue(ndev);
+               netif_wake_queue(ndev);
+               printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n",
+                       ndev->name,
+                       speeds[speed],
+                       fullduplex ? "full" : "half");
+       } else if (newlinkstate & LINK_DOWN &&
+                  dev->linkstate != newlinkstate) {
+               netif_stop_queue(ndev);
+               printk(KERN_INFO "%s: link now down.\n", ndev->name);
+       }
+
+       dev->linkstate = newlinkstate;
+}
+
+static int ns83820_setup_rx(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       unsigned i;
+       int ret;
+
+       dprintk("ns83820_setup_rx(%p)\n", ndev);
+
+       dev->rx_info.idle = 1;
+       dev->rx_info.next_rx = 0;
+       dev->rx_info.next_rx_desc = dev->rx_info.descs;
+       dev->rx_info.next_empty = 0;
+
+       for (i=0; i<NR_RX_DESC; i++)
+               clear_rx_desc(dev, i);
+
+       writel(0, dev->base + RXDP_HI);
+       writel(dev->rx_info.phy_descs, dev->base + RXDP);
+
+       ret = rx_refill(ndev, GFP_KERNEL);
+       if (!ret) {
+               dprintk("starting receiver\n");
+               /* prevent the interrupt handler from stomping on us */
+               spin_lock_irq(&dev->rx_info.lock);
+
+               writel(0x0001, dev->base + CCSR);
+               writel(0, dev->base + RFCR);
+               writel(0x7fc00000, dev->base + RFCR);
+               writel(0xffc00000, dev->base + RFCR);
+
+               dev->rx_info.up = 1;
+
+               phy_intr(ndev);
+
+               /* Okay, let it rip */
+               spin_lock(&dev->misc_lock);
+               dev->IMR_cache |= ISR_PHY;
+               dev->IMR_cache |= ISR_RXRCMP;
+               //dev->IMR_cache |= ISR_RXERR;
+               //dev->IMR_cache |= ISR_RXOK;
+               dev->IMR_cache |= ISR_RXORN;
+               dev->IMR_cache |= ISR_RXSOVR;
+               dev->IMR_cache |= ISR_RXDESC;
+               dev->IMR_cache |= ISR_RXIDLE;
+               dev->IMR_cache |= ISR_TXDESC;
+               dev->IMR_cache |= ISR_TXIDLE;
+
+               writel(dev->IMR_cache, dev->base + IMR);
+               writel(1, dev->base + IER);
+               spin_unlock(&dev->misc_lock);
+
+               kick_rx(ndev);
+
+               spin_unlock_irq(&dev->rx_info.lock);
+       }
+       return ret;
+}
+
+static void ns83820_cleanup_rx(struct ns83820 *dev)
+{
+       unsigned i;
+       unsigned long flags;
+
+       dprintk("ns83820_cleanup_rx(%p)\n", dev);
+
+       /* disable receive interrupts */
+       spin_lock_irqsave(&dev->misc_lock, flags);
+       dev->IMR_cache &= ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY | ISR_RXIDLE);
+       writel(dev->IMR_cache, dev->base + IMR);
+       spin_unlock_irqrestore(&dev->misc_lock, flags);
+
+       /* synchronize with the interrupt handler and kill it */
+       dev->rx_info.up = 0;
+       synchronize_irq(dev->pci_dev->irq);
+
+       /* touch the pci bus... */
+       readl(dev->base + IMR);
+
+       /* assumes the transmitter is already disabled and reset */
+       writel(0, dev->base + RXDP_HI);
+       writel(0, dev->base + RXDP);
+
+       for (i=0; i<NR_RX_DESC; i++) {
+               struct sk_buff *skb = dev->rx_info.skbs[i];
+               dev->rx_info.skbs[i] = NULL;
+               clear_rx_desc(dev, i);
+               kfree_skb(skb);
+       }
+}
+
+static void ns83820_rx_kick(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       /*if (nr_rx_empty(dev) >= NR_RX_DESC/4)*/ {
+               if (dev->rx_info.up) {
+                       rx_refill_atomic(ndev);
+                       kick_rx(ndev);
+               }
+       }
+
+       if (dev->rx_info.up && nr_rx_empty(dev) > NR_RX_DESC*3/4)
+               schedule_work(&dev->tq_refill);
+       else
+               kick_rx(ndev);
+       if (dev->rx_info.idle)
+               printk(KERN_DEBUG "%s: BAD\n", ndev->name);
+}
+
+/* rx_irq
+ *
+ */
+static void rx_irq(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       struct rx_info *info = &dev->rx_info;
+       unsigned next_rx;
+       int rx_rc, len;
+       u32 cmdsts;
+       __le32 *desc;
+       unsigned long flags;
+       int nr = 0;
+
+       dprintk("rx_irq(%p)\n", ndev);
+       dprintk("rxdp: %08x, descs: %08lx next_rx[%d]: %p next_empty[%d]: %p\n",
+               readl(dev->base + RXDP),
+               (long)(dev->rx_info.phy_descs),
+               (int)dev->rx_info.next_rx,
+               (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_rx)),
+               (int)dev->rx_info.next_empty,
+               (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_empty))
+               );
+
+       spin_lock_irqsave(&info->lock, flags);
+       if (!info->up)
+               goto out;
+
+       dprintk("walking descs\n");
+       next_rx = info->next_rx;
+       desc = info->next_rx_desc;
+       while ((CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) &&
+              (cmdsts != CMDSTS_OWN)) {
+               struct sk_buff *skb;
+               u32 extsts = le32_to_cpu(desc[DESC_EXTSTS]);
+               dma_addr_t bufptr = desc_addr_get(desc + DESC_BUFPTR);
+
+               dprintk("cmdsts: %08x\n", cmdsts);
+               dprintk("link: %08x\n", cpu_to_le32(desc[DESC_LINK]));
+               dprintk("extsts: %08x\n", extsts);
+
+               skb = info->skbs[next_rx];
+               info->skbs[next_rx] = NULL;
+               info->next_rx = (next_rx + 1) % NR_RX_DESC;
+
+               mb();
+               clear_rx_desc(dev, next_rx);
+
+               pci_unmap_single(dev->pci_dev, bufptr,
+                                RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+               len = cmdsts & CMDSTS_LEN_MASK;
+#ifdef NS83820_VLAN_ACCEL_SUPPORT
+               /* NH: As was mentioned below, this chip is kinda
+                * brain dead about vlan tag stripping.  Frames
+                * that are 64 bytes with a vlan header appended
+                * like arp frames, or pings, are flagged as Runts
+                * when the tag is stripped and hardware.  This
+                * also means that the OK bit in the descriptor
+                * is cleared when the frame comes in so we have
+                * to do a specific length check here to make sure
+                * the frame would have been ok, had we not stripped
+                * the tag.
+                */
+               if (likely((CMDSTS_OK & cmdsts) ||
+                       ((cmdsts & CMDSTS_RUNT) && len >= 56))) {
+#else
+               if (likely(CMDSTS_OK & cmdsts)) {
+#endif
+                       skb_put(skb, len);
+                       if (unlikely(!skb))
+                               goto netdev_mangle_me_harder_failed;
+                       if (cmdsts & CMDSTS_DEST_MULTI)
+                               ndev->stats.multicast++;
+                       ndev->stats.rx_packets++;
+                       ndev->stats.rx_bytes += len;
+                       if ((extsts & 0x002a0000) && !(extsts & 0x00540000)) {
+                               skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       } else {
+                               skb_checksum_none_assert(skb);
+                       }
+                       skb->protocol = eth_type_trans(skb, ndev);
+#ifdef NS83820_VLAN_ACCEL_SUPPORT
+                       if(extsts & EXTSTS_VPKT) {
+                               unsigned short tag;
+
+                               tag = ntohs(extsts & EXTSTS_VTG_MASK);
+                               __vlan_hwaccel_put_tag(skb, tag);
+                       }
+#endif
+                       rx_rc = netif_rx(skb);
+                       if (NET_RX_DROP == rx_rc) {
+netdev_mangle_me_harder_failed:
+                               ndev->stats.rx_dropped++;
+                       }
+               } else {
+                       kfree_skb(skb);
+               }
+
+               nr++;
+               next_rx = info->next_rx;
+               desc = info->descs + (DESC_SIZE * next_rx);
+       }
+       info->next_rx = next_rx;
+       info->next_rx_desc = info->descs + (DESC_SIZE * next_rx);
+
+out:
+       if (0 && !nr) {
+               Dprintk("dazed: cmdsts_f: %08x\n", cmdsts);
+       }
+
+       spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void rx_action(unsigned long _dev)
+{
+       struct net_device *ndev = (void *)_dev;
+       struct ns83820 *dev = PRIV(ndev);
+       rx_irq(ndev);
+       writel(ihr, dev->base + IHR);
+
+       spin_lock_irq(&dev->misc_lock);
+       dev->IMR_cache |= ISR_RXDESC;
+       writel(dev->IMR_cache, dev->base + IMR);
+       spin_unlock_irq(&dev->misc_lock);
+
+       rx_irq(ndev);
+       ns83820_rx_kick(ndev);
+}
+
+/* Packet Transmit code
+ */
+static inline void kick_tx(struct ns83820 *dev)
+{
+       dprintk("kick_tx(%p): tx_idx=%d free_idx=%d\n",
+               dev, dev->tx_idx, dev->tx_free_idx);
+       writel(CR_TXE, dev->base + CR);
+}
+
+/* No spinlock needed on the transmit irq path as the interrupt handler is
+ * serialized.
+ */
+static void do_tx_done(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       u32 cmdsts, tx_done_idx;
+       __le32 *desc;
+
+       dprintk("do_tx_done(%p)\n", ndev);
+       tx_done_idx = dev->tx_done_idx;
+       desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
+
+       dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n",
+               tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS]));
+       while ((tx_done_idx != dev->tx_free_idx) &&
+              !(CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) ) {
+               struct sk_buff *skb;
+               unsigned len;
+               dma_addr_t addr;
+
+               if (cmdsts & CMDSTS_ERR)
+                       ndev->stats.tx_errors++;
+               if (cmdsts & CMDSTS_OK)
+                       ndev->stats.tx_packets++;
+               if (cmdsts & CMDSTS_OK)
+                       ndev->stats.tx_bytes += cmdsts & 0xffff;
+
+               dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n",
+                       tx_done_idx, dev->tx_free_idx, cmdsts);
+               skb = dev->tx_skbs[tx_done_idx];
+               dev->tx_skbs[tx_done_idx] = NULL;
+               dprintk("done(%p)\n", skb);
+
+               len = cmdsts & CMDSTS_LEN_MASK;
+               addr = desc_addr_get(desc + DESC_BUFPTR);
+               if (skb) {
+                       pci_unmap_single(dev->pci_dev,
+                                       addr,
+                                       len,
+                                       PCI_DMA_TODEVICE);
+                       dev_kfree_skb_irq(skb);
+                       atomic_dec(&dev->nr_tx_skbs);
+               } else
+                       pci_unmap_page(dev->pci_dev,
+                                       addr,
+                                       len,
+                                       PCI_DMA_TODEVICE);
+
+               tx_done_idx = (tx_done_idx + 1) % NR_TX_DESC;
+               dev->tx_done_idx = tx_done_idx;
+               desc[DESC_CMDSTS] = cpu_to_le32(0);
+               mb();
+               desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
+       }
+
+       /* Allow network stack to resume queueing packets after we've
+        * finished transmitting at least 1/4 of the packets in the queue.
+        */
+       if (netif_queue_stopped(ndev) && start_tx_okay(dev)) {
+               dprintk("start_queue(%p)\n", ndev);
+               netif_start_queue(ndev);
+               netif_wake_queue(ndev);
+       }
+}
+
+static void ns83820_cleanup_tx(struct ns83820 *dev)
+{
+       unsigned i;
+
+       for (i=0; i<NR_TX_DESC; i++) {
+               struct sk_buff *skb = dev->tx_skbs[i];
+               dev->tx_skbs[i] = NULL;
+               if (skb) {
+                       __le32 *desc = dev->tx_descs + (i * DESC_SIZE);
+                       pci_unmap_single(dev->pci_dev,
+                                       desc_addr_get(desc + DESC_BUFPTR),
+                                       le32_to_cpu(desc[DESC_CMDSTS]) & CMDSTS_LEN_MASK,
+                                       PCI_DMA_TODEVICE);
+                       dev_kfree_skb_irq(skb);
+                       atomic_dec(&dev->nr_tx_skbs);
+               }
+       }
+
+       memset(dev->tx_descs, 0, NR_TX_DESC * DESC_SIZE * 4);
+}
+
+/* transmit routine.  This code relies on the network layer serializing
+ * its calls in, but will run happily in parallel with the interrupt
+ * handler.  This code currently has provisions for fragmenting tx buffers
+ * while trying to track down a bug in either the zero copy code or
+ * the tx fifo (hence the MAX_FRAG_LEN).
+ */
+static netdev_tx_t ns83820_hard_start_xmit(struct sk_buff *skb,
+                                          struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       u32 free_idx, cmdsts, extsts;
+       int nr_free, nr_frags;
+       unsigned tx_done_idx, last_idx;
+       dma_addr_t buf;
+       unsigned len;
+       skb_frag_t *frag;
+       int stopped = 0;
+       int do_intr = 0;
+       volatile __le32 *first_desc;
+
+       dprintk("ns83820_hard_start_xmit\n");
+
+       nr_frags =  skb_shinfo(skb)->nr_frags;
+again:
+       if (unlikely(dev->CFG_cache & CFG_LNKSTS)) {
+               netif_stop_queue(ndev);
+               if (unlikely(dev->CFG_cache & CFG_LNKSTS))
+                       return NETDEV_TX_BUSY;
+               netif_start_queue(ndev);
+       }
+
+       last_idx = free_idx = dev->tx_free_idx;
+       tx_done_idx = dev->tx_done_idx;
+       nr_free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC;
+       nr_free -= 1;
+       if (nr_free <= nr_frags) {
+               dprintk("stop_queue - not enough(%p)\n", ndev);
+               netif_stop_queue(ndev);
+
+               /* Check again: we may have raced with a tx done irq */
+               if (dev->tx_done_idx != tx_done_idx) {
+                       dprintk("restart queue(%p)\n", ndev);
+                       netif_start_queue(ndev);
+                       goto again;
+               }
+               return NETDEV_TX_BUSY;
+       }
+
+       if (free_idx == dev->tx_intr_idx) {
+               do_intr = 1;
+               dev->tx_intr_idx = (dev->tx_intr_idx + NR_TX_DESC/4) % NR_TX_DESC;
+       }
+
+       nr_free -= nr_frags;
+       if (nr_free < MIN_TX_DESC_FREE) {
+               dprintk("stop_queue - last entry(%p)\n", ndev);
+               netif_stop_queue(ndev);
+               stopped = 1;
+       }
+
+       frag = skb_shinfo(skb)->frags;
+       if (!nr_frags)
+               frag = NULL;
+       extsts = 0;
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               extsts |= EXTSTS_IPPKT;
+               if (IPPROTO_TCP == ip_hdr(skb)->protocol)
+                       extsts |= EXTSTS_TCPPKT;
+               else if (IPPROTO_UDP == ip_hdr(skb)->protocol)
+                       extsts |= EXTSTS_UDPPKT;
+       }
+
+#ifdef NS83820_VLAN_ACCEL_SUPPORT
+       if(vlan_tx_tag_present(skb)) {
+               /* fetch the vlan tag info out of the
+                * ancillary data if the vlan code
+                * is using hw vlan acceleration
+                */
+               short tag = vlan_tx_tag_get(skb);
+               extsts |= (EXTSTS_VPKT | htons(tag));
+       }
+#endif
+
+       len = skb->len;
+       if (nr_frags)
+               len -= skb->data_len;
+       buf = pci_map_single(dev->pci_dev, skb->data, len, PCI_DMA_TODEVICE);
+
+       first_desc = dev->tx_descs + (free_idx * DESC_SIZE);
+
+       for (;;) {
+               volatile __le32 *desc = dev->tx_descs + (free_idx * DESC_SIZE);
+
+               dprintk("frag[%3u]: %4u @ 0x%08Lx\n", free_idx, len,
+                       (unsigned long long)buf);
+               last_idx = free_idx;
+               free_idx = (free_idx + 1) % NR_TX_DESC;
+               desc[DESC_LINK] = cpu_to_le32(dev->tx_phy_descs + (free_idx * DESC_SIZE * 4));
+               desc_addr_set(desc + DESC_BUFPTR, buf);
+               desc[DESC_EXTSTS] = cpu_to_le32(extsts);
+
+               cmdsts = ((nr_frags) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0);
+               cmdsts |= (desc == first_desc) ? 0 : CMDSTS_OWN;
+               cmdsts |= len;
+               desc[DESC_CMDSTS] = cpu_to_le32(cmdsts);
+
+               if (!nr_frags)
+                       break;
+
+               buf = pci_map_page(dev->pci_dev, frag->page,
+                                  frag->page_offset,
+                                  frag->size, PCI_DMA_TODEVICE);
+               dprintk("frag: buf=%08Lx  page=%08lx offset=%08lx\n",
+                       (long long)buf, (long) page_to_pfn(frag->page),
+                       frag->page_offset);
+               len = frag->size;
+               frag++;
+               nr_frags--;
+       }
+       dprintk("done pkt\n");
+
+       spin_lock_irq(&dev->tx_lock);
+       dev->tx_skbs[last_idx] = skb;
+       first_desc[DESC_CMDSTS] |= cpu_to_le32(CMDSTS_OWN);
+       dev->tx_free_idx = free_idx;
+       atomic_inc(&dev->nr_tx_skbs);
+       spin_unlock_irq(&dev->tx_lock);
+
+       kick_tx(dev);
+
+       /* Check again: we may have raced with a tx done irq */
+       if (stopped && (dev->tx_done_idx != tx_done_idx) && start_tx_okay(dev))
+               netif_start_queue(ndev);
+
+       return NETDEV_TX_OK;
+}
+
+static void ns83820_update_stats(struct ns83820 *dev)
+{
+       struct net_device *ndev = dev->ndev;
+       u8 __iomem *base = dev->base;
+
+       /* the DP83820 will freeze counters, so we need to read all of them */
+       ndev->stats.rx_errors           += readl(base + 0x60) & 0xffff;
+       ndev->stats.rx_crc_errors       += readl(base + 0x64) & 0xffff;
+       ndev->stats.rx_missed_errors    += readl(base + 0x68) & 0xffff;
+       ndev->stats.rx_frame_errors     += readl(base + 0x6c) & 0xffff;
+       /*ndev->stats.rx_symbol_errors +=*/ readl(base + 0x70);
+       ndev->stats.rx_length_errors    += readl(base + 0x74) & 0xffff;
+       ndev->stats.rx_length_errors    += readl(base + 0x78) & 0xffff;
+       /*ndev->stats.rx_badopcode_errors += */ readl(base + 0x7c);
+       /*ndev->stats.rx_pause_count += */  readl(base + 0x80);
+       /*ndev->stats.tx_pause_count += */  readl(base + 0x84);
+       ndev->stats.tx_carrier_errors   += readl(base + 0x88) & 0xff;
+}
+
+static struct net_device_stats *ns83820_get_stats(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+
+       /* somewhat overkill */
+       spin_lock_irq(&dev->misc_lock);
+       ns83820_update_stats(dev);
+       spin_unlock_irq(&dev->misc_lock);
+
+       return &ndev->stats;
+}
+
+/* Let ethtool retrieve info */
+static int ns83820_get_settings(struct net_device *ndev,
+                               struct ethtool_cmd *cmd)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       u32 cfg, tanar, tbicr;
+       int fullduplex   = 0;
+
+       /*
+        * Here's the list of available ethtool commands from other drivers:
+        *      cmd->advertising =
+        *      ethtool_cmd_speed_set(cmd, ...)
+        *      cmd->duplex =
+        *      cmd->port = 0;
+        *      cmd->phy_address =
+        *      cmd->transceiver = 0;
+        *      cmd->autoneg =
+        *      cmd->maxtxpkt = 0;
+        *      cmd->maxrxpkt = 0;
+        */
+
+       /* read current configuration */
+       cfg   = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
+       tanar = readl(dev->base + TANAR);
+       tbicr = readl(dev->base + TBICR);
+
+       fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0;
+
+       cmd->supported = SUPPORTED_Autoneg;
+
+       if (dev->CFG_cache & CFG_TBI_EN) {
+               /* we have optical interface */
+               cmd->supported |= SUPPORTED_1000baseT_Half |
+                                       SUPPORTED_1000baseT_Full |
+                                       SUPPORTED_FIBRE;
+               cmd->port       = PORT_FIBRE;
+       } else {
+               /* we have copper */
+               cmd->supported |= SUPPORTED_10baseT_Half |
+                       SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half |
+                       SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Half |
+                       SUPPORTED_1000baseT_Full |
+                       SUPPORTED_MII;
+               cmd->port = PORT_MII;
+       }
+
+       cmd->duplex = fullduplex ? DUPLEX_FULL : DUPLEX_HALF;
+       switch (cfg / CFG_SPDSTS0 & 3) {
+       case 2:
+               ethtool_cmd_speed_set(cmd, SPEED_1000);
+               break;
+       case 1:
+               ethtool_cmd_speed_set(cmd, SPEED_100);
+               break;
+       default:
+               ethtool_cmd_speed_set(cmd, SPEED_10);
+               break;
+       }
+       cmd->autoneg = (tbicr & TBICR_MR_AN_ENABLE)
+               ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+       return 0;
+}
+
+/* Let ethool change settings*/
+static int ns83820_set_settings(struct net_device *ndev,
+                               struct ethtool_cmd *cmd)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       u32 cfg, tanar;
+       int have_optical = 0;
+       int fullduplex   = 0;
+
+       /* read current configuration */
+       cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
+       tanar = readl(dev->base + TANAR);
+
+       if (dev->CFG_cache & CFG_TBI_EN) {
+               /* we have optical */
+               have_optical = 1;
+               fullduplex   = (tanar & TANAR_FULL_DUP);
+
+       } else {
+               /* we have copper */
+               fullduplex = cfg & CFG_DUPSTS;
+       }
+
+       spin_lock_irq(&dev->misc_lock);
+       spin_lock(&dev->tx_lock);
+
+       /* Set duplex */
+       if (cmd->duplex != fullduplex) {
+               if (have_optical) {
+                       /*set full duplex*/
+                       if (cmd->duplex == DUPLEX_FULL) {
+                               /* force full duplex */
+                               writel(readl(dev->base + TXCFG)
+                                       | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
+                                       dev->base + TXCFG);
+                               writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
+                                       dev->base + RXCFG);
+                               /* Light up full duplex LED */
+                               writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT,
+                                       dev->base + GPIOR);
+                       } else {
+                               /*TODO: set half duplex */
+                       }
+
+               } else {
+                       /*we have copper*/
+                       /* TODO: Set duplex for copper cards */
+               }
+               printk(KERN_INFO "%s: Duplex set via ethtool\n",
+               ndev->name);
+       }
+
+       /* Set autonegotiation */
+       if (1) {
+               if (cmd->autoneg == AUTONEG_ENABLE) {
+                       /* restart auto negotiation */
+                       writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN,
+                               dev->base + TBICR);
+                       writel(TBICR_MR_AN_ENABLE, dev->base + TBICR);
+                               dev->linkstate = LINK_AUTONEGOTIATE;
+
+                       printk(KERN_INFO "%s: autoneg enabled via ethtool\n",
+                               ndev->name);
+               } else {
+                       /* disable auto negotiation */
+                       writel(0x00000000, dev->base + TBICR);
+               }
+
+               printk(KERN_INFO "%s: autoneg %s via ethtool\n", ndev->name,
+                               cmd->autoneg ? "ENABLED" : "DISABLED");
+       }
+
+       phy_intr(ndev);
+       spin_unlock(&dev->tx_lock);
+       spin_unlock_irq(&dev->misc_lock);
+
+       return 0;
+}
+/* end ethtool get/set support -df */
+
+static void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       strcpy(info->driver, "ns83820");
+       strcpy(info->version, VERSION);
+       strcpy(info->bus_info, pci_name(dev->pci_dev));
+}
+
+static u32 ns83820_get_link(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       u32 cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
+       return cfg & CFG_LNKSTS ? 1 : 0;
+}
+
+static const struct ethtool_ops ops = {
+       .get_settings    = ns83820_get_settings,
+       .set_settings    = ns83820_set_settings,
+       .get_drvinfo     = ns83820_get_drvinfo,
+       .get_link        = ns83820_get_link
+};
+
+static inline void ns83820_disable_interrupts(struct ns83820 *dev)
+{
+       writel(0, dev->base + IMR);
+       writel(0, dev->base + IER);
+       readl(dev->base + IER);
+}
+
+/* this function is called in irq context from the ISR */
+static void ns83820_mib_isr(struct ns83820 *dev)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->misc_lock, flags);
+       ns83820_update_stats(dev);
+       spin_unlock_irqrestore(&dev->misc_lock, flags);
+}
+
+static void ns83820_do_isr(struct net_device *ndev, u32 isr);
+static irqreturn_t ns83820_irq(int foo, void *data)
+{
+       struct net_device *ndev = data;
+       struct ns83820 *dev = PRIV(ndev);
+       u32 isr;
+       dprintk("ns83820_irq(%p)\n", ndev);
+
+       dev->ihr = 0;
+
+       isr = readl(dev->base + ISR);
+       dprintk("irq: %08x\n", isr);
+       ns83820_do_isr(ndev, isr);
+       return IRQ_HANDLED;
+}
+
+static void ns83820_do_isr(struct net_device *ndev, u32 isr)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       unsigned long flags;
+
+#ifdef DEBUG
+       if (isr & ~(ISR_PHY | ISR_RXDESC | ISR_RXEARLY | ISR_RXOK | ISR_RXERR | ISR_TXIDLE | ISR_TXOK | ISR_TXDESC))
+               Dprintk("odd isr? 0x%08x\n", isr);
+#endif
+
+       if (ISR_RXIDLE & isr) {
+               dev->rx_info.idle = 1;
+               Dprintk("oh dear, we are idle\n");
+               ns83820_rx_kick(ndev);
+       }
+
+       if ((ISR_RXDESC | ISR_RXOK) & isr) {
+               prefetch(dev->rx_info.next_rx_desc);
+
+               spin_lock_irqsave(&dev->misc_lock, flags);
+               dev->IMR_cache &= ~(ISR_RXDESC | ISR_RXOK);
+               writel(dev->IMR_cache, dev->base + IMR);
+               spin_unlock_irqrestore(&dev->misc_lock, flags);
+
+               tasklet_schedule(&dev->rx_tasklet);
+               //rx_irq(ndev);
+               //writel(4, dev->base + IHR);
+       }
+
+       if ((ISR_RXIDLE | ISR_RXORN | ISR_RXDESC | ISR_RXOK | ISR_RXERR) & isr)
+               ns83820_rx_kick(ndev);
+
+       if (unlikely(ISR_RXSOVR & isr)) {
+               //printk("overrun: rxsovr\n");
+               ndev->stats.rx_fifo_errors++;
+       }
+
+       if (unlikely(ISR_RXORN & isr)) {
+               //printk("overrun: rxorn\n");
+               ndev->stats.rx_fifo_errors++;
+       }
+
+       if ((ISR_RXRCMP & isr) && dev->rx_info.up)
+               writel(CR_RXE, dev->base + CR);
+
+       if (ISR_TXIDLE & isr) {
+               u32 txdp;
+               txdp = readl(dev->base + TXDP);
+               dprintk("txdp: %08x\n", txdp);
+               txdp -= dev->tx_phy_descs;
+               dev->tx_idx = txdp / (DESC_SIZE * 4);
+               if (dev->tx_idx >= NR_TX_DESC) {
+                       printk(KERN_ALERT "%s: BUG -- txdp out of range\n", ndev->name);
+                       dev->tx_idx = 0;
+               }
+               /* The may have been a race between a pci originated read
+                * and the descriptor update from the cpu.  Just in case,
+                * kick the transmitter if the hardware thinks it is on a
+                * different descriptor than we are.
+                */
+               if (dev->tx_idx != dev->tx_free_idx)
+                       kick_tx(dev);
+       }
+
+       /* Defer tx ring processing until more than a minimum amount of
+        * work has accumulated
+        */
+       if ((ISR_TXDESC | ISR_TXIDLE | ISR_TXOK | ISR_TXERR) & isr) {
+               spin_lock_irqsave(&dev->tx_lock, flags);
+               do_tx_done(ndev);
+               spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+               /* Disable TxOk if there are no outstanding tx packets.
+                */
+               if ((dev->tx_done_idx == dev->tx_free_idx) &&
+                   (dev->IMR_cache & ISR_TXOK)) {
+                       spin_lock_irqsave(&dev->misc_lock, flags);
+                       dev->IMR_cache &= ~ISR_TXOK;
+                       writel(dev->IMR_cache, dev->base + IMR);
+                       spin_unlock_irqrestore(&dev->misc_lock, flags);
+               }
+       }
+
+       /* The TxIdle interrupt can come in before the transmit has
+        * completed.  Normally we reap packets off of the combination
+        * of TxDesc and TxIdle and leave TxOk disabled (since it
+        * occurs on every packet), but when no further irqs of this
+        * nature are expected, we must enable TxOk.
+        */
+       if ((ISR_TXIDLE & isr) && (dev->tx_done_idx != dev->tx_free_idx)) {
+               spin_lock_irqsave(&dev->misc_lock, flags);
+               dev->IMR_cache |= ISR_TXOK;
+               writel(dev->IMR_cache, dev->base + IMR);
+               spin_unlock_irqrestore(&dev->misc_lock, flags);
+       }
+
+       /* MIB interrupt: one of the statistics counters is about to overflow */
+       if (unlikely(ISR_MIB & isr))
+               ns83820_mib_isr(dev);
+
+       /* PHY: Link up/down/negotiation state change */
+       if (unlikely(ISR_PHY & isr))
+               phy_intr(ndev);
+
+#if 0  /* Still working on the interrupt mitigation strategy */
+       if (dev->ihr)
+               writel(dev->ihr, dev->base + IHR);
+#endif
+}
+
+static void ns83820_do_reset(struct ns83820 *dev, u32 which)
+{
+       Dprintk("resetting chip...\n");
+       writel(which, dev->base + CR);
+       do {
+               schedule();
+       } while (readl(dev->base + CR) & which);
+       Dprintk("okay!\n");
+}
+
+static int ns83820_stop(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+
+       /* FIXME: protect against interrupt handler? */
+       del_timer_sync(&dev->tx_watchdog);
+
+       ns83820_disable_interrupts(dev);
+
+       dev->rx_info.up = 0;
+       synchronize_irq(dev->pci_dev->irq);
+
+       ns83820_do_reset(dev, CR_RST);
+
+       synchronize_irq(dev->pci_dev->irq);
+
+       spin_lock_irq(&dev->misc_lock);
+       dev->IMR_cache &= ~(ISR_TXURN | ISR_TXIDLE | ISR_TXERR | ISR_TXDESC | ISR_TXOK);
+       spin_unlock_irq(&dev->misc_lock);
+
+       ns83820_cleanup_rx(dev);
+       ns83820_cleanup_tx(dev);
+
+       return 0;
+}
+
+static void ns83820_tx_timeout(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+        u32 tx_done_idx;
+       __le32 *desc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->tx_lock, flags);
+
+       tx_done_idx = dev->tx_done_idx;
+       desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
+
+       printk(KERN_INFO "%s: tx_timeout: tx_done_idx=%d free_idx=%d cmdsts=%08x\n",
+               ndev->name,
+               tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS]));
+
+#if defined(DEBUG)
+       {
+               u32 isr;
+               isr = readl(dev->base + ISR);
+               printk("irq: %08x imr: %08x\n", isr, dev->IMR_cache);
+               ns83820_do_isr(ndev, isr);
+       }
+#endif
+
+       do_tx_done(ndev);
+
+       tx_done_idx = dev->tx_done_idx;
+       desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
+
+       printk(KERN_INFO "%s: after: tx_done_idx=%d free_idx=%d cmdsts=%08x\n",
+               ndev->name,
+               tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS]));
+
+       spin_unlock_irqrestore(&dev->tx_lock, flags);
+}
+
+static void ns83820_tx_watch(unsigned long data)
+{
+       struct net_device *ndev = (void *)data;
+       struct ns83820 *dev = PRIV(ndev);
+
+#if defined(DEBUG)
+       printk("ns83820_tx_watch: %u %u %d\n",
+               dev->tx_done_idx, dev->tx_free_idx, atomic_read(&dev->nr_tx_skbs)
+               );
+#endif
+
+       if (time_after(jiffies, dev_trans_start(ndev) + 1*HZ) &&
+           dev->tx_done_idx != dev->tx_free_idx) {
+               printk(KERN_DEBUG "%s: ns83820_tx_watch: %u %u %d\n",
+                       ndev->name,
+                       dev->tx_done_idx, dev->tx_free_idx,
+                       atomic_read(&dev->nr_tx_skbs));
+               ns83820_tx_timeout(ndev);
+       }
+
+       mod_timer(&dev->tx_watchdog, jiffies + 2*HZ);
+}
+
+static int ns83820_open(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       unsigned i;
+       u32 desc;
+       int ret;
+
+       dprintk("ns83820_open\n");
+
+       writel(0, dev->base + PQCR);
+
+       ret = ns83820_setup_rx(ndev);
+       if (ret)
+               goto failed;
+
+       memset(dev->tx_descs, 0, 4 * NR_TX_DESC * DESC_SIZE);
+       for (i=0; i<NR_TX_DESC; i++) {
+               dev->tx_descs[(i * DESC_SIZE) + DESC_LINK]
+                               = cpu_to_le32(
+                                 dev->tx_phy_descs
+                                 + ((i+1) % NR_TX_DESC) * DESC_SIZE * 4);
+       }
+
+       dev->tx_idx = 0;
+       dev->tx_done_idx = 0;
+       desc = dev->tx_phy_descs;
+       writel(0, dev->base + TXDP_HI);
+       writel(desc, dev->base + TXDP);
+
+       init_timer(&dev->tx_watchdog);
+       dev->tx_watchdog.data = (unsigned long)ndev;
+       dev->tx_watchdog.function = ns83820_tx_watch;
+       mod_timer(&dev->tx_watchdog, jiffies + 2*HZ);
+
+       netif_start_queue(ndev);        /* FIXME: wait for phy to come up */
+
+       return 0;
+
+failed:
+       ns83820_stop(ndev);
+       return ret;
+}
+
+static void ns83820_getmac(struct ns83820 *dev, u8 *mac)
+{
+       unsigned i;
+       for (i=0; i<3; i++) {
+               u32 data;
+
+               /* Read from the perfect match memory: this is loaded by
+                * the chip from the EEPROM via the EELOAD self test.
+                */
+               writel(i*2, dev->base + RFCR);
+               data = readl(dev->base + RFDR);
+
+               *mac++ = data;
+               *mac++ = data >> 8;
+       }
+}
+
+static int ns83820_change_mtu(struct net_device *ndev, int new_mtu)
+{
+       if (new_mtu > RX_BUF_SIZE)
+               return -EINVAL;
+       ndev->mtu = new_mtu;
+       return 0;
+}
+
+static void ns83820_set_multicast(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       u8 __iomem *rfcr = dev->base + RFCR;
+       u32 and_mask = 0xffffffff;
+       u32 or_mask = 0;
+       u32 val;
+
+       if (ndev->flags & IFF_PROMISC)
+               or_mask |= RFCR_AAU | RFCR_AAM;
+       else
+               and_mask &= ~(RFCR_AAU | RFCR_AAM);
+
+       if (ndev->flags & IFF_ALLMULTI || netdev_mc_count(ndev))
+               or_mask |= RFCR_AAM;
+       else
+               and_mask &= ~RFCR_AAM;
+
+       spin_lock_irq(&dev->misc_lock);
+       val = (readl(rfcr) & and_mask) | or_mask;
+       /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */
+       writel(val & ~RFCR_RFEN, rfcr);
+       writel(val, rfcr);
+       spin_unlock_irq(&dev->misc_lock);
+}
+
+static void ns83820_run_bist(struct net_device *ndev, const char *name, u32 enable, u32 done, u32 fail)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       int timed_out = 0;
+       unsigned long start;
+       u32 status;
+       int loops = 0;
+
+       dprintk("%s: start %s\n", ndev->name, name);
+
+       start = jiffies;
+
+       writel(enable, dev->base + PTSCR);
+       for (;;) {
+               loops++;
+               status = readl(dev->base + PTSCR);
+               if (!(status & enable))
+                       break;
+               if (status & done)
+                       break;
+               if (status & fail)
+                       break;
+               if (time_after_eq(jiffies, start + HZ)) {
+                       timed_out = 1;
+                       break;
+               }
+               schedule_timeout_uninterruptible(1);
+       }
+
+       if (status & fail)
+               printk(KERN_INFO "%s: %s failed! (0x%08x & 0x%08x)\n",
+                       ndev->name, name, status, fail);
+       else if (timed_out)
+               printk(KERN_INFO "%s: run_bist %s timed out! (%08x)\n",
+                       ndev->name, name, status);
+
+       dprintk("%s: done %s in %d loops\n", ndev->name, name, loops);
+}
+
+#ifdef PHY_CODE_IS_FINISHED
+static void ns83820_mii_write_bit(struct ns83820 *dev, int bit)
+{
+       /* drive MDC low */
+       dev->MEAR_cache &= ~MEAR_MDC;
+       writel(dev->MEAR_cache, dev->base + MEAR);
+       readl(dev->base + MEAR);
+
+       /* enable output, set bit */
+       dev->MEAR_cache |= MEAR_MDDIR;
+       if (bit)
+               dev->MEAR_cache |= MEAR_MDIO;
+       else
+               dev->MEAR_cache &= ~MEAR_MDIO;
+
+       /* set the output bit */
+       writel(dev->MEAR_cache, dev->base + MEAR);
+       readl(dev->base + MEAR);
+
+       /* Wait.  Max clock rate is 2.5MHz, this way we come in under 1MHz */
+       udelay(1);
+
+       /* drive MDC high causing the data bit to be latched */
+       dev->MEAR_cache |= MEAR_MDC;
+       writel(dev->MEAR_cache, dev->base + MEAR);
+       readl(dev->base + MEAR);
+
+       /* Wait again... */
+       udelay(1);
+}
+
+static int ns83820_mii_read_bit(struct ns83820 *dev)
+{
+       int bit;
+
+       /* drive MDC low, disable output */
+       dev->MEAR_cache &= ~MEAR_MDC;
+       dev->MEAR_cache &= ~MEAR_MDDIR;
+       writel(dev->MEAR_cache, dev->base + MEAR);
+       readl(dev->base + MEAR);
+
+       /* Wait.  Max clock rate is 2.5MHz, this way we come in under 1MHz */
+       udelay(1);
+
+       /* drive MDC high causing the data bit to be latched */
+       bit = (readl(dev->base + MEAR) & MEAR_MDIO) ? 1 : 0;
+       dev->MEAR_cache |= MEAR_MDC;
+       writel(dev->MEAR_cache, dev->base + MEAR);
+
+       /* Wait again... */
+       udelay(1);
+
+       return bit;
+}
+
+static unsigned ns83820_mii_read_reg(struct ns83820 *dev, unsigned phy, unsigned reg)
+{
+       unsigned data = 0;
+       int i;
+
+       /* read some garbage so that we eventually sync up */
+       for (i=0; i<64; i++)
+               ns83820_mii_read_bit(dev);
+
+       ns83820_mii_write_bit(dev, 0);  /* start */
+       ns83820_mii_write_bit(dev, 1);
+       ns83820_mii_write_bit(dev, 1);  /* opcode read */
+       ns83820_mii_write_bit(dev, 0);
+
+       /* write out the phy address: 5 bits, msb first */
+       for (i=0; i<5; i++)
+               ns83820_mii_write_bit(dev, phy & (0x10 >> i));
+
+       /* write out the register address, 5 bits, msb first */
+       for (i=0; i<5; i++)
+               ns83820_mii_write_bit(dev, reg & (0x10 >> i));
+
+       ns83820_mii_read_bit(dev);      /* turn around cycles */
+       ns83820_mii_read_bit(dev);
+
+       /* read in the register data, 16 bits msb first */
+       for (i=0; i<16; i++) {
+               data <<= 1;
+               data |= ns83820_mii_read_bit(dev);
+       }
+
+       return data;
+}
+
+static unsigned ns83820_mii_write_reg(struct ns83820 *dev, unsigned phy, unsigned reg, unsigned data)
+{
+       int i;
+
+       /* read some garbage so that we eventually sync up */
+       for (i=0; i<64; i++)
+               ns83820_mii_read_bit(dev);
+
+       ns83820_mii_write_bit(dev, 0);  /* start */
+       ns83820_mii_write_bit(dev, 1);
+       ns83820_mii_write_bit(dev, 0);  /* opcode read */
+       ns83820_mii_write_bit(dev, 1);
+
+       /* write out the phy address: 5 bits, msb first */
+       for (i=0; i<5; i++)
+               ns83820_mii_write_bit(dev, phy & (0x10 >> i));
+
+       /* write out the register address, 5 bits, msb first */
+       for (i=0; i<5; i++)
+               ns83820_mii_write_bit(dev, reg & (0x10 >> i));
+
+       ns83820_mii_read_bit(dev);      /* turn around cycles */
+       ns83820_mii_read_bit(dev);
+
+       /* read in the register data, 16 bits msb first */
+       for (i=0; i<16; i++)
+               ns83820_mii_write_bit(dev, (data >> (15 - i)) & 1);
+
+       return data;
+}
+
+static void ns83820_probe_phy(struct net_device *ndev)
+{
+       struct ns83820 *dev = PRIV(ndev);
+       static int first;
+       int i;
+#define MII_PHYIDR1    0x02
+#define MII_PHYIDR2    0x03
+
+#if 0
+       if (!first) {
+               unsigned tmp;
+               ns83820_mii_read_reg(dev, 1, 0x09);
+               ns83820_mii_write_reg(dev, 1, 0x10, 0x0d3e);
+
+               tmp = ns83820_mii_read_reg(dev, 1, 0x00);
+               ns83820_mii_write_reg(dev, 1, 0x00, tmp | 0x8000);
+               udelay(1300);
+               ns83820_mii_read_reg(dev, 1, 0x09);
+       }
+#endif
+       first = 1;
+
+       for (i=1; i<2; i++) {
+               int j;
+               unsigned a, b;
+               a = ns83820_mii_read_reg(dev, i, MII_PHYIDR1);
+               b = ns83820_mii_read_reg(dev, i, MII_PHYIDR2);
+
+               //printk("%s: phy %d: 0x%04x 0x%04x\n",
+               //      ndev->name, i, a, b);
+
+               for (j=0; j<0x16; j+=4) {
+                       dprintk("%s: [0x%02x] %04x %04x %04x %04x\n",
+                               ndev->name, j,
+                               ns83820_mii_read_reg(dev, i, 0 + j),
+                               ns83820_mii_read_reg(dev, i, 1 + j),
+                               ns83820_mii_read_reg(dev, i, 2 + j),
+                               ns83820_mii_read_reg(dev, i, 3 + j)
+                               );
+               }
+       }
+       {
+               unsigned a, b;
+               /* read firmware version: memory addr is 0x8402 and 0x8403 */
+               ns83820_mii_write_reg(dev, 1, 0x16, 0x000d);
+               ns83820_mii_write_reg(dev, 1, 0x1e, 0x810e);
+               a = ns83820_mii_read_reg(dev, 1, 0x1d);
+
+               ns83820_mii_write_reg(dev, 1, 0x16, 0x000d);
+               ns83820_mii_write_reg(dev, 1, 0x1e, 0x810e);
+               b = ns83820_mii_read_reg(dev, 1, 0x1d);
+               dprintk("version: 0x%04x 0x%04x\n", a, b);
+       }
+}
+#endif
+
+static const struct net_device_ops netdev_ops = {
+       .ndo_open               = ns83820_open,
+       .ndo_stop               = ns83820_stop,
+       .ndo_start_xmit         = ns83820_hard_start_xmit,
+       .ndo_get_stats          = ns83820_get_stats,
+       .ndo_change_mtu         = ns83820_change_mtu,
+       .ndo_set_multicast_list = ns83820_set_multicast,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_tx_timeout         = ns83820_tx_timeout,
+};
+
+static int __devinit ns83820_init_one(struct pci_dev *pci_dev,
+                                     const struct pci_device_id *id)
+{
+       struct net_device *ndev;
+       struct ns83820 *dev;
+       long addr;
+       int err;
+       int using_dac = 0;
+
+       /* See if we can set the dma mask early on; failure is fatal. */
+       if (sizeof(dma_addr_t) == 8 &&
+               !pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64))) {
+               using_dac = 1;
+       } else if (!pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
+               using_dac = 0;
+       } else {
+               dev_warn(&pci_dev->dev, "pci_set_dma_mask failed!\n");
+               return -ENODEV;
+       }
+
+       ndev = alloc_etherdev(sizeof(struct ns83820));
+       err = -ENOMEM;
+       if (!ndev)
+               goto out;
+
+       dev = PRIV(ndev);
+       dev->ndev = ndev;
+
+       spin_lock_init(&dev->rx_info.lock);
+       spin_lock_init(&dev->tx_lock);
+       spin_lock_init(&dev->misc_lock);
+       dev->pci_dev = pci_dev;
+
+       SET_NETDEV_DEV(ndev, &pci_dev->dev);
+
+       INIT_WORK(&dev->tq_refill, queue_refill);
+       tasklet_init(&dev->rx_tasklet, rx_action, (unsigned long)ndev);
+
+       err = pci_enable_device(pci_dev);
+       if (err) {
+               dev_info(&pci_dev->dev, "pci_enable_dev failed: %d\n", err);
+               goto out_free;
+       }
+
+       pci_set_master(pci_dev);
+       addr = pci_resource_start(pci_dev, 1);
+       dev->base = ioremap_nocache(addr, PAGE_SIZE);
+       dev->tx_descs = pci_alloc_consistent(pci_dev,
+                       4 * DESC_SIZE * NR_TX_DESC, &dev->tx_phy_descs);
+       dev->rx_info.descs = pci_alloc_consistent(pci_dev,
+                       4 * DESC_SIZE * NR_RX_DESC, &dev->rx_info.phy_descs);
+       err = -ENOMEM;
+       if (!dev->base || !dev->tx_descs || !dev->rx_info.descs)
+               goto out_disable;
+
+       dprintk("%p: %08lx  %p: %08lx\n",
+               dev->tx_descs, (long)dev->tx_phy_descs,
+               dev->rx_info.descs, (long)dev->rx_info.phy_descs);
+
+       ns83820_disable_interrupts(dev);
+
+       dev->IMR_cache = 0;
+
+       err = request_irq(pci_dev->irq, ns83820_irq, IRQF_SHARED,
+                         DRV_NAME, ndev);
+       if (err) {
+               dev_info(&pci_dev->dev, "unable to register irq %d, err %d\n",
+                       pci_dev->irq, err);
+               goto out_disable;
+       }
+
+       /*
+        * FIXME: we are holding rtnl_lock() over obscenely long area only
+        * because some of the setup code uses dev->name.  It's Wrong(tm) -
+        * we should be using driver-specific names for all that stuff.
+        * For now that will do, but we really need to come back and kill
+        * most of the dev_alloc_name() users later.
+        */
+       rtnl_lock();
+       err = dev_alloc_name(ndev, ndev->name);
+       if (err < 0) {
+               dev_info(&pci_dev->dev, "unable to get netdev name: %d\n", err);
+               goto out_free_irq;
+       }
+
+       printk("%s: ns83820.c: 0x22c: %08x, subsystem: %04x:%04x\n",
+               ndev->name, le32_to_cpu(readl(dev->base + 0x22c)),
+               pci_dev->subsystem_vendor, pci_dev->subsystem_device);
+
+       ndev->netdev_ops = &netdev_ops;
+       SET_ETHTOOL_OPS(ndev, &ops);
+       ndev->watchdog_timeo = 5 * HZ;
+       pci_set_drvdata(pci_dev, ndev);
+
+       ns83820_do_reset(dev, CR_RST);
+
+       /* Must reset the ram bist before running it */
+       writel(PTSCR_RBIST_RST, dev->base + PTSCR);
+       ns83820_run_bist(ndev, "sram bist",   PTSCR_RBIST_EN,
+                        PTSCR_RBIST_DONE, PTSCR_RBIST_FAIL);
+       ns83820_run_bist(ndev, "eeprom bist", PTSCR_EEBIST_EN, 0,
+                        PTSCR_EEBIST_FAIL);
+       ns83820_run_bist(ndev, "eeprom load", PTSCR_EELOAD_EN, 0, 0);
+
+       /* I love config registers */
+       dev->CFG_cache = readl(dev->base + CFG);
+
+       if ((dev->CFG_cache & CFG_PCI64_DET)) {
+               printk(KERN_INFO "%s: detected 64 bit PCI data bus.\n",
+                       ndev->name);
+               /*dev->CFG_cache |= CFG_DATA64_EN;*/
+               if (!(dev->CFG_cache & CFG_DATA64_EN))
+                       printk(KERN_INFO "%s: EEPROM did not enable 64 bit bus.  Disabled.\n",
+                               ndev->name);
+       } else
+               dev->CFG_cache &= ~(CFG_DATA64_EN);
+
+       dev->CFG_cache &= (CFG_TBI_EN  | CFG_MRM_DIS   | CFG_MWI_DIS |
+                          CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 |
+                          CFG_M64ADDR);
+       dev->CFG_cache |= CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS |
+                         CFG_EXTSTS_EN   | CFG_EXD         | CFG_PESEL;
+       dev->CFG_cache |= CFG_REQALG;
+       dev->CFG_cache |= CFG_POW;
+       dev->CFG_cache |= CFG_TMRTEST;
+
+       /* When compiled with 64 bit addressing, we must always enable
+        * the 64 bit descriptor format.
+        */
+       if (sizeof(dma_addr_t) == 8)
+               dev->CFG_cache |= CFG_M64ADDR;
+       if (using_dac)
+               dev->CFG_cache |= CFG_T64ADDR;
+
+       /* Big endian mode does not seem to do what the docs suggest */
+       dev->CFG_cache &= ~CFG_BEM;
+
+       /* setup optical transceiver if we have one */
+       if (dev->CFG_cache & CFG_TBI_EN) {
+               printk(KERN_INFO "%s: enabling optical transceiver\n",
+                       ndev->name);
+               writel(readl(dev->base + GPIOR) | 0x3e8, dev->base + GPIOR);
+
+               /* setup auto negotiation feature advertisement */
+               writel(readl(dev->base + TANAR)
+                      | TANAR_HALF_DUP | TANAR_FULL_DUP,
+                      dev->base + TANAR);
+
+               /* start auto negotiation */
+               writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN,
+                      dev->base + TBICR);
+               writel(TBICR_MR_AN_ENABLE, dev->base + TBICR);
+               dev->linkstate = LINK_AUTONEGOTIATE;
+
+               dev->CFG_cache |= CFG_MODE_1000;
+       }
+
+       writel(dev->CFG_cache, dev->base + CFG);
+       dprintk("CFG: %08x\n", dev->CFG_cache);
+
+       if (reset_phy) {
+               printk(KERN_INFO "%s: resetting phy\n", ndev->name);
+               writel(dev->CFG_cache | CFG_PHY_RST, dev->base + CFG);
+               msleep(10);
+               writel(dev->CFG_cache, dev->base + CFG);
+       }
+
+#if 0  /* Huh?  This sets the PCI latency register.  Should be done via
+        * the PCI layer.  FIXME.
+        */
+       if (readl(dev->base + SRR))
+               writel(readl(dev->base+0x20c) | 0xfe00, dev->base + 0x20c);
+#endif
+
+       /* Note!  The DMA burst size interacts with packet
+        * transmission, such that the largest packet that
+        * can be transmitted is 8192 - FLTH - burst size.
+        * If only the transmit fifo was larger...
+        */
+       /* Ramit : 1024 DMA is not a good idea, it ends up banging
+        * some DELL and COMPAQ SMP systems */
+       writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA512
+               | ((1600 / 32) * 0x100),
+               dev->base + TXCFG);
+
+       /* Flush the interrupt holdoff timer */
+       writel(0x000, dev->base + IHR);
+       writel(0x100, dev->base + IHR);
+       writel(0x000, dev->base + IHR);
+
+       /* Set Rx to full duplex, don't accept runt, errored, long or length
+        * range errored packets.  Use 512 byte DMA.
+        */
+       /* Ramit : 1024 DMA is not a good idea, it ends up banging
+        * some DELL and COMPAQ SMP systems
+        * Turn on ALP, only we are accpeting Jumbo Packets */
+       writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD
+               | RXCFG_STRIPCRC
+               //| RXCFG_ALP
+               | (RXCFG_MXDMA512) | 0, dev->base + RXCFG);
+
+       /* Disable priority queueing */
+       writel(0, dev->base + PQCR);
+
+       /* Enable IP checksum validation and detetion of VLAN headers.
+        * Note: do not set the reject options as at least the 0x102
+        * revision of the chip does not properly accept IP fragments
+        * at least for UDP.
+        */
+       /* Ramit : Be sure to turn on RXCFG_ARP if VLAN's are enabled, since
+        * the MAC it calculates the packetsize AFTER stripping the VLAN
+        * header, and if a VLAN Tagged packet of 64 bytes is received (like
+        * a ping with a VLAN header) then the card, strips the 4 byte VLAN
+        * tag and then checks the packet size, so if RXCFG_ARP is not enabled,
+        * it discrards it!.  These guys......
+        * also turn on tag stripping if hardware acceleration is enabled
+        */
+#ifdef NS83820_VLAN_ACCEL_SUPPORT
+#define VRCR_INIT_VALUE (VRCR_IPEN|VRCR_VTDEN|VRCR_VTREN)
+#else
+#define VRCR_INIT_VALUE (VRCR_IPEN|VRCR_VTDEN)
+#endif
+       writel(VRCR_INIT_VALUE, dev->base + VRCR);
+
+       /* Enable per-packet TCP/UDP/IP checksumming
+        * and per packet vlan tag insertion if
+        * vlan hardware acceleration is enabled
+        */
+#ifdef NS83820_VLAN_ACCEL_SUPPORT
+#define VTCR_INIT_VALUE (VTCR_PPCHK|VTCR_VPPTI)
+#else
+#define VTCR_INIT_VALUE VTCR_PPCHK
+#endif
+       writel(VTCR_INIT_VALUE, dev->base + VTCR);
+
+       /* Ramit : Enable async and sync pause frames */
+       /* writel(0, dev->base + PCR); */
+       writel((PCR_PS_MCAST | PCR_PS_DA | PCR_PSEN | PCR_FFLO_4K |
+               PCR_FFHI_8K | PCR_STLO_4 | PCR_STHI_8 | PCR_PAUSE_CNT),
+               dev->base + PCR);
+
+       /* Disable Wake On Lan */
+       writel(0, dev->base + WCSR);
+
+       ns83820_getmac(dev, ndev->dev_addr);
+
+       /* Yes, we support dumb IP checksum on transmit */
+       ndev->features |= NETIF_F_SG;
+       ndev->features |= NETIF_F_IP_CSUM;
+
+#ifdef NS83820_VLAN_ACCEL_SUPPORT
+       /* We also support hardware vlan acceleration */
+       ndev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+#endif
+
+       if (using_dac) {
+               printk(KERN_INFO "%s: using 64 bit addressing.\n",
+                       ndev->name);
+               ndev->features |= NETIF_F_HIGHDMA;
+       }
+
+       printk(KERN_INFO "%s: ns83820 v" VERSION ": DP83820 v%u.%u: %pM io=0x%08lx irq=%d f=%s\n",
+               ndev->name,
+               (unsigned)readl(dev->base + SRR) >> 8,
+               (unsigned)readl(dev->base + SRR) & 0xff,
+               ndev->dev_addr, addr, pci_dev->irq,
+               (ndev->features & NETIF_F_HIGHDMA) ? "h,sg" : "sg"
+               );
+
+#ifdef PHY_CODE_IS_FINISHED
+       ns83820_probe_phy(ndev);
+#endif
+
+       err = register_netdevice(ndev);
+       if (err) {
+               printk(KERN_INFO "ns83820: unable to register netdev: %d\n", err);
+               goto out_cleanup;
+       }
+       rtnl_unlock();
+
+       return 0;
+
+out_cleanup:
+       ns83820_disable_interrupts(dev); /* paranoia */
+out_free_irq:
+       rtnl_unlock();
+       free_irq(pci_dev->irq, ndev);
+out_disable:
+       if (dev->base)
+               iounmap(dev->base);
+       pci_free_consistent(pci_dev, 4 * DESC_SIZE * NR_TX_DESC, dev->tx_descs, dev->tx_phy_descs);
+       pci_free_consistent(pci_dev, 4 * DESC_SIZE * NR_RX_DESC, dev->rx_info.descs, dev->rx_info.phy_descs);
+       pci_disable_device(pci_dev);
+out_free:
+       free_netdev(ndev);
+       pci_set_drvdata(pci_dev, NULL);
+out:
+       return err;
+}
+
+static void __devexit ns83820_remove_one(struct pci_dev *pci_dev)
+{
+       struct net_device *ndev = pci_get_drvdata(pci_dev);
+       struct ns83820 *dev = PRIV(ndev); /* ok even if NULL */
+
+       if (!ndev)                      /* paranoia */
+               return;
+
+       ns83820_disable_interrupts(dev); /* paranoia */
+
+       unregister_netdev(ndev);
+       free_irq(dev->pci_dev->irq, ndev);
+       iounmap(dev->base);
+       pci_free_consistent(dev->pci_dev, 4 * DESC_SIZE * NR_TX_DESC,
+                       dev->tx_descs, dev->tx_phy_descs);
+       pci_free_consistent(dev->pci_dev, 4 * DESC_SIZE * NR_RX_DESC,
+                       dev->rx_info.descs, dev->rx_info.phy_descs);
+       pci_disable_device(dev->pci_dev);
+       free_netdev(ndev);
+       pci_set_drvdata(pci_dev, NULL);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(ns83820_pci_tbl) = {
+       { 0x100b, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, .driver_data = 0, },
+       { 0, },
+};
+
+static struct pci_driver driver = {
+       .name           = "ns83820",
+       .id_table       = ns83820_pci_tbl,
+       .probe          = ns83820_init_one,
+       .remove         = __devexit_p(ns83820_remove_one),
+#if 0  /* FIXME: implement */
+       .suspend        = ,
+       .resume         = ,
+#endif
+};
+
+
+static int __init ns83820_init(void)
+{
+       printk(KERN_INFO "ns83820.c: National Semiconductor DP83820 10/100/1000 driver.\n");
+       return pci_register_driver(&driver);
+}
+
+static void __exit ns83820_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+MODULE_AUTHOR("Benjamin LaHaise <bcrl@kvack.org>");
+MODULE_DESCRIPTION("National Semiconductor DP83820 10/100/1000 driver");
+MODULE_LICENSE("GPL");
+
+MODULE_DEVICE_TABLE(pci, ns83820_pci_tbl);
+
+module_param(lnksts, int, 0);
+MODULE_PARM_DESC(lnksts, "Polarity of LNKSTS bit");
+
+module_param(ihr, int, 0);
+MODULE_PARM_DESC(ihr, "Time in 100 us increments to delay interrupts (range 0-127)");
+
+module_param(reset_phy, int, 0);
+MODULE_PARM_DESC(reset_phy, "Set to 1 to reset the PHY on startup");
+
+module_init(ns83820_init);
+module_exit(ns83820_exit);
diff --git a/drivers/net/ethernet/natsemi/sonic.c b/drivers/net/ethernet/natsemi/sonic.c
new file mode 100644 (file)
index 0000000..26e25d7
--- /dev/null
@@ -0,0 +1,742 @@
+/*
+ * sonic.c
+ *
+ * (C) 2005 Finn Thain
+ *
+ * Converted to DMA API, added zero-copy buffer handling, and
+ * (from the mac68k project) introduced dhd's support for 16-bit cards.
+ *
+ * (C) 1996,1998 by Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * This driver is based on work from Andreas Busse, but most of
+ * the code is rewritten.
+ *
+ * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
+ *
+ *    Core code included by system sonic drivers
+ *
+ * And... partially rewritten again by David Huggins-Daines in order
+ * to cope with screwed up Macintosh NICs that may or may not use
+ * 16-bit DMA.
+ *
+ * (C) 1999 David Huggins-Daines <dhd@debian.org>
+ *
+ */
+
+/*
+ * Sources: Olivetti M700-10 Risc Personal Computer hardware handbook,
+ * National Semiconductors data sheet for the DP83932B Sonic Ethernet
+ * controller, and the files "8390.c" and "skeleton.c" in this directory.
+ *
+ * Additional sources: Nat Semi data sheet for the DP83932C and Nat Semi
+ * Application Note AN-746, the files "lance.c" and "ibmlana.c". See also
+ * the NetBSD file "sys/arch/mac68k/dev/if_sn.c".
+ */
+
+
+
+/*
+ * Open/initialize the SONIC controller.
+ *
+ * This routine should set everything up anew at each open, even
+ *  registers that "should" only need to be set once at boot, so that
+ *  there is non-reboot way to recover if something goes wrong.
+ */
+static int sonic_open(struct net_device *dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       int i;
+
+       if (sonic_debug > 2)
+               printk("sonic_open: initializing sonic driver.\n");
+
+       for (i = 0; i < SONIC_NUM_RRS; i++) {
+               struct sk_buff *skb = dev_alloc_skb(SONIC_RBSIZE + 2);
+               if (skb == NULL) {
+                       while(i > 0) { /* free any that were allocated successfully */
+                               i--;
+                               dev_kfree_skb(lp->rx_skb[i]);
+                               lp->rx_skb[i] = NULL;
+                       }
+                       printk(KERN_ERR "%s: couldn't allocate receive buffers\n",
+                              dev->name);
+                       return -ENOMEM;
+               }
+               /* align IP header unless DMA requires otherwise */
+               if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
+                       skb_reserve(skb, 2);
+               lp->rx_skb[i] = skb;
+       }
+
+       for (i = 0; i < SONIC_NUM_RRS; i++) {
+               dma_addr_t laddr = dma_map_single(lp->device, skb_put(lp->rx_skb[i], SONIC_RBSIZE),
+                                                 SONIC_RBSIZE, DMA_FROM_DEVICE);
+               if (!laddr) {
+                       while(i > 0) { /* free any that were mapped successfully */
+                               i--;
+                               dma_unmap_single(lp->device, lp->rx_laddr[i], SONIC_RBSIZE, DMA_FROM_DEVICE);
+                               lp->rx_laddr[i] = (dma_addr_t)0;
+                       }
+                       for (i = 0; i < SONIC_NUM_RRS; i++) {
+                               dev_kfree_skb(lp->rx_skb[i]);
+                               lp->rx_skb[i] = NULL;
+                       }
+                       printk(KERN_ERR "%s: couldn't map rx DMA buffers\n",
+                              dev->name);
+                       return -ENOMEM;
+               }
+               lp->rx_laddr[i] = laddr;
+       }
+
+       /*
+        * Initialize the SONIC
+        */
+       sonic_init(dev);
+
+       netif_start_queue(dev);
+
+       if (sonic_debug > 2)
+               printk("sonic_open: Initialization done.\n");
+
+       return 0;
+}
+
+
+/*
+ * Close the SONIC device
+ */
+static int sonic_close(struct net_device *dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       int i;
+
+       if (sonic_debug > 2)
+               printk("sonic_close\n");
+
+       netif_stop_queue(dev);
+
+       /*
+        * stop the SONIC, disable interrupts
+        */
+       SONIC_WRITE(SONIC_IMR, 0);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+
+       /* unmap and free skbs that haven't been transmitted */
+       for (i = 0; i < SONIC_NUM_TDS; i++) {
+               if(lp->tx_laddr[i]) {
+                       dma_unmap_single(lp->device, lp->tx_laddr[i], lp->tx_len[i], DMA_TO_DEVICE);
+                       lp->tx_laddr[i] = (dma_addr_t)0;
+               }
+               if(lp->tx_skb[i]) {
+                       dev_kfree_skb(lp->tx_skb[i]);
+                       lp->tx_skb[i] = NULL;
+               }
+       }
+
+       /* unmap and free the receive buffers */
+       for (i = 0; i < SONIC_NUM_RRS; i++) {
+               if(lp->rx_laddr[i]) {
+                       dma_unmap_single(lp->device, lp->rx_laddr[i], SONIC_RBSIZE, DMA_FROM_DEVICE);
+                       lp->rx_laddr[i] = (dma_addr_t)0;
+               }
+               if(lp->rx_skb[i]) {
+                       dev_kfree_skb(lp->rx_skb[i]);
+                       lp->rx_skb[i] = NULL;
+               }
+       }
+
+       return 0;
+}
+
+static void sonic_tx_timeout(struct net_device *dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       int i;
+       /*
+        * put the Sonic into software-reset mode and
+        * disable all interrupts before releasing DMA buffers
+        */
+       SONIC_WRITE(SONIC_IMR, 0);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+       /* We could resend the original skbs. Easier to re-initialise. */
+       for (i = 0; i < SONIC_NUM_TDS; i++) {
+               if(lp->tx_laddr[i]) {
+                       dma_unmap_single(lp->device, lp->tx_laddr[i], lp->tx_len[i], DMA_TO_DEVICE);
+                       lp->tx_laddr[i] = (dma_addr_t)0;
+               }
+               if(lp->tx_skb[i]) {
+                       dev_kfree_skb(lp->tx_skb[i]);
+                       lp->tx_skb[i] = NULL;
+               }
+       }
+       /* Try to restart the adaptor. */
+       sonic_init(dev);
+       lp->stats.tx_errors++;
+       dev->trans_start = jiffies; /* prevent tx timeout */
+       netif_wake_queue(dev);
+}
+
+/*
+ * transmit packet
+ *
+ * Appends new TD during transmission thus avoiding any TX interrupts
+ * until we run out of TDs.
+ * This routine interacts closely with the ISR in that it may,
+ *   set tx_skb[i]
+ *   reset the status flags of the new TD
+ *   set and reset EOL flags
+ *   stop the tx queue
+ * The ISR interacts with this routine in various ways. It may,
+ *   reset tx_skb[i]
+ *   test the EOL and status flags of the TDs
+ *   wake the tx queue
+ * Concurrently with all of this, the SONIC is potentially writing to
+ * the status flags of the TDs.
+ * Until some mutual exclusion is added, this code will not work with SMP. However,
+ * MIPS Jazz machines and m68k Macs were all uni-processor machines.
+ */
+
+static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       dma_addr_t laddr;
+       int length;
+       int entry = lp->next_tx;
+
+       if (sonic_debug > 2)
+               printk("sonic_send_packet: skb=%p, dev=%p\n", skb, dev);
+
+       length = skb->len;
+       if (length < ETH_ZLEN) {
+               if (skb_padto(skb, ETH_ZLEN))
+                       return NETDEV_TX_OK;
+               length = ETH_ZLEN;
+       }
+
+       /*
+        * Map the packet data into the logical DMA address space
+        */
+
+       laddr = dma_map_single(lp->device, skb->data, length, DMA_TO_DEVICE);
+       if (!laddr) {
+               printk(KERN_ERR "%s: failed to map tx DMA buffer.\n", dev->name);
+               dev_kfree_skb(skb);
+               return NETDEV_TX_BUSY;
+       }
+
+       sonic_tda_put(dev, entry, SONIC_TD_STATUS, 0);       /* clear status */
+       sonic_tda_put(dev, entry, SONIC_TD_FRAG_COUNT, 1);   /* single fragment */
+       sonic_tda_put(dev, entry, SONIC_TD_PKTSIZE, length); /* length of packet */
+       sonic_tda_put(dev, entry, SONIC_TD_FRAG_PTR_L, laddr & 0xffff);
+       sonic_tda_put(dev, entry, SONIC_TD_FRAG_PTR_H, laddr >> 16);
+       sonic_tda_put(dev, entry, SONIC_TD_FRAG_SIZE, length);
+       sonic_tda_put(dev, entry, SONIC_TD_LINK,
+               sonic_tda_get(dev, entry, SONIC_TD_LINK) | SONIC_EOL);
+
+       /*
+        * Must set tx_skb[entry] only after clearing status, and
+        * before clearing EOL and before stopping queue
+        */
+       wmb();
+       lp->tx_len[entry] = length;
+       lp->tx_laddr[entry] = laddr;
+       lp->tx_skb[entry] = skb;
+
+       wmb();
+       sonic_tda_put(dev, lp->eol_tx, SONIC_TD_LINK,
+                                 sonic_tda_get(dev, lp->eol_tx, SONIC_TD_LINK) & ~SONIC_EOL);
+       lp->eol_tx = entry;
+
+       lp->next_tx = (entry + 1) & SONIC_TDS_MASK;
+       if (lp->tx_skb[lp->next_tx] != NULL) {
+               /* The ring is full, the ISR has yet to process the next TD. */
+               if (sonic_debug > 3)
+                       printk("%s: stopping queue\n", dev->name);
+               netif_stop_queue(dev);
+               /* after this packet, wait for ISR to free up some TDAs */
+       } else netif_start_queue(dev);
+
+       if (sonic_debug > 2)
+               printk("sonic_send_packet: issuing Tx command\n");
+
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP);
+
+       return NETDEV_TX_OK;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static irqreturn_t sonic_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       struct sonic_local *lp = netdev_priv(dev);
+       int status;
+
+       if (!(status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT))
+               return IRQ_NONE;
+
+       do {
+               if (status & SONIC_INT_PKTRX) {
+                       if (sonic_debug > 2)
+                               printk("%s: packet rx\n", dev->name);
+                       sonic_rx(dev);  /* got packet(s) */
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_PKTRX); /* clear the interrupt */
+               }
+
+               if (status & SONIC_INT_TXDN) {
+                       int entry = lp->cur_tx;
+                       int td_status;
+                       int freed_some = 0;
+
+                       /* At this point, cur_tx is the index of a TD that is one of:
+                        *   unallocated/freed                          (status set   & tx_skb[entry] clear)
+                        *   allocated and sent                         (status set   & tx_skb[entry] set  )
+                        *   allocated and not yet sent                 (status clear & tx_skb[entry] set  )
+                        *   still being allocated by sonic_send_packet (status clear & tx_skb[entry] clear)
+                        */
+
+                       if (sonic_debug > 2)
+                               printk("%s: tx done\n", dev->name);
+
+                       while (lp->tx_skb[entry] != NULL) {
+                               if ((td_status = sonic_tda_get(dev, entry, SONIC_TD_STATUS)) == 0)
+                                       break;
+
+                               if (td_status & 0x0001) {
+                                       lp->stats.tx_packets++;
+                                       lp->stats.tx_bytes += sonic_tda_get(dev, entry, SONIC_TD_PKTSIZE);
+                               } else {
+                                       lp->stats.tx_errors++;
+                                       if (td_status & 0x0642)
+                                               lp->stats.tx_aborted_errors++;
+                                       if (td_status & 0x0180)
+                                               lp->stats.tx_carrier_errors++;
+                                       if (td_status & 0x0020)
+                                               lp->stats.tx_window_errors++;
+                                       if (td_status & 0x0004)
+                                               lp->stats.tx_fifo_errors++;
+                               }
+
+                               /* We must free the original skb */
+                               dev_kfree_skb_irq(lp->tx_skb[entry]);
+                               lp->tx_skb[entry] = NULL;
+                               /* and unmap DMA buffer */
+                               dma_unmap_single(lp->device, lp->tx_laddr[entry], lp->tx_len[entry], DMA_TO_DEVICE);
+                               lp->tx_laddr[entry] = (dma_addr_t)0;
+                               freed_some = 1;
+
+                               if (sonic_tda_get(dev, entry, SONIC_TD_LINK) & SONIC_EOL) {
+                                       entry = (entry + 1) & SONIC_TDS_MASK;
+                                       break;
+                               }
+                               entry = (entry + 1) & SONIC_TDS_MASK;
+                       }
+
+                       if (freed_some || lp->tx_skb[entry] == NULL)
+                               netif_wake_queue(dev);  /* The ring is no longer full */
+                       lp->cur_tx = entry;
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_TXDN); /* clear the interrupt */
+               }
+
+               /*
+                * check error conditions
+                */
+               if (status & SONIC_INT_RFO) {
+                       if (sonic_debug > 1)
+                               printk("%s: rx fifo overrun\n", dev->name);
+                       lp->stats.rx_fifo_errors++;
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RFO); /* clear the interrupt */
+               }
+               if (status & SONIC_INT_RDE) {
+                       if (sonic_debug > 1)
+                               printk("%s: rx descriptors exhausted\n", dev->name);
+                       lp->stats.rx_dropped++;
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RDE); /* clear the interrupt */
+               }
+               if (status & SONIC_INT_RBAE) {
+                       if (sonic_debug > 1)
+                               printk("%s: rx buffer area exceeded\n", dev->name);
+                       lp->stats.rx_dropped++;
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RBAE); /* clear the interrupt */
+               }
+
+               /* counter overruns; all counters are 16bit wide */
+               if (status & SONIC_INT_FAE) {
+                       lp->stats.rx_frame_errors += 65536;
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_FAE); /* clear the interrupt */
+               }
+               if (status & SONIC_INT_CRC) {
+                       lp->stats.rx_crc_errors += 65536;
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_CRC); /* clear the interrupt */
+               }
+               if (status & SONIC_INT_MP) {
+                       lp->stats.rx_missed_errors += 65536;
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_MP); /* clear the interrupt */
+               }
+
+               /* transmit error */
+               if (status & SONIC_INT_TXER) {
+                       if ((SONIC_READ(SONIC_TCR) & SONIC_TCR_FU) && (sonic_debug > 2))
+                               printk(KERN_ERR "%s: tx fifo underrun\n", dev->name);
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_TXER); /* clear the interrupt */
+               }
+
+               /* bus retry */
+               if (status & SONIC_INT_BR) {
+                       printk(KERN_ERR "%s: Bus retry occurred! Device interrupt disabled.\n",
+                               dev->name);
+                       /* ... to help debug DMA problems causing endless interrupts. */
+                       /* Bounce the eth interface to turn on the interrupt again. */
+                       SONIC_WRITE(SONIC_IMR, 0);
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_BR); /* clear the interrupt */
+               }
+
+               /* load CAM done */
+               if (status & SONIC_INT_LCD)
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_LCD); /* clear the interrupt */
+       } while((status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT));
+       return IRQ_HANDLED;
+}
+
+/*
+ * We have a good packet(s), pass it/them up the network stack.
+ */
+static void sonic_rx(struct net_device *dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       int status;
+       int entry = lp->cur_rx;
+
+       while (sonic_rda_get(dev, entry, SONIC_RD_IN_USE) == 0) {
+               struct sk_buff *used_skb;
+               struct sk_buff *new_skb;
+               dma_addr_t new_laddr;
+               u16 bufadr_l;
+               u16 bufadr_h;
+               int pkt_len;
+
+               status = sonic_rda_get(dev, entry, SONIC_RD_STATUS);
+               if (status & SONIC_RCR_PRX) {
+                       /* Malloc up new buffer. */
+                       new_skb = dev_alloc_skb(SONIC_RBSIZE + 2);
+                       if (new_skb == NULL) {
+                               printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n", dev->name);
+                               lp->stats.rx_dropped++;
+                               break;
+                       }
+                       /* provide 16 byte IP header alignment unless DMA requires otherwise */
+                       if(SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
+                               skb_reserve(new_skb, 2);
+
+                       new_laddr = dma_map_single(lp->device, skb_put(new_skb, SONIC_RBSIZE),
+                                              SONIC_RBSIZE, DMA_FROM_DEVICE);
+                       if (!new_laddr) {
+                               dev_kfree_skb(new_skb);
+                               printk(KERN_ERR "%s: Failed to map rx buffer, dropping packet.\n", dev->name);
+                               lp->stats.rx_dropped++;
+                               break;
+                       }
+
+                       /* now we have a new skb to replace it, pass the used one up the stack */
+                       dma_unmap_single(lp->device, lp->rx_laddr[entry], SONIC_RBSIZE, DMA_FROM_DEVICE);
+                       used_skb = lp->rx_skb[entry];
+                       pkt_len = sonic_rda_get(dev, entry, SONIC_RD_PKTLEN);
+                       skb_trim(used_skb, pkt_len);
+                       used_skb->protocol = eth_type_trans(used_skb, dev);
+                       netif_rx(used_skb);
+                       lp->stats.rx_packets++;
+                       lp->stats.rx_bytes += pkt_len;
+
+                       /* and insert the new skb */
+                       lp->rx_laddr[entry] = new_laddr;
+                       lp->rx_skb[entry] = new_skb;
+
+                       bufadr_l = (unsigned long)new_laddr & 0xffff;
+                       bufadr_h = (unsigned long)new_laddr >> 16;
+                       sonic_rra_put(dev, entry, SONIC_RR_BUFADR_L, bufadr_l);
+                       sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, bufadr_h);
+               } else {
+                       /* This should only happen, if we enable accepting broken packets. */
+                       lp->stats.rx_errors++;
+                       if (status & SONIC_RCR_FAER)
+                               lp->stats.rx_frame_errors++;
+                       if (status & SONIC_RCR_CRCR)
+                               lp->stats.rx_crc_errors++;
+               }
+               if (status & SONIC_RCR_LPKT) {
+                       /*
+                        * this was the last packet out of the current receive buffer
+                        * give the buffer back to the SONIC
+                        */
+                       lp->cur_rwp += SIZEOF_SONIC_RR * SONIC_BUS_SCALE(lp->dma_bitmode);
+                       if (lp->cur_rwp >= lp->rra_end) lp->cur_rwp = lp->rra_laddr & 0xffff;
+                       SONIC_WRITE(SONIC_RWP, lp->cur_rwp);
+                       if (SONIC_READ(SONIC_ISR) & SONIC_INT_RBE) {
+                               if (sonic_debug > 2)
+                                       printk("%s: rx buffer exhausted\n", dev->name);
+                               SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE); /* clear the flag */
+                       }
+               } else
+                       printk(KERN_ERR "%s: rx desc without RCR_LPKT. Shouldn't happen !?\n",
+                            dev->name);
+               /*
+                * give back the descriptor
+                */
+               sonic_rda_put(dev, entry, SONIC_RD_LINK,
+                       sonic_rda_get(dev, entry, SONIC_RD_LINK) | SONIC_EOL);
+               sonic_rda_put(dev, entry, SONIC_RD_IN_USE, 1);
+               sonic_rda_put(dev, lp->eol_rx, SONIC_RD_LINK,
+                       sonic_rda_get(dev, lp->eol_rx, SONIC_RD_LINK) & ~SONIC_EOL);
+               lp->eol_rx = entry;
+               lp->cur_rx = entry = (entry + 1) & SONIC_RDS_MASK;
+       }
+       /*
+        * If any worth-while packets have been received, netif_rx()
+        * has done a mark_bh(NET_BH) for us and will work on them
+        * when we get to the bottom-half routine.
+        */
+}
+
+
+/*
+ * Get the current statistics.
+ * This may be called with the device open or closed.
+ */
+static struct net_device_stats *sonic_get_stats(struct net_device *dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+
+       /* read the tally counter from the SONIC and reset them */
+       lp->stats.rx_crc_errors += SONIC_READ(SONIC_CRCT);
+       SONIC_WRITE(SONIC_CRCT, 0xffff);
+       lp->stats.rx_frame_errors += SONIC_READ(SONIC_FAET);
+       SONIC_WRITE(SONIC_FAET, 0xffff);
+       lp->stats.rx_missed_errors += SONIC_READ(SONIC_MPT);
+       SONIC_WRITE(SONIC_MPT, 0xffff);
+
+       return &lp->stats;
+}
+
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void sonic_multicast_list(struct net_device *dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       unsigned int rcr;
+       struct netdev_hw_addr *ha;
+       unsigned char *addr;
+       int i;
+
+       rcr = SONIC_READ(SONIC_RCR) & ~(SONIC_RCR_PRO | SONIC_RCR_AMC);
+       rcr |= SONIC_RCR_BRD;   /* accept broadcast packets */
+
+       if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
+               rcr |= SONIC_RCR_PRO;
+       } else {
+               if ((dev->flags & IFF_ALLMULTI) ||
+                   (netdev_mc_count(dev) > 15)) {
+                       rcr |= SONIC_RCR_AMC;
+               } else {
+                       if (sonic_debug > 2)
+                               printk("sonic_multicast_list: mc_count %d\n",
+                                      netdev_mc_count(dev));
+                       sonic_set_cam_enable(dev, 1);  /* always enable our own address */
+                       i = 1;
+                       netdev_for_each_mc_addr(ha, dev) {
+                               addr = ha->addr;
+                               sonic_cda_put(dev, i, SONIC_CD_CAP0, addr[1] << 8 | addr[0]);
+                               sonic_cda_put(dev, i, SONIC_CD_CAP1, addr[3] << 8 | addr[2]);
+                               sonic_cda_put(dev, i, SONIC_CD_CAP2, addr[5] << 8 | addr[4]);
+                               sonic_set_cam_enable(dev, sonic_get_cam_enable(dev) | (1 << i));
+                               i++;
+                       }
+                       SONIC_WRITE(SONIC_CDC, 16);
+                       /* issue Load CAM command */
+                       SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff);
+                       SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
+               }
+       }
+
+       if (sonic_debug > 2)
+               printk("sonic_multicast_list: setting RCR=%x\n", rcr);
+
+       SONIC_WRITE(SONIC_RCR, rcr);
+}
+
+
+/*
+ * Initialize the SONIC ethernet controller.
+ */
+static int sonic_init(struct net_device *dev)
+{
+       unsigned int cmd;
+       struct sonic_local *lp = netdev_priv(dev);
+       int i;
+
+       /*
+        * put the Sonic into software-reset mode and
+        * disable all interrupts
+        */
+       SONIC_WRITE(SONIC_IMR, 0);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+
+       /*
+        * clear software reset flag, disable receiver, clear and
+        * enable interrupts, then completely initialize the SONIC
+        */
+       SONIC_WRITE(SONIC_CMD, 0);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS);
+
+       /*
+        * initialize the receive resource area
+        */
+       if (sonic_debug > 2)
+               printk("sonic_init: initialize receive resource area\n");
+
+       for (i = 0; i < SONIC_NUM_RRS; i++) {
+               u16 bufadr_l = (unsigned long)lp->rx_laddr[i] & 0xffff;
+               u16 bufadr_h = (unsigned long)lp->rx_laddr[i] >> 16;
+               sonic_rra_put(dev, i, SONIC_RR_BUFADR_L, bufadr_l);
+               sonic_rra_put(dev, i, SONIC_RR_BUFADR_H, bufadr_h);
+               sonic_rra_put(dev, i, SONIC_RR_BUFSIZE_L, SONIC_RBSIZE >> 1);
+               sonic_rra_put(dev, i, SONIC_RR_BUFSIZE_H, 0);
+       }
+
+       /* initialize all RRA registers */
+       lp->rra_end = (lp->rra_laddr + SONIC_NUM_RRS * SIZEOF_SONIC_RR *
+                                       SONIC_BUS_SCALE(lp->dma_bitmode)) & 0xffff;
+       lp->cur_rwp = (lp->rra_laddr + (SONIC_NUM_RRS - 1) * SIZEOF_SONIC_RR *
+                                       SONIC_BUS_SCALE(lp->dma_bitmode)) & 0xffff;
+
+       SONIC_WRITE(SONIC_RSA, lp->rra_laddr & 0xffff);
+       SONIC_WRITE(SONIC_REA, lp->rra_end);
+       SONIC_WRITE(SONIC_RRP, lp->rra_laddr & 0xffff);
+       SONIC_WRITE(SONIC_RWP, lp->cur_rwp);
+       SONIC_WRITE(SONIC_URRA, lp->rra_laddr >> 16);
+       SONIC_WRITE(SONIC_EOBC, (SONIC_RBSIZE >> 1) - (lp->dma_bitmode ? 2 : 1));
+
+       /* load the resource pointers */
+       if (sonic_debug > 3)
+               printk("sonic_init: issuing RRRA command\n");
+
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RRRA);
+       i = 0;
+       while (i++ < 100) {
+               if (SONIC_READ(SONIC_CMD) & SONIC_CR_RRRA)
+                       break;
+       }
+
+       if (sonic_debug > 2)
+               printk("sonic_init: status=%x i=%d\n", SONIC_READ(SONIC_CMD), i);
+
+       /*
+        * Initialize the receive descriptors so that they
+        * become a circular linked list, ie. let the last
+        * descriptor point to the first again.
+        */
+       if (sonic_debug > 2)
+               printk("sonic_init: initialize receive descriptors\n");
+       for (i=0; i<SONIC_NUM_RDS; i++) {
+               sonic_rda_put(dev, i, SONIC_RD_STATUS, 0);
+               sonic_rda_put(dev, i, SONIC_RD_PKTLEN, 0);
+               sonic_rda_put(dev, i, SONIC_RD_PKTPTR_L, 0);
+               sonic_rda_put(dev, i, SONIC_RD_PKTPTR_H, 0);
+               sonic_rda_put(dev, i, SONIC_RD_SEQNO, 0);
+               sonic_rda_put(dev, i, SONIC_RD_IN_USE, 1);
+               sonic_rda_put(dev, i, SONIC_RD_LINK,
+                       lp->rda_laddr +
+                       ((i+1) * SIZEOF_SONIC_RD * SONIC_BUS_SCALE(lp->dma_bitmode)));
+       }
+       /* fix last descriptor */
+       sonic_rda_put(dev, SONIC_NUM_RDS - 1, SONIC_RD_LINK,
+               (lp->rda_laddr & 0xffff) | SONIC_EOL);
+       lp->eol_rx = SONIC_NUM_RDS - 1;
+       lp->cur_rx = 0;
+       SONIC_WRITE(SONIC_URDA, lp->rda_laddr >> 16);
+       SONIC_WRITE(SONIC_CRDA, lp->rda_laddr & 0xffff);
+
+       /*
+        * initialize transmit descriptors
+        */
+       if (sonic_debug > 2)
+               printk("sonic_init: initialize transmit descriptors\n");
+       for (i = 0; i < SONIC_NUM_TDS; i++) {
+               sonic_tda_put(dev, i, SONIC_TD_STATUS, 0);
+               sonic_tda_put(dev, i, SONIC_TD_CONFIG, 0);
+               sonic_tda_put(dev, i, SONIC_TD_PKTSIZE, 0);
+               sonic_tda_put(dev, i, SONIC_TD_FRAG_COUNT, 0);
+               sonic_tda_put(dev, i, SONIC_TD_LINK,
+                       (lp->tda_laddr & 0xffff) +
+                       (i + 1) * SIZEOF_SONIC_TD * SONIC_BUS_SCALE(lp->dma_bitmode));
+               lp->tx_skb[i] = NULL;
+       }
+       /* fix last descriptor */
+       sonic_tda_put(dev, SONIC_NUM_TDS - 1, SONIC_TD_LINK,
+               (lp->tda_laddr & 0xffff));
+
+       SONIC_WRITE(SONIC_UTDA, lp->tda_laddr >> 16);
+       SONIC_WRITE(SONIC_CTDA, lp->tda_laddr & 0xffff);
+       lp->cur_tx = lp->next_tx = 0;
+       lp->eol_tx = SONIC_NUM_TDS - 1;
+
+       /*
+        * put our own address to CAM desc[0]
+        */
+       sonic_cda_put(dev, 0, SONIC_CD_CAP0, dev->dev_addr[1] << 8 | dev->dev_addr[0]);
+       sonic_cda_put(dev, 0, SONIC_CD_CAP1, dev->dev_addr[3] << 8 | dev->dev_addr[2]);
+       sonic_cda_put(dev, 0, SONIC_CD_CAP2, dev->dev_addr[5] << 8 | dev->dev_addr[4]);
+       sonic_set_cam_enable(dev, 1);
+
+       for (i = 0; i < 16; i++)
+               sonic_cda_put(dev, i, SONIC_CD_ENTRY_POINTER, i);
+
+       /*
+        * initialize CAM registers
+        */
+       SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff);
+       SONIC_WRITE(SONIC_CDC, 16);
+
+       /*
+        * load the CAM
+        */
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
+
+       i = 0;
+       while (i++ < 100) {
+               if (SONIC_READ(SONIC_ISR) & SONIC_INT_LCD)
+                       break;
+       }
+       if (sonic_debug > 2) {
+               printk("sonic_init: CMD=%x, ISR=%x\n, i=%d",
+                      SONIC_READ(SONIC_CMD), SONIC_READ(SONIC_ISR), i);
+       }
+
+       /*
+        * enable receiver, disable loopback
+        * and enable all interrupts
+        */
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RXEN | SONIC_CR_STP);
+       SONIC_WRITE(SONIC_RCR, SONIC_RCR_DEFAULT);
+       SONIC_WRITE(SONIC_TCR, SONIC_TCR_DEFAULT);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+       SONIC_WRITE(SONIC_IMR, SONIC_IMR_DEFAULT);
+
+       cmd = SONIC_READ(SONIC_CMD);
+       if ((cmd & SONIC_CR_RXEN) == 0 || (cmd & SONIC_CR_STP) == 0)
+               printk(KERN_ERR "sonic_init: failed, status=%x\n", cmd);
+
+       if (sonic_debug > 2)
+               printk("sonic_init: new status=%x\n",
+                      SONIC_READ(SONIC_CMD));
+
+       return 0;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/natsemi/sonic.h b/drivers/net/ethernet/natsemi/sonic.h
new file mode 100644 (file)
index 0000000..07091dd
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Header file for sonic.c
+ *
+ * (C) Waldorf Electronics, Germany
+ * Written by Andreas Busse
+ *
+ * NOTE: most of the structure definitions here are endian dependent.
+ * If you want to use this driver on big endian machines, the data
+ * and pad structure members must be exchanged. Also, the structures
+ * need to be changed accordingly to the bus size.
+ *
+ * 981229 MSch:        did just that for the 68k Mac port (32 bit, big endian)
+ *
+ * 990611 David Huggins-Daines <dhd@debian.org>: This machine abstraction
+ * does not cope with 16-bit bus sizes very well.  Therefore I have
+ * rewritten it with ugly macros and evil inlines.
+ *
+ * 050625 Finn Thain: introduced more 32-bit cards and dhd's support
+ *        for 16-bit cards (from the mac68k project).
+ */
+
+#ifndef SONIC_H
+#define SONIC_H
+
+
+/*
+ * SONIC register offsets
+ */
+
+#define SONIC_CMD              0x00
+#define SONIC_DCR              0x01
+#define SONIC_RCR              0x02
+#define SONIC_TCR              0x03
+#define SONIC_IMR              0x04
+#define SONIC_ISR              0x05
+
+#define SONIC_UTDA             0x06
+#define SONIC_CTDA             0x07
+
+#define SONIC_URDA             0x0d
+#define SONIC_CRDA             0x0e
+#define SONIC_EOBC             0x13
+#define SONIC_URRA             0x14
+#define SONIC_RSA              0x15
+#define SONIC_REA              0x16
+#define SONIC_RRP              0x17
+#define SONIC_RWP              0x18
+#define SONIC_RSC              0x2b
+
+#define SONIC_CEP              0x21
+#define SONIC_CAP2             0x22
+#define SONIC_CAP1             0x23
+#define SONIC_CAP0             0x24
+#define SONIC_CE               0x25
+#define SONIC_CDP              0x26
+#define SONIC_CDC              0x27
+
+#define SONIC_WT0              0x29
+#define SONIC_WT1              0x2a
+
+#define SONIC_SR               0x28
+
+
+/* test-only registers */
+
+#define SONIC_TPS              0x08
+#define SONIC_TFC              0x09
+#define SONIC_TSA0             0x0a
+#define SONIC_TSA1             0x0b
+#define SONIC_TFS              0x0c
+
+#define SONIC_CRBA0            0x0f
+#define SONIC_CRBA1            0x10
+#define SONIC_RBWC0            0x11
+#define SONIC_RBWC1            0x12
+#define SONIC_TTDA             0x20
+#define SONIC_MDT              0x2f
+
+#define SONIC_TRBA0            0x19
+#define SONIC_TRBA1            0x1a
+#define SONIC_TBWC0            0x1b
+#define SONIC_TBWC1            0x1c
+#define SONIC_LLFA             0x1f
+
+#define SONIC_ADDR0            0x1d
+#define SONIC_ADDR1            0x1e
+
+/*
+ * Error counters
+ */
+
+#define SONIC_CRCT              0x2c
+#define SONIC_FAET              0x2d
+#define SONIC_MPT               0x2e
+
+#define SONIC_DCR2              0x3f
+
+/*
+ * SONIC command bits
+ */
+
+#define SONIC_CR_LCAM           0x0200
+#define SONIC_CR_RRRA           0x0100
+#define SONIC_CR_RST            0x0080
+#define SONIC_CR_ST             0x0020
+#define SONIC_CR_STP            0x0010
+#define SONIC_CR_RXEN           0x0008
+#define SONIC_CR_RXDIS          0x0004
+#define SONIC_CR_TXP            0x0002
+#define SONIC_CR_HTX            0x0001
+
+/*
+ * SONIC data configuration bits
+ */
+
+#define SONIC_DCR_EXBUS         0x8000
+#define SONIC_DCR_LBR           0x2000
+#define SONIC_DCR_PO1           0x1000
+#define SONIC_DCR_PO0           0x0800
+#define SONIC_DCR_SBUS          0x0400
+#define SONIC_DCR_USR1          0x0200
+#define SONIC_DCR_USR0          0x0100
+#define SONIC_DCR_WC1           0x0080
+#define SONIC_DCR_WC0           0x0040
+#define SONIC_DCR_DW            0x0020
+#define SONIC_DCR_BMS           0x0010
+#define SONIC_DCR_RFT1          0x0008
+#define SONIC_DCR_RFT0          0x0004
+#define SONIC_DCR_TFT1          0x0002
+#define SONIC_DCR_TFT0          0x0001
+
+/*
+ * Constants for the SONIC receive control register.
+ */
+
+#define SONIC_RCR_ERR           0x8000
+#define SONIC_RCR_RNT           0x4000
+#define SONIC_RCR_BRD           0x2000
+#define SONIC_RCR_PRO           0x1000
+#define SONIC_RCR_AMC           0x0800
+#define SONIC_RCR_LB1           0x0400
+#define SONIC_RCR_LB0           0x0200
+
+#define SONIC_RCR_MC            0x0100
+#define SONIC_RCR_BC            0x0080
+#define SONIC_RCR_LPKT          0x0040
+#define SONIC_RCR_CRS           0x0020
+#define SONIC_RCR_COL           0x0010
+#define SONIC_RCR_CRCR          0x0008
+#define SONIC_RCR_FAER          0x0004
+#define SONIC_RCR_LBK           0x0002
+#define SONIC_RCR_PRX           0x0001
+
+#define SONIC_RCR_LB_OFF        0
+#define SONIC_RCR_LB_MAC        SONIC_RCR_LB0
+#define SONIC_RCR_LB_ENDEC      SONIC_RCR_LB1
+#define SONIC_RCR_LB_TRANS      (SONIC_RCR_LB0 | SONIC_RCR_LB1)
+
+/* default RCR setup */
+
+#define SONIC_RCR_DEFAULT       (SONIC_RCR_BRD)
+
+
+/*
+ * SONIC Transmit Control register bits
+ */
+
+#define SONIC_TCR_PINTR         0x8000
+#define SONIC_TCR_POWC          0x4000
+#define SONIC_TCR_CRCI          0x2000
+#define SONIC_TCR_EXDIS         0x1000
+#define SONIC_TCR_EXD           0x0400
+#define SONIC_TCR_DEF           0x0200
+#define SONIC_TCR_NCRS          0x0100
+#define SONIC_TCR_CRLS          0x0080
+#define SONIC_TCR_EXC           0x0040
+#define SONIC_TCR_PMB           0x0008
+#define SONIC_TCR_FU            0x0004
+#define SONIC_TCR_BCM           0x0002
+#define SONIC_TCR_PTX           0x0001
+
+#define SONIC_TCR_DEFAULT       0x0000
+
+/*
+ * Constants for the SONIC_INTERRUPT_MASK and
+ * SONIC_INTERRUPT_STATUS registers.
+ */
+
+#define SONIC_INT_BR           0x4000
+#define SONIC_INT_HBL          0x2000
+#define SONIC_INT_LCD          0x1000
+#define SONIC_INT_PINT         0x0800
+#define SONIC_INT_PKTRX                0x0400
+#define SONIC_INT_TXDN         0x0200
+#define SONIC_INT_TXER         0x0100
+#define SONIC_INT_TC           0x0080
+#define SONIC_INT_RDE          0x0040
+#define SONIC_INT_RBE          0x0020
+#define SONIC_INT_RBAE         0x0010
+#define SONIC_INT_CRC          0x0008
+#define SONIC_INT_FAE          0x0004
+#define SONIC_INT_MP           0x0002
+#define SONIC_INT_RFO          0x0001
+
+
+/*
+ * The interrupts we allow.
+ */
+
+#define SONIC_IMR_DEFAULT     ( SONIC_INT_BR | \
+                                SONIC_INT_LCD | \
+                                SONIC_INT_RFO | \
+                                SONIC_INT_PKTRX | \
+                                SONIC_INT_TXDN | \
+                                SONIC_INT_TXER | \
+                                SONIC_INT_RDE | \
+                                SONIC_INT_RBAE | \
+                                SONIC_INT_CRC | \
+                                SONIC_INT_FAE | \
+                                SONIC_INT_MP)
+
+
+#define SONIC_EOL       0x0001
+#define CAM_DESCRIPTORS 16
+
+/* Offsets in the various DMA buffers accessed by the SONIC */
+
+#define SONIC_BITMODE16 0
+#define SONIC_BITMODE32 1
+#define SONIC_BUS_SCALE(bitmode) ((bitmode) ? 4 : 2)
+/* Note!  These are all measured in bus-size units, so use SONIC_BUS_SCALE */
+#define SIZEOF_SONIC_RR 4
+#define SONIC_RR_BUFADR_L  0
+#define SONIC_RR_BUFADR_H  1
+#define SONIC_RR_BUFSIZE_L 2
+#define SONIC_RR_BUFSIZE_H 3
+
+#define SIZEOF_SONIC_RD 7
+#define SONIC_RD_STATUS   0
+#define SONIC_RD_PKTLEN   1
+#define SONIC_RD_PKTPTR_L 2
+#define SONIC_RD_PKTPTR_H 3
+#define SONIC_RD_SEQNO    4
+#define SONIC_RD_LINK     5
+#define SONIC_RD_IN_USE   6
+
+#define SIZEOF_SONIC_TD 8
+#define SONIC_TD_STATUS       0
+#define SONIC_TD_CONFIG       1
+#define SONIC_TD_PKTSIZE      2
+#define SONIC_TD_FRAG_COUNT   3
+#define SONIC_TD_FRAG_PTR_L   4
+#define SONIC_TD_FRAG_PTR_H   5
+#define SONIC_TD_FRAG_SIZE    6
+#define SONIC_TD_LINK         7
+
+#define SIZEOF_SONIC_CD 4
+#define SONIC_CD_ENTRY_POINTER 0
+#define SONIC_CD_CAP0          1
+#define SONIC_CD_CAP1          2
+#define SONIC_CD_CAP2          3
+
+#define SIZEOF_SONIC_CDA ((CAM_DESCRIPTORS * SIZEOF_SONIC_CD) + 1)
+#define SONIC_CDA_CAM_ENABLE   (CAM_DESCRIPTORS * SIZEOF_SONIC_CD)
+
+/*
+ * Some tunables for the buffer areas. Power of 2 is required
+ * the current driver uses one receive buffer for each descriptor.
+ *
+ * MSch: use more buffer space for the slow m68k Macs!
+ */
+#define SONIC_NUM_RRS   16            /* number of receive resources */
+#define SONIC_NUM_RDS   SONIC_NUM_RRS /* number of receive descriptors */
+#define SONIC_NUM_TDS   16            /* number of transmit descriptors */
+
+#define SONIC_RDS_MASK  (SONIC_NUM_RDS-1)
+#define SONIC_TDS_MASK  (SONIC_NUM_TDS-1)
+
+#define SONIC_RBSIZE   1520          /* size of one resource buffer */
+
+/* Again, measured in bus size units! */
+#define SIZEOF_SONIC_DESC (SIZEOF_SONIC_CDA    \
+       + (SIZEOF_SONIC_TD * SONIC_NUM_TDS)     \
+       + (SIZEOF_SONIC_RD * SONIC_NUM_RDS)     \
+       + (SIZEOF_SONIC_RR * SONIC_NUM_RRS))
+
+/* Information that need to be kept for each board. */
+struct sonic_local {
+       /* Bus size.  0 == 16 bits, 1 == 32 bits. */
+       int dma_bitmode;
+       /* Register offset within the longword (independent of endianness,
+          and varies from one type of Macintosh SONIC to another
+          (Aarrgh)) */
+       int reg_offset;
+       void *descriptors;
+       /* Crud.  These areas have to be within the same 64K.  Therefore
+       we allocate a desriptors page, and point these to places within it. */
+       void *cda;  /* CAM descriptor area */
+       void *tda;  /* Transmit descriptor area */
+       void *rra;  /* Receive resource area */
+       void *rda;  /* Receive descriptor area */
+       struct sk_buff* volatile rx_skb[SONIC_NUM_RRS]; /* packets to be received */
+       struct sk_buff* volatile tx_skb[SONIC_NUM_TDS]; /* packets to be transmitted */
+       unsigned int tx_len[SONIC_NUM_TDS]; /* lengths of tx DMA mappings */
+       /* Logical DMA addresses on MIPS, bus addresses on m68k
+        * (so "laddr" is a bit misleading) */
+       dma_addr_t descriptors_laddr;
+       u32 cda_laddr;              /* logical DMA address of CDA */
+       u32 tda_laddr;              /* logical DMA address of TDA */
+       u32 rra_laddr;              /* logical DMA address of RRA */
+       u32 rda_laddr;              /* logical DMA address of RDA */
+       dma_addr_t rx_laddr[SONIC_NUM_RRS]; /* logical DMA addresses of rx skbuffs */
+       dma_addr_t tx_laddr[SONIC_NUM_TDS]; /* logical DMA addresses of tx skbuffs */
+       unsigned int rra_end;
+       unsigned int cur_rwp;
+       unsigned int cur_rx;
+       unsigned int cur_tx;           /* first unacked transmit packet */
+       unsigned int eol_rx;
+       unsigned int eol_tx;           /* last unacked transmit packet */
+       unsigned int next_tx;          /* next free TD */
+       struct device *device;         /* generic device */
+       struct net_device_stats stats;
+};
+
+#define TX_TIMEOUT (3 * HZ)
+
+/* Index to functions, as function prototypes. */
+
+static int sonic_open(struct net_device *dev);
+static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t sonic_interrupt(int irq, void *dev_id);
+static void sonic_rx(struct net_device *dev);
+static int sonic_close(struct net_device *dev);
+static struct net_device_stats *sonic_get_stats(struct net_device *dev);
+static void sonic_multicast_list(struct net_device *dev);
+static int sonic_init(struct net_device *dev);
+static void sonic_tx_timeout(struct net_device *dev);
+
+/* Internal inlines for reading/writing DMA buffers.  Note that bus
+   size and endianness matter here, whereas they don't for registers,
+   as far as we can tell. */
+/* OpenBSD calls this "SWO".  I'd like to think that sonic_buf_put()
+   is a much better name. */
+static inline void sonic_buf_put(void* base, int bitmode,
+                                int offset, __u16 val)
+{
+       if (bitmode)
+#ifdef __BIG_ENDIAN
+               ((__u16 *) base + (offset*2))[1] = val;
+#else
+               ((__u16 *) base + (offset*2))[0] = val;
+#endif
+       else
+               ((__u16 *) base)[offset] = val;
+}
+
+static inline __u16 sonic_buf_get(void* base, int bitmode,
+                                 int offset)
+{
+       if (bitmode)
+#ifdef __BIG_ENDIAN
+               return ((volatile __u16 *) base + (offset*2))[1];
+#else
+               return ((volatile __u16 *) base + (offset*2))[0];
+#endif
+       else
+               return ((volatile __u16 *) base)[offset];
+}
+
+/* Inlines that you should actually use for reading/writing DMA buffers */
+static inline void sonic_cda_put(struct net_device* dev, int entry,
+                                int offset, __u16 val)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       sonic_buf_put(lp->cda, lp->dma_bitmode,
+                     (entry * SIZEOF_SONIC_CD) + offset, val);
+}
+
+static inline __u16 sonic_cda_get(struct net_device* dev, int entry,
+                                 int offset)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       return sonic_buf_get(lp->cda, lp->dma_bitmode,
+                            (entry * SIZEOF_SONIC_CD) + offset);
+}
+
+static inline void sonic_set_cam_enable(struct net_device* dev, __u16 val)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       sonic_buf_put(lp->cda, lp->dma_bitmode, SONIC_CDA_CAM_ENABLE, val);
+}
+
+static inline __u16 sonic_get_cam_enable(struct net_device* dev)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       return sonic_buf_get(lp->cda, lp->dma_bitmode, SONIC_CDA_CAM_ENABLE);
+}
+
+static inline void sonic_tda_put(struct net_device* dev, int entry,
+                                int offset, __u16 val)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       sonic_buf_put(lp->tda, lp->dma_bitmode,
+                     (entry * SIZEOF_SONIC_TD) + offset, val);
+}
+
+static inline __u16 sonic_tda_get(struct net_device* dev, int entry,
+                                 int offset)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       return sonic_buf_get(lp->tda, lp->dma_bitmode,
+                            (entry * SIZEOF_SONIC_TD) + offset);
+}
+
+static inline void sonic_rda_put(struct net_device* dev, int entry,
+                                int offset, __u16 val)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       sonic_buf_put(lp->rda, lp->dma_bitmode,
+                     (entry * SIZEOF_SONIC_RD) + offset, val);
+}
+
+static inline __u16 sonic_rda_get(struct net_device* dev, int entry,
+                                 int offset)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       return sonic_buf_get(lp->rda, lp->dma_bitmode,
+                            (entry * SIZEOF_SONIC_RD) + offset);
+}
+
+static inline void sonic_rra_put(struct net_device* dev, int entry,
+                                int offset, __u16 val)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       sonic_buf_put(lp->rra, lp->dma_bitmode,
+                     (entry * SIZEOF_SONIC_RR) + offset, val);
+}
+
+static inline __u16 sonic_rra_get(struct net_device* dev, int entry,
+                                 int offset)
+{
+       struct sonic_local *lp = netdev_priv(dev);
+       return sonic_buf_get(lp->rra, lp->dma_bitmode,
+                            (entry * SIZEOF_SONIC_RR) + offset);
+}
+
+static const char *version =
+    "sonic.c:v0.92 20.9.98 tsbogend@alpha.franken.de\n";
+
+#endif /* SONIC_H */
diff --git a/drivers/net/ethernet/natsemi/xtsonic.c b/drivers/net/ethernet/natsemi/xtsonic.c
new file mode 100644 (file)
index 0000000..9f12026
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * xtsonic.c
+ *
+ * (C) 2001 - 2007 Tensilica Inc.
+ *     Kevin Chea <kchea@yahoo.com>
+ *     Marc Gauthier <marc@linux-xtensa.org>
+ *     Chris Zankel <chris@zankel.net>
+ *
+ * (C) 1996,1998 by Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * This driver is based on work from Andreas Busse, but most of
+ * the code is rewritten.
+ *
+ * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
+ *
+ * A driver for the onboard Sonic ethernet controller on the XT2000.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/dma.h>
+
+static char xtsonic_string[] = "xtsonic";
+
+extern unsigned xtboard_nvram_valid(void);
+extern void xtboard_get_ether_addr(unsigned char *buf);
+
+#include "sonic.h"
+
+/*
+ * According to the documentation for the Sonic ethernet controller,
+ * EOBC should be 760 words (1520 bytes) for 32-bit applications, and,
+ * as such, 2 words less than the buffer size. The value for RBSIZE
+ * defined in sonic.h, however is only 1520.
+ *
+ * (Note that in 16-bit configurations, EOBC is 759 words (1518 bytes) and
+ * RBSIZE 1520 bytes)
+ */
+#undef SONIC_RBSIZE
+#define SONIC_RBSIZE   1524
+
+/*
+ * The chip provides 256 byte register space.
+ */
+#define SONIC_MEM_SIZE 0x100
+
+/*
+ * Macros to access SONIC registers
+ */
+#define SONIC_READ(reg) \
+       (0xffff & *((volatile unsigned int *)dev->base_addr+reg))
+
+#define SONIC_WRITE(reg,val) \
+       *((volatile unsigned int *)dev->base_addr+reg) = val
+
+
+/* Use 0 for production, 1 for verification, and >2 for debug */
+#ifdef SONIC_DEBUG
+static unsigned int sonic_debug = SONIC_DEBUG;
+#else
+static unsigned int sonic_debug = 1;
+#endif
+
+/*
+ * We cannot use station (ethernet) address prefixes to detect the
+ * sonic controller since these are board manufacturer depended.
+ * So we check for known Silicon Revision IDs instead.
+ */
+static unsigned short known_revisions[] =
+{
+       0x101,                  /* SONIC 83934 */
+       0xffff                  /* end of list */
+};
+
+static int xtsonic_open(struct net_device *dev)
+{
+       int retval;
+
+       retval = request_irq(dev->irq, sonic_interrupt, IRQF_DISABLED,
+                               "sonic", dev);
+       if (retval) {
+               printk(KERN_ERR "%s: unable to get IRQ %d.\n",
+                      dev->name, dev->irq);
+               return -EAGAIN;
+       }
+
+       retval = sonic_open(dev);
+       if (retval)
+               free_irq(dev->irq, dev);
+       return retval;
+}
+
+static int xtsonic_close(struct net_device *dev)
+{
+       int err;
+       err = sonic_close(dev);
+       free_irq(dev->irq, dev);
+       return err;
+}
+
+static const struct net_device_ops xtsonic_netdev_ops = {
+       .ndo_open               = xtsonic_open,
+       .ndo_stop               = xtsonic_close,
+       .ndo_start_xmit         = sonic_send_packet,
+       .ndo_get_stats          = sonic_get_stats,
+       .ndo_set_multicast_list = sonic_multicast_list,
+       .ndo_tx_timeout         = sonic_tx_timeout,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+};
+
+static int __init sonic_probe1(struct net_device *dev)
+{
+       static unsigned version_printed = 0;
+       unsigned int silicon_revision;
+       struct sonic_local *lp = netdev_priv(dev);
+       unsigned int base_addr = dev->base_addr;
+       int i;
+       int err = 0;
+
+       if (!request_mem_region(base_addr, 0x100, xtsonic_string))
+               return -EBUSY;
+
+       /*
+        * get the Silicon Revision ID. If this is one of the known
+        * one assume that we found a SONIC ethernet controller at
+        * the expected location.
+        */
+       silicon_revision = SONIC_READ(SONIC_SR);
+       if (sonic_debug > 1)
+               printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);
+
+       i = 0;
+       while ((known_revisions[i] != 0xffff) &&
+                       (known_revisions[i] != silicon_revision))
+               i++;
+
+       if (known_revisions[i] == 0xffff) {
+               printk("SONIC ethernet controller not found (0x%4x)\n",
+                               silicon_revision);
+               return -ENODEV;
+       }
+
+       if (sonic_debug  &&  version_printed++ == 0)
+               printk(version);
+
+       /*
+        * Put the sonic into software reset, then retrieve ethernet address.
+        * Note: we are assuming that the boot-loader has initialized the cam.
+        */
+       SONIC_WRITE(SONIC_CMD,SONIC_CR_RST);
+       SONIC_WRITE(SONIC_DCR,
+                   SONIC_DCR_WC0|SONIC_DCR_DW|SONIC_DCR_LBR|SONIC_DCR_SBUS);
+       SONIC_WRITE(SONIC_CEP,0);
+       SONIC_WRITE(SONIC_IMR,0);
+
+       SONIC_WRITE(SONIC_CMD,SONIC_CR_RST);
+       SONIC_WRITE(SONIC_CEP,0);
+
+       for (i=0; i<3; i++) {
+               unsigned int val = SONIC_READ(SONIC_CAP0-i);
+               dev->dev_addr[i*2] = val;
+               dev->dev_addr[i*2+1] = val >> 8;
+       }
+
+       /* Initialize the device structure. */
+
+       lp->dma_bitmode = SONIC_BITMODE32;
+
+       /*
+        *  Allocate local private descriptor areas in uncached space.
+        *  The entire structure must be located within the same 64kb segment.
+        *  A simple way to ensure this is to allocate twice the
+        *  size of the structure -- given that the structure is
+        *  much less than 64 kB, at least one of the halves of
+        *  the allocated area will be contained entirely in 64 kB.
+        *  We also allocate extra space for a pointer to allow freeing
+        *  this structure later on (in xtsonic_cleanup_module()).
+        */
+       lp->descriptors =
+               dma_alloc_coherent(lp->device,
+                       SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                       &lp->descriptors_laddr, GFP_KERNEL);
+
+       if (lp->descriptors == NULL) {
+               printk(KERN_ERR "%s: couldn't alloc DMA memory for "
+                               " descriptors.\n", dev_name(lp->device));
+               goto out;
+       }
+
+       lp->cda = lp->descriptors;
+       lp->tda = lp->cda + (SIZEOF_SONIC_CDA
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
+                            * SONIC_BUS_SCALE(lp->dma_bitmode));
+
+       /* get the virtual dma address */
+
+       lp->cda_laddr = lp->descriptors_laddr;
+       lp->tda_laddr = lp->cda_laddr + (SIZEOF_SONIC_CDA
+                                        * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rda_laddr = lp->tda_laddr + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
+                                        * SONIC_BUS_SCALE(lp->dma_bitmode));
+       lp->rra_laddr = lp->rda_laddr + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
+                                        * SONIC_BUS_SCALE(lp->dma_bitmode));
+
+       dev->netdev_ops         = &xtsonic_netdev_ops;
+       dev->watchdog_timeo     = TX_TIMEOUT;
+
+       /*
+        * clear tally counter
+        */
+       SONIC_WRITE(SONIC_CRCT,0xffff);
+       SONIC_WRITE(SONIC_FAET,0xffff);
+       SONIC_WRITE(SONIC_MPT,0xffff);
+
+       return 0;
+out:
+       release_region(dev->base_addr, SONIC_MEM_SIZE);
+       return err;
+}
+
+
+/*
+ * Probe for a SONIC ethernet controller on an XT2000 board.
+ * Actually probing is superfluous but we're paranoid.
+ */
+
+int __devinit xtsonic_probe(struct platform_device *pdev)
+{
+       struct net_device *dev;
+       struct sonic_local *lp;
+       struct resource *resmem, *resirq;
+       int err = 0;
+
+       if ((resmem = platform_get_resource(pdev, IORESOURCE_MEM, 0)) == NULL)
+               return -ENODEV;
+
+       if ((resirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0)) == NULL)
+               return -ENODEV;
+
+       if ((dev = alloc_etherdev(sizeof(struct sonic_local))) == NULL)
+               return -ENOMEM;
+
+       lp = netdev_priv(dev);
+       lp->device = &pdev->dev;
+       SET_NETDEV_DEV(dev, &pdev->dev);
+       netdev_boot_setup_check(dev);
+
+       dev->base_addr = resmem->start;
+       dev->irq = resirq->start;
+
+       if ((err = sonic_probe1(dev)))
+               goto out;
+       if ((err = register_netdev(dev)))
+               goto out1;
+
+       printk("%s: SONIC ethernet @%08lx, MAC %pM, IRQ %d\n", dev->name,
+              dev->base_addr, dev->dev_addr, dev->irq);
+
+       return 0;
+
+out1:
+       release_region(dev->base_addr, SONIC_MEM_SIZE);
+out:
+       free_netdev(dev);
+
+       return err;
+}
+
+MODULE_DESCRIPTION("Xtensa XT2000 SONIC ethernet driver");
+module_param(sonic_debug, int, 0);
+MODULE_PARM_DESC(sonic_debug, "xtsonic debug level (1-4)");
+
+#include "sonic.c"
+
+static int __devexit xtsonic_device_remove (struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct sonic_local *lp = netdev_priv(dev);
+
+       unregister_netdev(dev);
+       dma_free_coherent(lp->device,
+                         SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                         lp->descriptors, lp->descriptors_laddr);
+       release_region (dev->base_addr, SONIC_MEM_SIZE);
+       free_netdev(dev);
+
+       return 0;
+}
+
+static struct platform_driver xtsonic_driver = {
+       .probe = xtsonic_probe,
+       .remove = __devexit_p(xtsonic_device_remove),
+       .driver = {
+               .name = xtsonic_string,
+       },
+};
+
+static int __init xtsonic_init(void)
+{
+       return platform_driver_register(&xtsonic_driver);
+}
+
+static void __exit xtsonic_cleanup(void)
+{
+       platform_driver_unregister(&xtsonic_driver);
+}
+
+module_init(xtsonic_init);
+module_exit(xtsonic_cleanup);
diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c
deleted file mode 100644 (file)
index a7d6cad..0000000
+++ /dev/null
@@ -1,1075 +0,0 @@
-/*
-net-3-driver for the IBM LAN Adapter/A
-
-This is an extension to the Linux operating system, and is covered by the
-same GNU General Public License that covers that work.
-
-Copyright 1999 by Alfred Arnold (alfred@ccac.rwth-aachen.de,
-                                 alfred.arnold@lancom.de)
-
-This driver is based both on the SK_MCA driver, which is itself based on the
-SK_G16 and 3C523 driver.
-
-paper sources:
-  'PC Hardware: Aufbau, Funktionsweise, Programmierung' by
-  Hans-Peter Messmer for the basic Microchannel stuff
-
-  'Linux Geraetetreiber' by Allesandro Rubini, Kalle Dalheimer
-  for help on Ethernet driver programming
-
-  'DP83934CVUL-20/25 MHz SONIC-T Ethernet Controller Datasheet' by National
-  Semiconductor for info on the MAC chip
-
-  'LAN Technical Reference Ethernet Adapter Interface Version 1 Release 1.0
-   Document Number SC30-3661-00' by IBM for info on the adapter itself
-
-  Also see http://www.national.com/analog 
-
-special acknowledgements to:
-  - Bob Eager for helping me out with documentation from IBM
-  - Jim Shorney for his endless patience with me while I was using
-    him as a beta tester to trace down the address filter bug ;-)
-
-  Missing things:
-
-  -> set debug level via ioctl instead of compile-time switches
-  -> I didn't follow the development of the 2.1.x kernels, so my
-     assumptions about which things changed with which kernel version
-     are probably nonsense
-
-History:
-  Nov 6th, 1999
-       startup from SK_MCA driver
-  Dec 6th, 1999
-       finally got docs about the card.  A big thank you to Bob Eager!
-  Dec 12th, 1999
-       first packet received
-  Dec 13th, 1999
-       recv queue done, tcpdump works
-  Dec 15th, 1999
-       transmission part works
-  Dec 28th, 1999
-       added usage of the isa_functions for Linux 2.3 .  Things should
-       still work with 2.0.x....
-  Jan 28th, 2000
-       in Linux 2.2.13, the version.h file mysteriously didn't get
-       included.  Added a workaround for this.  Furthermore, it now
-       not only compiles as a modules ;-)
-  Jan 30th, 2000
-       newer kernels automatically probe more than one board, so the
-       'startslot' as a variable is also needed here
-  Apr 12th, 2000
-       the interrupt mask register is not set 'hard' instead of individually
-       setting registers, since this seems to set bits that shouldn't be
-       set
-  May 21st, 2000
-       reset interrupt status immediately after CAM load
-       add a recovery delay after releasing the chip's reset line
-  May 24th, 2000
-       finally found the bug in the address filter setup - damned signed
-        chars!
-  June 1st, 2000
-       corrected version codes, added support for the latest 2.3 changes
-  Oct 28th, 2002
-       cleaned up for the 2.5 tree <alan@lxorguk.ukuu.org.uk>
-
- *************************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/mca.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_ether.h>
-#include <linux/skbuff.h>
-#include <linux/bitops.h>
-
-#include <asm/processor.h>
-#include <asm/io.h>
-
-#define _IBM_LANA_DRIVER_
-#include "ibmlana.h"
-
-#undef DEBUG
-
-#define DRV_NAME "ibmlana"
-
-/* ------------------------------------------------------------------------
- * global static data - not more since we can handle multiple boards and
- * have to pack all state info into the device struct!
- * ------------------------------------------------------------------------ */
-
-static char *MediaNames[Media_Count] = {
-       "10BaseT", "10Base5", "Unknown", "10Base2"
-};
-
-/* ------------------------------------------------------------------------
- * private subfunctions
- * ------------------------------------------------------------------------ */
-
-#ifdef DEBUG
-  /* dump all registers */
-
-static void dumpregs(struct net_device *dev)
-{
-       int z;
-
-       for (z = 0; z < 160; z += 2) {
-               if (!(z & 15))
-                       printk("REGS: %04x:", z);
-               printk(" %04x", inw(dev->base_addr + z));
-               if ((z & 15) == 14)
-                       printk("\n");
-       }
-}
-
-/* dump parts of shared memory - only needed during debugging */
-
-static void dumpmem(struct net_device *dev, u32 start, u32 len)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-       int z;
-
-       printk("Address %04x:\n", start);
-       for (z = 0; z < len; z++) {
-               if ((z & 15) == 0)
-                       printk("%04x:", z);
-               printk(" %02x", readb(priv->base + start + z));
-               if ((z & 15) == 15)
-                       printk("\n");
-       }
-       if ((z & 15) != 0)
-               printk("\n");
-}
-
-/* print exact time - ditto */
-
-static void PrTime(void)
-{
-       struct timeval tv;
-
-       do_gettimeofday(&tv);
-       printk("%9d:%06d: ", (int) tv.tv_sec, (int) tv.tv_usec);
-}
-#endif                         /* DEBUG */
-
-/* deduce resources out of POS registers */
-
-static void getaddrs(struct mca_device *mdev, int *base, int *memlen,
-                    int *iobase, int *irq, ibmlana_medium *medium)
-{
-       u_char pos0, pos1;
-
-       pos0 = mca_device_read_stored_pos(mdev, 2);
-       pos1 = mca_device_read_stored_pos(mdev, 3);
-
-       *base = 0xc0000 + ((pos1 & 0xf0) << 9);
-       *memlen = (pos1 & 0x01) ? 0x8000 : 0x4000;
-       *iobase = (pos0 & 0xe0) << 7;
-       switch (pos0 & 0x06) {
-       case 0:
-               *irq = 5;
-               break;
-       case 2:
-               *irq = 15;
-               break;
-       case 4:
-               *irq = 10;
-               break;
-       case 6:
-               *irq = 11;
-               break;
-       }
-       *medium = (pos0 & 0x18) >> 3;
-}
-
-/* wait on register value with mask and timeout */
-
-static int wait_timeout(struct net_device *dev, int regoffs, u16 mask,
-                       u16 value, int timeout)
-{
-       unsigned long fin = jiffies + timeout;
-
-       while (time_before(jiffies,fin))
-               if ((inw(dev->base_addr + regoffs) & mask) == value)
-                       return 1;
-
-       return 0;
-}
-
-
-/* reset the whole board */
-
-static void ResetBoard(struct net_device *dev)
-{
-       unsigned char bcmval;
-
-       /* read original board control value */
-
-       bcmval = inb(dev->base_addr + BCMREG);
-
-       /* set reset bit for a while */
-
-       bcmval |= BCMREG_RESET;
-       outb(bcmval, dev->base_addr + BCMREG);
-       udelay(10);
-       bcmval &= ~BCMREG_RESET;
-       outb(bcmval, dev->base_addr + BCMREG);
-
-       /* switch over to RAM again */
-
-       bcmval |= BCMREG_RAMEN | BCMREG_RAMWIN;
-       outb(bcmval, dev->base_addr + BCMREG);
-}
-
-/* calculate RAM layout & set up descriptors in RAM */
-
-static void InitDscrs(struct net_device *dev)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-       u32 addr, baddr, raddr;
-       int z;
-       tda_t tda;
-       rda_t rda;
-       rra_t rra;
-
-       /* initialize RAM */
-
-       memset_io(priv->base, 0xaa,
-                     dev->mem_start - dev->mem_start); /* XXX: typo? */
-
-       /* setup n TX descriptors - independent of RAM size */
-
-       priv->tdastart = addr = 0;
-       priv->txbufstart = baddr = sizeof(tda_t) * TXBUFCNT;
-       for (z = 0; z < TXBUFCNT; z++) {
-               tda.status = 0;
-               tda.config = 0;
-               tda.length = 0;
-               tda.fragcount = 1;
-               tda.startlo = baddr;
-               tda.starthi = 0;
-               tda.fraglength = 0;
-               if (z == TXBUFCNT - 1)
-                       tda.link = priv->tdastart;
-               else
-                       tda.link = addr + sizeof(tda_t);
-               tda.link |= 1;
-               memcpy_toio(priv->base + addr, &tda, sizeof(tda_t));
-               addr += sizeof(tda_t);
-               baddr += PKTSIZE;
-       }
-
-       /* calculate how many receive buffers fit into remaining memory */
-
-       priv->rxbufcnt = (dev->mem_end - dev->mem_start - baddr) / (sizeof(rra_t) + sizeof(rda_t) + PKTSIZE);
-
-       /* calculate receive addresses */
-
-       priv->rrastart = raddr = priv->txbufstart + (TXBUFCNT * PKTSIZE);
-       priv->rdastart = addr = priv->rrastart + (priv->rxbufcnt * sizeof(rra_t));
-       priv->rxbufstart = baddr = priv->rdastart + (priv->rxbufcnt * sizeof(rda_t));
-
-       for (z = 0; z < priv->rxbufcnt; z++) {
-               rra.startlo = baddr;
-               rra.starthi = 0;
-               rra.cntlo = PKTSIZE >> 1;
-               rra.cnthi = 0;
-               memcpy_toio(priv->base + raddr, &rra, sizeof(rra_t));
-
-               rda.status = 0;
-               rda.length = 0;
-               rda.startlo = 0;
-               rda.starthi = 0;
-               rda.seqno = 0;
-               if (z < priv->rxbufcnt - 1)
-                       rda.link = addr + sizeof(rda_t);
-               else
-                       rda.link = 1;
-               rda.inuse = 1;
-               memcpy_toio(priv->base + addr, &rda, sizeof(rda_t));
-
-               baddr += PKTSIZE;
-               raddr += sizeof(rra_t);
-               addr += sizeof(rda_t);
-       }
-
-       /* initialize current pointers */
-
-       priv->nextrxdescr = 0;
-       priv->lastrxdescr = priv->rxbufcnt - 1;
-       priv->nexttxdescr = 0;
-       priv->currtxdescr = 0;
-       priv->txusedcnt = 0;
-       memset(priv->txused, 0, sizeof(priv->txused));
-}
-
-/* set up Rx + Tx descriptors in SONIC */
-
-static int InitSONIC(struct net_device *dev)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-
-       /* set up start & end of resource area */
-
-       outw(0, SONIC_URRA);
-       outw(priv->rrastart, dev->base_addr + SONIC_RSA);
-       outw(priv->rrastart + (priv->rxbufcnt * sizeof(rra_t)), dev->base_addr + SONIC_REA);
-       outw(priv->rrastart, dev->base_addr + SONIC_RRP);
-       outw(priv->rrastart, dev->base_addr + SONIC_RWP);
-
-       /* set EOBC so that only one packet goes into one buffer */
-
-       outw((PKTSIZE - 4) >> 1, dev->base_addr + SONIC_EOBC);
-
-       /* let SONIC read the first RRA descriptor */
-
-       outw(CMDREG_RRRA, dev->base_addr + SONIC_CMDREG);
-       if (!wait_timeout(dev, SONIC_CMDREG, CMDREG_RRRA, 0, 2)) {
-               printk(KERN_ERR "%s: SONIC did not respond on RRRA command - giving up.", dev->name);
-               return 0;
-       }
-
-       /* point SONIC to the first RDA */
-
-       outw(0, dev->base_addr + SONIC_URDA);
-       outw(priv->rdastart, dev->base_addr + SONIC_CRDA);
-
-       /* set upper half of TDA address */
-
-       outw(0, dev->base_addr + SONIC_UTDA);
-
-       return 1;
-}
-
-/* stop SONIC so we can reinitialize it */
-
-static void StopSONIC(struct net_device *dev)
-{
-       /* disable interrupts */
-
-       outb(inb(dev->base_addr + BCMREG) & (~BCMREG_IEN), dev->base_addr + BCMREG);
-       outb(0, dev->base_addr + SONIC_IMREG);
-
-       /* reset the SONIC */
-
-       outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
-       udelay(10);
-       outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
-}
-
-/* initialize card and SONIC for proper operation */
-
-static void putcam(camentry_t * cams, int *camcnt, char *addr)
-{
-       camentry_t *pcam = cams + (*camcnt);
-       u8 *uaddr = (u8 *) addr;
-
-       pcam->index = *camcnt;
-       pcam->addr0 = (((u16) uaddr[1]) << 8) | uaddr[0];
-       pcam->addr1 = (((u16) uaddr[3]) << 8) | uaddr[2];
-       pcam->addr2 = (((u16) uaddr[5]) << 8) | uaddr[4];
-       (*camcnt)++;
-}
-
-static void InitBoard(struct net_device *dev)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-       int camcnt;
-       camentry_t cams[16];
-       u32 cammask;
-       struct netdev_hw_addr *ha;
-       u16 rcrval;
-
-       /* reset the SONIC */
-
-       outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
-       udelay(10);
-
-       /* clear all spurious interrupts */
-
-       outw(inw(dev->base_addr + SONIC_ISREG), dev->base_addr + SONIC_ISREG);
-
-       /* set up the SONIC's bus interface - constant for this adapter -
-          must be done while the SONIC is in reset */
-
-       outw(DCREG_USR1 | DCREG_USR0 | DCREG_WC1 | DCREG_DW32, dev->base_addr + SONIC_DCREG);
-       outw(0, dev->base_addr + SONIC_DCREG2);
-
-       /* remove reset form the SONIC */
-
-       outw(0, dev->base_addr + SONIC_CMDREG);
-       udelay(10);
-
-       /* data sheet requires URRA to be programmed before setting up the CAM contents */
-
-       outw(0, dev->base_addr + SONIC_URRA);
-
-       /* program the CAM entry 0 to the device address */
-
-       camcnt = 0;
-       putcam(cams, &camcnt, dev->dev_addr);
-
-       /* start putting the multicast addresses into the CAM list.  Stop if
-          it is full. */
-
-       netdev_for_each_mc_addr(ha, dev) {
-               putcam(cams, &camcnt, ha->addr);
-               if (camcnt == 16)
-                       break;
-       }
-
-       /* calculate CAM mask */
-
-       cammask = (1 << camcnt) - 1;
-
-       /* feed CDA into SONIC, initialize RCR value (always get broadcasts) */
-
-       memcpy_toio(priv->base, cams, sizeof(camentry_t) * camcnt);
-       memcpy_toio(priv->base + (sizeof(camentry_t) * camcnt), &cammask, sizeof(cammask));
-
-#ifdef DEBUG
-       printk("CAM setup:\n");
-       dumpmem(dev, 0, sizeof(camentry_t) * camcnt + sizeof(cammask));
-#endif
-
-       outw(0, dev->base_addr + SONIC_CAMPTR);
-       outw(camcnt, dev->base_addr + SONIC_CAMCNT);
-       outw(CMDREG_LCAM, dev->base_addr + SONIC_CMDREG);
-       if (!wait_timeout(dev, SONIC_CMDREG, CMDREG_LCAM, 0, 2)) {
-               printk(KERN_ERR "%s:SONIC did not respond on LCAM command - giving up.", dev->name);
-               return;
-       } else {
-               /* clear interrupt condition */
-
-               outw(ISREG_LCD, dev->base_addr + SONIC_ISREG);
-
-#ifdef DEBUG
-               printk("Loading CAM done, address pointers %04x:%04x\n",
-                      inw(dev->base_addr + SONIC_URRA),
-                      inw(dev->base_addr + SONIC_CAMPTR));
-               {
-                       int z;
-
-                       printk("\n-->CAM: PTR %04x CNT %04x\n",
-                              inw(dev->base_addr + SONIC_CAMPTR),
-                              inw(dev->base_addr + SONIC_CAMCNT));
-                       outw(CMDREG_RST, dev->base_addr + SONIC_CMDREG);
-                       for (z = 0; z < camcnt; z++) {
-                               outw(z, dev->base_addr + SONIC_CAMEPTR);
-                               printk("Entry %d: %04x %04x %04x\n", z,
-                                      inw(dev->base_addr + SONIC_CAMADDR0),
-                                      inw(dev->base_addr + SONIC_CAMADDR1),
-                                      inw(dev->base_addr + SONIC_CAMADDR2));
-                       }
-                       outw(0, dev->base_addr + SONIC_CMDREG);
-               }
-#endif
-       }
-
-       rcrval = RCREG_BRD | RCREG_LB_NONE;
-
-       /* if still multicast addresses left or ALLMULTI is set, set the multicast
-          enable bit */
-
-       if ((dev->flags & IFF_ALLMULTI) || netdev_mc_count(dev) > camcnt)
-               rcrval |= RCREG_AMC;
-
-       /* promiscuous mode ? */
-
-       if (dev->flags & IFF_PROMISC)
-               rcrval |= RCREG_PRO;
-
-       /* program receive mode */
-
-       outw(rcrval, dev->base_addr + SONIC_RCREG);
-#ifdef DEBUG
-       printk("\nRCRVAL: %04x\n", rcrval);
-#endif
-
-       /* set up descriptors in shared memory + feed them into SONIC registers */
-
-       InitDscrs(dev);
-       if (!InitSONIC(dev))
-               return;
-
-       /* reset all pending interrupts */
-
-       outw(0xffff, dev->base_addr + SONIC_ISREG);
-
-       /* enable transmitter + receiver interrupts */
-
-       outw(CMDREG_RXEN, dev->base_addr + SONIC_CMDREG);
-       outw(IMREG_PRXEN | IMREG_RBEEN | IMREG_PTXEN | IMREG_TXEREN, dev->base_addr + SONIC_IMREG);
-
-       /* turn on card interrupts */
-
-       outb(inb(dev->base_addr + BCMREG) | BCMREG_IEN, dev->base_addr + BCMREG);
-
-#ifdef DEBUG
-       printk("Register dump after initialization:\n");
-       dumpregs(dev);
-#endif
-}
-
-/* start transmission of a descriptor */
-
-static void StartTx(struct net_device *dev, int descr)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-       int addr;
-
-       addr = priv->tdastart + (descr * sizeof(tda_t));
-
-       /* put descriptor address into SONIC */
-
-       outw(addr, dev->base_addr + SONIC_CTDA);
-
-       /* trigger transmitter */
-
-       priv->currtxdescr = descr;
-       outw(CMDREG_TXP, dev->base_addr + SONIC_CMDREG);
-}
-
-/* ------------------------------------------------------------------------
- * interrupt handler(s)
- * ------------------------------------------------------------------------ */
-
-/* receive buffer area exhausted */
-
-static void irqrbe_handler(struct net_device *dev)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-
-       /* point the SONIC back to the RRA start */
-
-       outw(priv->rrastart, dev->base_addr + SONIC_RRP);
-       outw(priv->rrastart, dev->base_addr + SONIC_RWP);
-}
-
-/* receive interrupt */
-
-static void irqrx_handler(struct net_device *dev)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-       rda_t rda;
-       u32 rdaaddr, lrdaaddr;
-
-       /* loop until ... */
-
-       while (1) {
-               /* read descriptor that was next to be filled by SONIC */
-
-               rdaaddr = priv->rdastart + (priv->nextrxdescr * sizeof(rda_t));
-               lrdaaddr = priv->rdastart + (priv->lastrxdescr * sizeof(rda_t));
-               memcpy_fromio(&rda, priv->base + rdaaddr, sizeof(rda_t));
-
-               /* iron out upper word halves of fields we use - SONIC will duplicate
-                  bits 0..15 to 16..31 */
-
-               rda.status &= 0xffff;
-               rda.length &= 0xffff;
-               rda.startlo &= 0xffff;
-
-               /* stop if the SONIC still owns it, i.e. there is no data for us */
-
-               if (rda.inuse)
-                       break;
-
-               /* good packet? */
-
-               else if (rda.status & RCREG_PRX) {
-                       struct sk_buff *skb;
-
-                       /* fetch buffer */
-
-                       skb = dev_alloc_skb(rda.length + 2);
-                       if (skb == NULL)
-                               dev->stats.rx_dropped++;
-                       else {
-                               /* copy out data */
-
-                               memcpy_fromio(skb_put(skb, rda.length),
-                                              priv->base +
-                                              rda.startlo, rda.length);
-
-                               /* set up skb fields */
-
-                               skb->protocol = eth_type_trans(skb, dev);
-                               skb_checksum_none_assert(skb);
-
-                               /* bookkeeping */
-                               dev->stats.rx_packets++;
-                               dev->stats.rx_bytes += rda.length;
-
-                               /* pass to the upper layers */
-                               netif_rx(skb);
-                       }
-               }
-
-               /* otherwise check error status bits and increase statistics */
-
-               else {
-                       dev->stats.rx_errors++;
-                       if (rda.status & RCREG_FAER)
-                               dev->stats.rx_frame_errors++;
-                       if (rda.status & RCREG_CRCR)
-                               dev->stats.rx_crc_errors++;
-               }
-
-               /* descriptor processed, will become new last descriptor in queue */
-
-               rda.link = 1;
-               rda.inuse = 1;
-               memcpy_toio(priv->base + rdaaddr, &rda,
-                            sizeof(rda_t));
-
-               /* set up link and EOL = 0 in currently last descriptor. Only write
-                  the link field since the SONIC may currently already access the
-                  other fields. */
-
-               memcpy_toio(priv->base + lrdaaddr + 20, &rdaaddr, 4);
-
-               /* advance indices */
-
-               priv->lastrxdescr = priv->nextrxdescr;
-               if ((++priv->nextrxdescr) >= priv->rxbufcnt)
-                       priv->nextrxdescr = 0;
-       }
-}
-
-/* transmit interrupt */
-
-static void irqtx_handler(struct net_device *dev)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-       tda_t tda;
-
-       /* fetch descriptor (we forgot the size ;-) */
-       memcpy_fromio(&tda, priv->base + priv->tdastart + (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t));
-
-       /* update statistics */
-       dev->stats.tx_packets++;
-       dev->stats.tx_bytes += tda.length;
-
-       /* update our pointers */
-       priv->txused[priv->currtxdescr] = 0;
-       priv->txusedcnt--;
-
-       /* if there are more descriptors present in RAM, start them */
-       if (priv->txusedcnt > 0)
-               StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT);
-
-       /* tell the upper layer we can go on transmitting */
-       netif_wake_queue(dev);
-}
-
-static void irqtxerr_handler(struct net_device *dev)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-       tda_t tda;
-
-       /* fetch descriptor to check status */
-       memcpy_fromio(&tda, priv->base + priv->tdastart + (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t));
-
-       /* update statistics */
-       dev->stats.tx_errors++;
-       if (tda.status & (TCREG_NCRS | TCREG_CRSL))
-               dev->stats.tx_carrier_errors++;
-       if (tda.status & TCREG_EXC)
-               dev->stats.tx_aborted_errors++;
-       if (tda.status & TCREG_OWC)
-               dev->stats.tx_window_errors++;
-       if (tda.status & TCREG_FU)
-               dev->stats.tx_fifo_errors++;
-
-       /* update our pointers */
-       priv->txused[priv->currtxdescr] = 0;
-       priv->txusedcnt--;
-
-       /* if there are more descriptors present in RAM, start them */
-       if (priv->txusedcnt > 0)
-               StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT);
-
-       /* tell the upper layer we can go on transmitting */
-       netif_wake_queue(dev);
-}
-
-/* general interrupt entry */
-
-static irqreturn_t irq_handler(int dummy, void *device)
-{
-       struct net_device *dev = device;
-       u16 ival;
-
-       /* in case we're not meant... */
-       if (!(inb(dev->base_addr + BCMREG) & BCMREG_IPEND))
-               return IRQ_NONE;
-
-       /* loop through the interrupt bits until everything is clear */
-       while (1) {
-               ival = inw(dev->base_addr + SONIC_ISREG);
-
-               if (ival & ISREG_RBE) {
-                       irqrbe_handler(dev);
-                       outw(ISREG_RBE, dev->base_addr + SONIC_ISREG);
-               }
-               if (ival & ISREG_PKTRX) {
-                       irqrx_handler(dev);
-                       outw(ISREG_PKTRX, dev->base_addr + SONIC_ISREG);
-               }
-               if (ival & ISREG_TXDN) {
-                       irqtx_handler(dev);
-                       outw(ISREG_TXDN, dev->base_addr + SONIC_ISREG);
-               }
-               if (ival & ISREG_TXER) {
-                       irqtxerr_handler(dev);
-                       outw(ISREG_TXER, dev->base_addr + SONIC_ISREG);
-               }
-               break;
-       }
-       return IRQ_HANDLED;
-}
-
-/* ------------------------------------------------------------------------
- * driver methods
- * ------------------------------------------------------------------------ */
-
-/* MCA info */
-
-#if 0 /* info available elsewhere, but this is kept for reference */
-static int ibmlana_getinfo(char *buf, int slot, void *d)
-{
-       int len = 0, i;
-       struct net_device *dev = (struct net_device *) d;
-       ibmlana_priv *priv;
-
-       /* can't say anything about an uninitialized device... */
-
-       if (dev == NULL)
-               return len;
-       priv = netdev_priv(dev);
-
-       /* print info */
-
-       len += sprintf(buf + len, "IRQ: %d\n", priv->realirq);
-       len += sprintf(buf + len, "I/O: %#lx\n", dev->base_addr);
-       len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start, dev->mem_end - 1);
-       len += sprintf(buf + len, "Transceiver: %s\n", MediaNames[priv->medium]);
-       len += sprintf(buf + len, "Device: %s\n", dev->name);
-       len += sprintf(buf + len, "MAC address:");
-       for (i = 0; i < 6; i++)
-               len += sprintf(buf + len, " %02x", dev->dev_addr[i]);
-       buf[len++] = '\n';
-       buf[len] = 0;
-
-       return len;
-}
-#endif
-
-/* open driver.  Means also initialization and start of LANCE */
-
-static int ibmlana_open(struct net_device *dev)
-{
-       int result;
-       ibmlana_priv *priv = netdev_priv(dev);
-
-       /* register resources - only necessary for IRQ */
-
-       result = request_irq(priv->realirq, irq_handler, IRQF_SHARED,
-                            dev->name, dev);
-       if (result != 0) {
-               printk(KERN_ERR "%s: failed to register irq %d\n", dev->name, dev->irq);
-               return result;
-       }
-       dev->irq = priv->realirq;
-
-       /* set up the card and SONIC */
-       InitBoard(dev);
-
-       /* initialize operational flags */
-       netif_start_queue(dev);
-       return 0;
-}
-
-/* close driver.  Shut down board and free allocated resources */
-
-static int ibmlana_close(struct net_device *dev)
-{
-       /* turn off board */
-
-       /* release resources */
-       if (dev->irq != 0)
-               free_irq(dev->irq, dev);
-       dev->irq = 0;
-       return 0;
-}
-
-/* transmit a block. */
-
-static netdev_tx_t ibmlana_tx(struct sk_buff *skb, struct net_device *dev)
-{
-       ibmlana_priv *priv = netdev_priv(dev);
-       int tmplen, addr;
-       unsigned long flags;
-       tda_t tda;
-       int baddr;
-
-       /* find out if there are free slots for a frame to transmit. If not,
-          the upper layer is in deep desperation and we simply ignore the frame. */
-
-       if (priv->txusedcnt >= TXBUFCNT) {
-               dev->stats.tx_dropped++;
-               goto tx_done;
-       }
-
-       /* copy the frame data into the next free transmit buffer - fillup missing */
-       tmplen = skb->len;
-       if (tmplen < 60)
-               tmplen = 60;
-       baddr = priv->txbufstart + (priv->nexttxdescr * PKTSIZE);
-       memcpy_toio(priv->base + baddr, skb->data, skb->len);
-
-       /* copy filler into RAM - in case we're filling up...
-          we're filling a bit more than necessary, but that doesn't harm
-          since the buffer is far larger...
-          Sorry Linus for the filler string but I couldn't resist ;-) */
-
-       if (tmplen > skb->len) {
-               char *fill = "NetBSD is a nice OS too! ";
-               unsigned int destoffs = skb->len, l = strlen(fill);
-
-               while (destoffs < tmplen) {
-                       memcpy_toio(priv->base + baddr + destoffs, fill, l);
-                       destoffs += l;
-               }
-       }
-
-       /* set up the new frame descriptor */
-       addr = priv->tdastart + (priv->nexttxdescr * sizeof(tda_t));
-       memcpy_fromio(&tda, priv->base + addr, sizeof(tda_t));
-       tda.length = tda.fraglength = tmplen;
-       memcpy_toio(priv->base + addr, &tda, sizeof(tda_t));
-
-       /* if there were no active descriptors, trigger the SONIC */
-       spin_lock_irqsave(&priv->lock, flags);
-
-       priv->txusedcnt++;
-       priv->txused[priv->nexttxdescr] = 1;
-
-       /* are all transmission slots used up ? */
-       if (priv->txusedcnt >= TXBUFCNT)
-               netif_stop_queue(dev);
-
-       if (priv->txusedcnt == 1)
-               StartTx(dev, priv->nexttxdescr);
-       priv->nexttxdescr = (priv->nexttxdescr + 1) % TXBUFCNT;
-
-       spin_unlock_irqrestore(&priv->lock, flags);
-tx_done:
-       dev_kfree_skb(skb);
-       return NETDEV_TX_OK;
-}
-
-/* switch receiver mode. */
-
-static void ibmlana_set_multicast_list(struct net_device *dev)
-{
-       /* first stop the SONIC... */
-       StopSONIC(dev);
-       /* ...then reinit it with the new flags */
-       InitBoard(dev);
-}
-
-/* ------------------------------------------------------------------------
- * hardware check
- * ------------------------------------------------------------------------ */
-
-static int ibmlana_irq;
-static int ibmlana_io;
-static int startslot;          /* counts through slots when probing multiple devices */
-
-static short ibmlana_adapter_ids[] __initdata = {
-       IBM_LANA_ID,
-       0x0000
-};
-
-static char *ibmlana_adapter_names[] __devinitdata = {
-       "IBM LAN Adapter/A",
-       NULL
-};
-
-
-static const struct net_device_ops ibmlana_netdev_ops = {
-       .ndo_open               = ibmlana_open,
-       .ndo_stop               = ibmlana_close,
-       .ndo_start_xmit         = ibmlana_tx,
-       .ndo_set_multicast_list = ibmlana_set_multicast_list,
-       .ndo_change_mtu         = eth_change_mtu,
-       .ndo_set_mac_address    = eth_mac_addr,
-       .ndo_validate_addr      = eth_validate_addr,
-};
-
-static int __devinit ibmlana_init_one(struct device *kdev)
-{
-       struct mca_device *mdev = to_mca_device(kdev);
-       struct net_device *dev;
-       int slot = mdev->slot, z, rc;
-       int base = 0, irq = 0, iobase = 0, memlen = 0;
-       ibmlana_priv *priv;
-       ibmlana_medium medium;
-
-       dev = alloc_etherdev(sizeof(ibmlana_priv));
-       if (!dev)
-               return -ENOMEM;
-
-       dev->irq = ibmlana_irq;
-       dev->base_addr = ibmlana_io;
-
-       base = dev->mem_start;
-       irq = dev->irq;
-
-       /* deduce card addresses */
-       getaddrs(mdev, &base, &memlen, &iobase, &irq, &medium);
-
-       /* were we looking for something different ? */
-       if (dev->irq && dev->irq != irq) {
-               rc = -ENODEV;
-               goto err_out;
-       }
-       if (dev->mem_start && dev->mem_start != base) {
-               rc = -ENODEV;
-               goto err_out;
-       }
-
-       /* announce success */
-       printk(KERN_INFO "%s: IBM LAN Adapter/A found in slot %d\n", dev->name, slot + 1);
-
-       /* try to obtain I/O range */
-       if (!request_region(iobase, IBM_LANA_IORANGE, DRV_NAME)) {
-               printk(KERN_ERR "%s: cannot allocate I/O range at %#x!\n", DRV_NAME, iobase);
-               startslot = slot + 1;
-               rc = -EBUSY;
-               goto err_out;
-       }
-
-       priv = netdev_priv(dev);
-       priv->slot = slot;
-       priv->realirq = mca_device_transform_irq(mdev, irq);
-       priv->medium = medium;
-       spin_lock_init(&priv->lock);
-
-       /* set base + irq for this device (irq not allocated so far) */
-
-       dev->irq = 0;
-       dev->mem_start = base;
-       dev->mem_end = base + memlen;
-       dev->base_addr = iobase;
-
-       priv->base = ioremap(base, memlen);
-       if (!priv->base) {
-               printk(KERN_ERR "%s: cannot remap memory!\n", DRV_NAME);
-               startslot = slot + 1;
-               rc = -EBUSY;
-               goto err_out_reg;
-       }
-
-       mca_device_set_name(mdev, ibmlana_adapter_names[mdev->index]);
-       mca_device_set_claim(mdev, 1);
-
-       /* set methods */
-       dev->netdev_ops = &ibmlana_netdev_ops;
-       dev->flags |= IFF_MULTICAST;
-
-       /* copy out MAC address */
-
-       for (z = 0; z < ETH_ALEN; z++)
-               dev->dev_addr[z] = inb(dev->base_addr + MACADDRPROM + z);
-
-       /* print config */
-
-       printk(KERN_INFO "%s: IRQ %d, I/O %#lx, memory %#lx-%#lx, "
-              "MAC address %pM.\n",
-              dev->name, priv->realirq, dev->base_addr,
-              dev->mem_start, dev->mem_end - 1,
-              dev->dev_addr);
-       printk(KERN_INFO "%s: %s medium\n", dev->name, MediaNames[priv->medium]);
-
-       /* reset board */
-
-       ResetBoard(dev);
-
-       /* next probe will start at next slot */
-
-       startslot = slot + 1;
-
-       rc = register_netdev(dev);
-       if (rc)
-               goto err_out_claimed;
-
-       dev_set_drvdata(kdev, dev);
-       return 0;
-
-err_out_claimed:
-       mca_device_set_claim(mdev, 0);
-       iounmap(priv->base);
-err_out_reg:
-       release_region(iobase, IBM_LANA_IORANGE);
-err_out:
-       free_netdev(dev);
-       return rc;
-}
-
-static int ibmlana_remove_one(struct device *kdev)
-{
-       struct mca_device *mdev = to_mca_device(kdev);
-       struct net_device *dev = dev_get_drvdata(kdev);
-       ibmlana_priv *priv = netdev_priv(dev);
-
-       unregister_netdev(dev);
-       /*DeinitBoard(dev); */
-       release_region(dev->base_addr, IBM_LANA_IORANGE);
-       mca_device_set_claim(mdev, 0);
-       iounmap(priv->base);
-       free_netdev(dev);
-       return 0;
-}
-
-/* ------------------------------------------------------------------------
- * modularization support
- * ------------------------------------------------------------------------ */
-
-module_param_named(irq, ibmlana_irq, int, 0);
-module_param_named(io, ibmlana_io, int, 0);
-MODULE_PARM_DESC(irq, "IBM LAN/A IRQ number");
-MODULE_PARM_DESC(io, "IBM LAN/A I/O base address");
-MODULE_LICENSE("GPL");
-
-static struct mca_driver ibmlana_driver = {
-       .id_table = ibmlana_adapter_ids,
-       .driver = {
-               .name   = "ibmlana",
-               .bus    = &mca_bus_type,
-               .probe  = ibmlana_init_one,
-               .remove = ibmlana_remove_one,
-       },
-};
-
-static int __init ibmlana_init_module(void)
-{
-       return mca_register_driver(&ibmlana_driver);
-}
-
-static void __exit ibmlana_cleanup_module(void)
-{
-       mca_unregister_driver(&ibmlana_driver);
-}
-
-module_init(ibmlana_init_module);
-module_exit(ibmlana_cleanup_module);
diff --git a/drivers/net/ibmlana.h b/drivers/net/ibmlana.h
deleted file mode 100644 (file)
index accd5ef..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-#ifndef _IBM_LANA_INCLUDE_
-#define _IBM_LANA_INCLUDE_
-
-#ifdef _IBM_LANA_DRIVER_
-
-/* maximum packet size */
-
-#define PKTSIZE 1524
-
-/* number of transmit buffers */
-
-#define TXBUFCNT 4
-
-/* Adapter ID's */
-#define IBM_LANA_ID 0xffe0
-
-/* media enumeration - defined in a way that it fits onto the LAN/A's
-   POS registers... */
-
-typedef enum {
-       Media_10BaseT, Media_10Base5,
-       Media_Unknown, Media_10Base2, Media_Count
-} ibmlana_medium;
-
-/* private structure */
-
-typedef struct {
-       unsigned int slot;              /* MCA-Slot-#                       */
-       int realirq;                    /* memorizes actual IRQ, even when
-                                          currently not allocated          */
-       ibmlana_medium medium;          /* physical cannector               */
-       u32     tdastart, txbufstart,   /* addresses                        */
-               rrastart, rxbufstart, rdastart, rxbufcnt, txusedcnt;
-       int     nextrxdescr,            /* next rx descriptor to be used    */
-               lastrxdescr,            /* last free rx descriptor          */
-               nexttxdescr,            /* last tx descriptor to be used    */
-               currtxdescr,            /* tx descriptor currently tx'ed    */
-               txused[TXBUFCNT];       /* busy flags                       */
-       void __iomem *base;
-       spinlock_t lock;
-} ibmlana_priv;
-
-/* this card uses quite a lot of I/O ports...luckily the MCA bus decodes
-   a full 64K I/O range... */
-
-#define IBM_LANA_IORANGE 0xa0
-
-/* Command Register: */
-
-#define SONIC_CMDREG     0x00
-#define CMDREG_HTX       0x0001        /* halt transmission                */
-#define CMDREG_TXP       0x0002        /* start transmission               */
-#define CMDREG_RXDIS     0x0004        /* disable receiver                 */
-#define CMDREG_RXEN      0x0008        /* enable receiver                  */
-#define CMDREG_STP       0x0010        /* stop timer                       */
-#define CMDREG_ST        0x0020        /* start timer                      */
-#define CMDREG_RST       0x0080        /* software reset                   */
-#define CMDREG_RRRA      0x0100        /* force SONIC to read first RRA    */
-#define CMDREG_LCAM      0x0200        /* force SONIC to read CAM descrs   */
-
-/* Data Configuration Register */
-
-#define SONIC_DCREG      0x02
-#define DCREG_EXBUS      0x8000        /* Extended Bus Mode                */
-#define DCREG_LBR        0x2000        /* Latched Bus Retry                */
-#define DCREG_PO1        0x1000        /* Programmable Outputs             */
-#define DCREG_PO0        0x0800
-#define DCREG_SBUS       0x0400        /* Synchronous Bus Mode             */
-#define DCREG_USR1       0x0200        /* User Definable Pins              */
-#define DCREG_USR0       0x0100
-#define DCREG_WC0        0x0000        /* 0..3 Wait States                 */
-#define DCREG_WC1        0x0040
-#define DCREG_WC2        0x0080
-#define DCREG_WC3        0x00c0
-#define DCREG_DW16       0x0000        /* 16 bit Bus Mode                  */
-#define DCREG_DW32       0x0020        /* 32 bit Bus Mode                  */
-#define DCREG_BMS        0x0010        /* Block Mode Select                */
-#define DCREG_RFT4       0x0000        /* 4/8/16/24 bytes RX  Threshold    */
-#define DCREG_RFT8       0x0004
-#define DCREG_RFT16      0x0008
-#define DCREG_RFT24      0x000c
-#define DCREG_TFT8       0x0000        /* 8/16/24/28 bytes TX Threshold    */
-#define DCREG_TFT16      0x0001
-#define DCREG_TFT24      0x0002
-#define DCREG_TFT28      0x0003
-
-/* Receive Control Register */
-
-#define SONIC_RCREG      0x04
-#define RCREG_ERR        0x8000        /* accept damaged and collided pkts */
-#define RCREG_RNT        0x4000        /* accept packets that are < 64     */
-#define RCREG_BRD        0x2000        /* accept broadcasts                */
-#define RCREG_PRO        0x1000        /* promiscuous mode                  */
-#define RCREG_AMC        0x0800        /* accept all multicasts            */
-#define RCREG_LB_NONE    0x0000        /* no loopback                      */
-#define RCREG_LB_MAC     0x0200        /* MAC loopback                     */
-#define RCREG_LB_ENDEC   0x0400        /* ENDEC loopback                   */
-#define RCREG_LB_XVR     0x0600        /* Transceiver loopback             */
-#define RCREG_MC         0x0100        /* Multicast received               */
-#define RCREG_BC         0x0080        /* Broadcast received               */
-#define RCREG_LPKT       0x0040        /* last packet in RBA               */
-#define RCREG_CRS        0x0020        /* carrier sense present            */
-#define RCREG_COL        0x0010        /* recv'd packet with collision     */
-#define RCREG_CRCR       0x0008        /* recv'd packet with CRC error     */
-#define RCREG_FAER       0x0004        /* recv'd packet with inv. framing  */
-#define RCREG_LBK        0x0002        /* recv'd loopback packet           */
-#define RCREG_PRX        0x0001        /* recv'd packet is OK              */
-
-/* Transmit Control Register */
-
-#define SONIC_TCREG      0x06
-#define TCREG_PINT       0x8000        /* generate interrupt after TDA read */
-#define TCREG_POWC       0x4000        /* timer start out of window detect */
-#define TCREG_CRCI       0x2000        /* inhibit CRC generation           */
-#define TCREG_EXDIS      0x1000        /* disable excessive deferral timer */
-#define TCREG_EXD        0x0400        /* excessive deferral occurred       */
-#define TCREG_DEF        0x0200        /* single deferral occurred          */
-#define TCREG_NCRS       0x0100        /* no carrier detected              */
-#define TCREG_CRSL       0x0080        /* carrier lost                     */
-#define TCREG_EXC        0x0040        /* excessive collisions occurred     */
-#define TCREG_OWC        0x0020        /* out of window collision occurred  */
-#define TCREG_PMB        0x0008        /* packet monitored bad             */
-#define TCREG_FU         0x0004        /* FIFO underrun                    */
-#define TCREG_BCM        0x0002        /* byte count mismatch of fragments */
-#define TCREG_PTX        0x0001        /* packet transmitted OK            */
-
-/* Interrupt Mask Register */
-
-#define SONIC_IMREG      0x08
-#define IMREG_BREN       0x4000        /* interrupt when bus retry occurred */
-#define IMREG_HBLEN      0x2000        /* interrupt when heartbeat lost    */
-#define IMREG_LCDEN      0x1000        /* interrupt when CAM loaded        */
-#define IMREG_PINTEN     0x0800        /* interrupt when PINT in TDA set   */
-#define IMREG_PRXEN      0x0400        /* interrupt when packet received   */
-#define IMREG_PTXEN      0x0200        /* interrupt when packet was sent   */
-#define IMREG_TXEREN     0x0100        /* interrupt when send failed       */
-#define IMREG_TCEN       0x0080        /* interrupt when timer completed   */
-#define IMREG_RDEEN      0x0040        /* interrupt when RDA exhausted     */
-#define IMREG_RBEEN      0x0020        /* interrupt when RBA exhausted     */
-#define IMREG_RBAEEN     0x0010        /* interrupt when RBA too short     */
-#define IMREG_CRCEN      0x0008        /* interrupt when CRC counter rolls */
-#define IMREG_FAEEN      0x0004        /* interrupt when FAE counter rolls */
-#define IMREG_MPEN       0x0002        /* interrupt when MP counter rolls  */
-#define IMREG_RFOEN      0x0001        /* interrupt when Rx FIFO overflows */
-
-/* Interrupt Status Register */
-
-#define SONIC_ISREG      0x0a
-#define ISREG_BR         0x4000        /* bus retry occurred                */
-#define ISREG_HBL        0x2000        /* heartbeat lost                   */
-#define ISREG_LCD        0x1000        /* CAM loaded                       */
-#define ISREG_PINT       0x0800        /* PINT in TDA set                  */
-#define ISREG_PKTRX      0x0400        /* packet received                  */
-#define ISREG_TXDN       0x0200        /* packet was sent                  */
-#define ISREG_TXER       0x0100        /* send failed                      */
-#define ISREG_TC         0x0080        /* timer completed                  */
-#define ISREG_RDE        0x0040        /* RDA exhausted                    */
-#define ISREG_RBE        0x0020        /* RBA exhausted                    */
-#define ISREG_RBAE       0x0010        /* RBA too short for received frame */
-#define ISREG_CRC        0x0008        /* CRC counter rolls over           */
-#define ISREG_FAE        0x0004        /* FAE counter rolls over           */
-#define ISREG_MP         0x0002        /* MP counter rolls  over           */
-#define ISREG_RFO        0x0001        /* Rx FIFO overflows                */
-
-#define SONIC_UTDA       0x0c  /* current transmit descr address   */
-#define SONIC_CTDA       0x0e
-
-#define SONIC_URDA       0x1a  /* current receive descr address    */
-#define SONIC_CRDA       0x1c
-
-#define SONIC_CRBA0      0x1e  /* current receive buffer address   */
-#define SONIC_CRBA1      0x20
-
-#define SONIC_RBWC0      0x22  /* word count in receive buffer     */
-#define SONIC_RBWC1      0x24
-
-#define SONIC_EOBC       0x26  /* minimum space to be free in RBA  */
-
-#define SONIC_URRA       0x28  /* upper address of CDA & Recv Area */
-
-#define SONIC_RSA        0x2a  /* start of receive resource area   */
-
-#define SONIC_REA        0x2c  /* end of receive resource area     */
-
-#define SONIC_RRP        0x2e  /* resource read pointer            */
-
-#define SONIC_RWP        0x30  /* resource write pointer           */
-
-#define SONIC_CAMEPTR    0x42  /* CAM entry pointer                */
-
-#define SONIC_CAMADDR2   0x44  /* CAM address ports                */
-#define SONIC_CAMADDR1   0x46
-#define SONIC_CAMADDR0   0x48
-
-#define SONIC_CAMPTR     0x4c  /* lower address of CDA             */
-
-#define SONIC_CAMCNT     0x4e  /* # of CAM descriptors to load     */
-
-/* Data Configuration Register 2    */
-
-#define SONIC_DCREG2     0x7e
-#define DCREG2_EXPO3     0x8000        /* extended programmable outputs    */
-#define DCREG2_EXPO2     0x4000
-#define DCREG2_EXPO1     0x2000
-#define DCREG2_EXPO0     0x1000
-#define DCREG2_HD        0x0800        /* heartbeat disable                */
-#define DCREG2_JD        0x0200        /* jabber timer disable             */
-#define DCREG2_AUTO      0x0100        /* enable AUI/TP auto selection     */
-#define DCREG2_XWRAP     0x0040        /* TP transceiver loopback          */
-#define DCREG2_PH        0x0010        /* HOLD request timing              */
-#define DCREG2_PCM       0x0004        /* packet compress when matched     */
-#define DCREG2_PCNM      0x0002        /* packet compress when not matched */
-#define DCREG2_RJCM      0x0001        /* inverse packet match via CAM     */
-
-/* Board Control Register: Enable RAM, Interrupts... */
-
-#define BCMREG           0x80
-#define BCMREG_RAMEN     0x80  /* switch over to RAM               */
-#define BCMREG_IPEND     0x40  /* interrupt pending ?              */
-#define BCMREG_RESET     0x08  /* reset board                      */
-#define BCMREG_16BIT     0x04  /* adapter in 16-bit slot           */
-#define BCMREG_RAMWIN    0x02  /* enable RAM window                */
-#define BCMREG_IEN       0x01  /* interrupt enable                 */
-
-/* MAC Address PROM */
-
-#define MACADDRPROM      0x92
-
-/* structure of a CAM entry */
-
-typedef struct {
-       u32 index;              /* pointer into CAM area            */
-       u32 addr0;              /* address part (bits 0..15 used)   */
-       u32 addr1;
-       u32 addr2;
-} camentry_t;
-
-/* structure of a receive resource */
-
-typedef struct {
-       u32 startlo;            /* start address (bits 0..15 used)  */
-       u32 starthi;
-       u32 cntlo;              /* size in 16-bit quantities        */
-       u32 cnthi;
-} rra_t;
-
-/* structure of a receive descriptor */
-
-typedef struct {
-       u32 status;             /* packet status                    */
-       u32 length;             /* length in bytes                  */
-       u32 startlo;            /* start address                    */
-       u32 starthi;
-       u32 seqno;              /* frame sequence                   */
-       u32 link;               /* pointer to next descriptor       */
-       /* bit 0 = EOL                      */
-       u32 inuse;              /* !=0 --> free for SONIC to write  */
-} rda_t;
-
-/* structure of a transmit descriptor */
-
-typedef struct {
-       u32 status;             /* transmit status                  */
-       u32 config;             /* value for TCR                    */
-       u32 length;             /* total length                     */
-       u32 fragcount;          /* number of fragments              */
-       u32 startlo;            /* start address of fragment        */
-       u32 starthi;
-       u32 fraglength;         /* length of this fragment          */
-       /* more address/length triplets may */
-       /* follow here                      */
-       u32 link;               /* pointer to next descriptor       */
-       /* bit 0 = EOL                      */
-} tda_t;
-
-#endif                         /* _IBM_LANA_DRIVER_ */
-
-#endif /* _IBM_LANA_INCLUDE_ */
diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c
deleted file mode 100644 (file)
index 949c1f9..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * jazzsonic.c
- *
- * (C) 2005 Finn Thain
- *
- * Converted to DMA API, and (from the mac68k project) introduced
- * dhd's support for 16-bit cards.
- *
- * (C) 1996,1998 by Thomas Bogendoerfer (tsbogend@alpha.franken.de)
- *
- * This driver is based on work from Andreas Busse, but most of
- * the code is rewritten.
- *
- * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
- *
- * A driver for the onboard Sonic ethernet controller on Mips Jazz
- * systems (Acer Pica-61, Mips Magnum 4000, Olivetti M700 and
- * perhaps others, too)
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/gfp.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-
-#include <asm/bootinfo.h>
-#include <asm/system.h>
-#include <asm/pgtable.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/jazz.h>
-#include <asm/jazzdma.h>
-
-static char jazz_sonic_string[] = "jazzsonic";
-
-#define SONIC_MEM_SIZE 0x100
-
-#include "sonic.h"
-
-/*
- * Macros to access SONIC registers
- */
-#define SONIC_READ(reg) (*((volatile unsigned int *)dev->base_addr+reg))
-
-#define SONIC_WRITE(reg,val)                                           \
-do {                                                                   \
-       *((volatile unsigned int *)dev->base_addr+(reg)) = (val);               \
-} while (0)
-
-
-/* use 0 for production, 1 for verification, >1 for debug */
-#ifdef SONIC_DEBUG
-static unsigned int sonic_debug = SONIC_DEBUG;
-#else
-static unsigned int sonic_debug = 1;
-#endif
-
-/*
- * We cannot use station (ethernet) address prefixes to detect the
- * sonic controller since these are board manufacturer depended.
- * So we check for known Silicon Revision IDs instead.
- */
-static unsigned short known_revisions[] =
-{
-       0x04,                   /* Mips Magnum 4000 */
-       0xffff                  /* end of list */
-};
-
-static int jazzsonic_open(struct net_device* dev)
-{
-       int retval;
-
-       retval = request_irq(dev->irq, sonic_interrupt, IRQF_DISABLED,
-                               "sonic", dev);
-       if (retval) {
-               printk(KERN_ERR "%s: unable to get IRQ %d.\n",
-                               dev->name, dev->irq);
-               return retval;
-       }
-
-       retval = sonic_open(dev);
-       if (retval)
-               free_irq(dev->irq, dev);
-       return retval;
-}
-
-static int jazzsonic_close(struct net_device* dev)
-{
-       int err;
-       err = sonic_close(dev);
-       free_irq(dev->irq, dev);
-       return err;
-}
-
-static const struct net_device_ops sonic_netdev_ops = {
-       .ndo_open               = jazzsonic_open,
-       .ndo_stop               = jazzsonic_close,
-       .ndo_start_xmit         = sonic_send_packet,
-       .ndo_get_stats          = sonic_get_stats,
-       .ndo_set_multicast_list = sonic_multicast_list,
-       .ndo_tx_timeout         = sonic_tx_timeout,
-       .ndo_change_mtu         = eth_change_mtu,
-       .ndo_validate_addr      = eth_validate_addr,
-       .ndo_set_mac_address    = eth_mac_addr,
-};
-
-static int __devinit sonic_probe1(struct net_device *dev)
-{
-       static unsigned version_printed;
-       unsigned int silicon_revision;
-       unsigned int val;
-       struct sonic_local *lp = netdev_priv(dev);
-       int err = -ENODEV;
-       int i;
-
-       if (!request_mem_region(dev->base_addr, SONIC_MEM_SIZE, jazz_sonic_string))
-               return -EBUSY;
-
-       /*
-        * get the Silicon Revision ID. If this is one of the known
-        * one assume that we found a SONIC ethernet controller at
-        * the expected location.
-        */
-       silicon_revision = SONIC_READ(SONIC_SR);
-       if (sonic_debug > 1)
-               printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);
-
-       i = 0;
-       while (known_revisions[i] != 0xffff &&
-              known_revisions[i] != silicon_revision)
-               i++;
-
-       if (known_revisions[i] == 0xffff) {
-               printk("SONIC ethernet controller not found (0x%4x)\n",
-                      silicon_revision);
-               goto out;
-       }
-
-       if (sonic_debug  &&  version_printed++ == 0)
-               printk(version);
-
-       printk(KERN_INFO "%s: Sonic ethernet found at 0x%08lx, ",
-              dev_name(lp->device), dev->base_addr);
-
-       /*
-        * Put the sonic into software reset, then
-        * retrieve and print the ethernet address.
-        */
-       SONIC_WRITE(SONIC_CMD,SONIC_CR_RST);
-       SONIC_WRITE(SONIC_CEP,0);
-       for (i=0; i<3; i++) {
-               val = SONIC_READ(SONIC_CAP0-i);
-               dev->dev_addr[i*2] = val;
-               dev->dev_addr[i*2+1] = val >> 8;
-       }
-
-       err = -ENOMEM;
-
-       /* Initialize the device structure. */
-
-       lp->dma_bitmode = SONIC_BITMODE32;
-
-       /* Allocate the entire chunk of memory for the descriptors.
-           Note that this cannot cross a 64K boundary. */
-       if ((lp->descriptors = dma_alloc_coherent(lp->device,
-                               SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                               &lp->descriptors_laddr, GFP_KERNEL)) == NULL) {
-               printk(KERN_ERR "%s: couldn't alloc DMA memory for descriptors.\n",
-                      dev_name(lp->device));
-               goto out;
-       }
-
-       /* Now set up the pointers to point to the appropriate places */
-       lp->cda = lp->descriptors;
-       lp->tda = lp->cda + (SIZEOF_SONIC_CDA
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-
-       lp->cda_laddr = lp->descriptors_laddr;
-       lp->tda_laddr = lp->cda_laddr + (SIZEOF_SONIC_CDA
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rda_laddr = lp->tda_laddr + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rra_laddr = lp->rda_laddr + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-
-       dev->netdev_ops = &sonic_netdev_ops;
-       dev->watchdog_timeo = TX_TIMEOUT;
-
-       /*
-        * clear tally counter
-        */
-       SONIC_WRITE(SONIC_CRCT,0xffff);
-       SONIC_WRITE(SONIC_FAET,0xffff);
-       SONIC_WRITE(SONIC_MPT,0xffff);
-
-       return 0;
-out:
-       release_mem_region(dev->base_addr, SONIC_MEM_SIZE);
-       return err;
-}
-
-/*
- * Probe for a SONIC ethernet controller on a Mips Jazz board.
- * Actually probing is superfluous but we're paranoid.
- */
-static int __devinit jazz_sonic_probe(struct platform_device *pdev)
-{
-       struct net_device *dev;
-       struct sonic_local *lp;
-       struct resource *res;
-       int err = 0;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENODEV;
-
-       dev = alloc_etherdev(sizeof(struct sonic_local));
-       if (!dev)
-               return -ENOMEM;
-
-       lp = netdev_priv(dev);
-       lp->device = &pdev->dev;
-       SET_NETDEV_DEV(dev, &pdev->dev);
-       platform_set_drvdata(pdev, dev);
-
-       netdev_boot_setup_check(dev);
-
-       dev->base_addr = res->start;
-       dev->irq = platform_get_irq(pdev, 0);
-       err = sonic_probe1(dev);
-       if (err)
-               goto out;
-       err = register_netdev(dev);
-       if (err)
-               goto out1;
-
-       printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
-
-       return 0;
-
-out1:
-       release_mem_region(dev->base_addr, SONIC_MEM_SIZE);
-out:
-       free_netdev(dev);
-
-       return err;
-}
-
-MODULE_DESCRIPTION("Jazz SONIC ethernet driver");
-module_param(sonic_debug, int, 0);
-MODULE_PARM_DESC(sonic_debug, "jazzsonic debug level (1-4)");
-MODULE_ALIAS("platform:jazzsonic");
-
-#include "sonic.c"
-
-static int __devexit jazz_sonic_device_remove (struct platform_device *pdev)
-{
-       struct net_device *dev = platform_get_drvdata(pdev);
-       struct sonic_local* lp = netdev_priv(dev);
-
-       unregister_netdev(dev);
-       dma_free_coherent(lp->device, SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                         lp->descriptors, lp->descriptors_laddr);
-       release_mem_region(dev->base_addr, SONIC_MEM_SIZE);
-       free_netdev(dev);
-
-       return 0;
-}
-
-static struct platform_driver jazz_sonic_driver = {
-       .probe  = jazz_sonic_probe,
-       .remove = __devexit_p(jazz_sonic_device_remove),
-       .driver = {
-               .name   = jazz_sonic_string,
-               .owner  = THIS_MODULE,
-       },
-};
-
-static int __init jazz_sonic_init_module(void)
-{
-       return platform_driver_register(&jazz_sonic_driver);
-}
-
-static void __exit jazz_sonic_cleanup_module(void)
-{
-       platform_driver_unregister(&jazz_sonic_driver);
-}
-
-module_init(jazz_sonic_init_module);
-module_exit(jazz_sonic_cleanup_module);
diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c
deleted file mode 100644 (file)
index c93679e..0000000
+++ /dev/null
@@ -1,666 +0,0 @@
-/*
- * macsonic.c
- *
- * (C) 2005 Finn Thain
- *
- * Converted to DMA API, converted to unified driver model, made it work as
- * a module again, and from the mac68k project, introduced more 32-bit cards
- * and dhd's support for 16-bit cards.
- *
- * (C) 1998 Alan Cox
- *
- * Debugging Andreas Ehliar, Michael Schmitz
- *
- * Based on code
- * (C) 1996 by Thomas Bogendoerfer (tsbogend@bigbug.franken.de)
- *
- * This driver is based on work from Andreas Busse, but most of
- * the code is rewritten.
- *
- * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
- *
- * A driver for the Mac onboard Sonic ethernet chip.
- *
- * 98/12/21 MSch: judged from tests on Q800, it's basically working,
- *               but eating up both receive and transmit resources
- *               and duplicating packets. Needs more testing.
- *
- * 99/01/03 MSch: upgraded to version 0.92 of the core driver, fixed.
- *
- * 00/10/31 sammy@oh.verio.com: Updated driver for 2.4 kernels, fixed problems
- *          on centris.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/gfp.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/nubus.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/bitrev.h>
-#include <linux/slab.h>
-
-#include <asm/bootinfo.h>
-#include <asm/system.h>
-#include <asm/pgtable.h>
-#include <asm/io.h>
-#include <asm/hwtest.h>
-#include <asm/dma.h>
-#include <asm/macintosh.h>
-#include <asm/macints.h>
-#include <asm/mac_via.h>
-
-static char mac_sonic_string[] = "macsonic";
-
-#include "sonic.h"
-
-/* These should basically be bus-size and endian independent (since
-   the SONIC is at least smart enough that it uses the same endianness
-   as the host, unlike certain less enlightened Macintosh NICs) */
-#define SONIC_READ(reg) (nubus_readw(dev->base_addr + (reg * 4) \
-             + lp->reg_offset))
-#define SONIC_WRITE(reg,val) (nubus_writew(val, dev->base_addr + (reg * 4) \
-             + lp->reg_offset))
-
-/* use 0 for production, 1 for verification, >1 for debug */
-#ifdef SONIC_DEBUG
-static unsigned int sonic_debug = SONIC_DEBUG;
-#else
-static unsigned int sonic_debug = 1;
-#endif
-
-static int sonic_version_printed;
-
-/* For onboard SONIC */
-#define ONBOARD_SONIC_REGISTERS        0x50F0A000
-#define ONBOARD_SONIC_PROM_BASE        0x50f08000
-
-enum macsonic_type {
-       MACSONIC_DUODOCK,
-       MACSONIC_APPLE,
-       MACSONIC_APPLE16,
-       MACSONIC_DAYNA,
-       MACSONIC_DAYNALINK
-};
-
-/* For the built-in SONIC in the Duo Dock */
-#define DUODOCK_SONIC_REGISTERS 0xe10000
-#define DUODOCK_SONIC_PROM_BASE 0xe12000
-
-/* For Apple-style NuBus SONIC */
-#define APPLE_SONIC_REGISTERS  0
-#define APPLE_SONIC_PROM_BASE  0x40000
-
-/* Daynalink LC SONIC */
-#define DAYNALINK_PROM_BASE 0x400000
-
-/* For Dayna-style NuBus SONIC (haven't seen one yet) */
-#define DAYNA_SONIC_REGISTERS   0x180000
-/* This is what OpenBSD says.  However, this is definitely in NuBus
-   ROM space so we should be able to get it by walking the NuBus
-   resource directories */
-#define DAYNA_SONIC_MAC_ADDR   0xffe004
-
-#define SONIC_READ_PROM(addr) nubus_readb(prom_addr+addr)
-
-/*
- * For reversing the PROM address
- */
-
-static inline void bit_reverse_addr(unsigned char addr[6])
-{
-       int i;
-
-       for(i = 0; i < 6; i++)
-               addr[i] = bitrev8(addr[i]);
-}
-
-static irqreturn_t macsonic_interrupt(int irq, void *dev_id)
-{
-       irqreturn_t result;
-       unsigned long flags;
-
-       local_irq_save(flags);
-       result = sonic_interrupt(irq, dev_id);
-       local_irq_restore(flags);
-       return result;
-}
-
-static int macsonic_open(struct net_device* dev)
-{
-       int retval;
-
-       retval = request_irq(dev->irq, sonic_interrupt, IRQ_FLG_FAST,
-                               "sonic", dev);
-       if (retval) {
-               printk(KERN_ERR "%s: unable to get IRQ %d.\n",
-                               dev->name, dev->irq);
-               goto err;
-       }
-       /* Under the A/UX interrupt scheme, the onboard SONIC interrupt comes
-        * in at priority level 3. However, we sometimes get the level 2 inter-
-        * rupt as well, which must prevent re-entrance of the sonic handler.
-        */
-       if (dev->irq == IRQ_AUTO_3) {
-               retval = request_irq(IRQ_NUBUS_9, macsonic_interrupt,
-                                       IRQ_FLG_FAST, "sonic", dev);
-               if (retval) {
-                       printk(KERN_ERR "%s: unable to get IRQ %d.\n",
-                                       dev->name, IRQ_NUBUS_9);
-                       goto err_irq;
-               }
-       }
-       retval = sonic_open(dev);
-       if (retval)
-               goto err_irq_nubus;
-       return 0;
-
-err_irq_nubus:
-       if (dev->irq == IRQ_AUTO_3)
-               free_irq(IRQ_NUBUS_9, dev);
-err_irq:
-       free_irq(dev->irq, dev);
-err:
-       return retval;
-}
-
-static int macsonic_close(struct net_device* dev)
-{
-       int err;
-       err = sonic_close(dev);
-       free_irq(dev->irq, dev);
-       if (dev->irq == IRQ_AUTO_3)
-               free_irq(IRQ_NUBUS_9, dev);
-       return err;
-}
-
-static const struct net_device_ops macsonic_netdev_ops = {
-       .ndo_open               = macsonic_open,
-       .ndo_stop               = macsonic_close,
-       .ndo_start_xmit         = sonic_send_packet,
-       .ndo_set_multicast_list = sonic_multicast_list,
-       .ndo_tx_timeout         = sonic_tx_timeout,
-       .ndo_get_stats          = sonic_get_stats,
-       .ndo_validate_addr      = eth_validate_addr,
-       .ndo_change_mtu         = eth_change_mtu,
-       .ndo_set_mac_address    = eth_mac_addr,
-};
-
-static int __devinit macsonic_init(struct net_device *dev)
-{
-       struct sonic_local* lp = netdev_priv(dev);
-
-       /* Allocate the entire chunk of memory for the descriptors.
-           Note that this cannot cross a 64K boundary. */
-       if ((lp->descriptors = dma_alloc_coherent(lp->device,
-                   SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                   &lp->descriptors_laddr, GFP_KERNEL)) == NULL) {
-               printk(KERN_ERR "%s: couldn't alloc DMA memory for descriptors.\n",
-                      dev_name(lp->device));
-               return -ENOMEM;
-       }
-
-       /* Now set up the pointers to point to the appropriate places */
-       lp->cda = lp->descriptors;
-       lp->tda = lp->cda + (SIZEOF_SONIC_CDA
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-
-       lp->cda_laddr = lp->descriptors_laddr;
-       lp->tda_laddr = lp->cda_laddr + (SIZEOF_SONIC_CDA
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rda_laddr = lp->tda_laddr + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rra_laddr = lp->rda_laddr + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-
-       dev->netdev_ops = &macsonic_netdev_ops;
-       dev->watchdog_timeo = TX_TIMEOUT;
-
-       /*
-        * clear tally counter
-        */
-       SONIC_WRITE(SONIC_CRCT, 0xffff);
-       SONIC_WRITE(SONIC_FAET, 0xffff);
-       SONIC_WRITE(SONIC_MPT, 0xffff);
-
-       return 0;
-}
-
-#define INVALID_MAC(mac) (memcmp(mac, "\x08\x00\x07", 3) && \
-                          memcmp(mac, "\x00\xA0\x40", 3) && \
-                          memcmp(mac, "\x00\x80\x19", 3) && \
-                          memcmp(mac, "\x00\x05\x02", 3))
-
-static void __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       const int prom_addr = ONBOARD_SONIC_PROM_BASE;
-       unsigned short val;
-
-       /*
-        * On NuBus boards we can sometimes look in the ROM resources.
-        * No such luck for comm-slot/onboard.
-        * On the PowerBook 520, the PROM base address is a mystery.
-        */
-       if (hwreg_present((void *)prom_addr)) {
-               int i;
-
-               for (i = 0; i < 6; i++)
-                       dev->dev_addr[i] = SONIC_READ_PROM(i);
-               if (!INVALID_MAC(dev->dev_addr))
-                       return;
-
-               /*
-                * Most of the time, the address is bit-reversed. The NetBSD
-                * source has a rather long and detailed historical account of
-                * why this is so.
-                */
-               bit_reverse_addr(dev->dev_addr);
-               if (!INVALID_MAC(dev->dev_addr))
-                       return;
-
-               /*
-                * If we still have what seems to be a bogus address, we'll
-                * look in the CAM. The top entry should be ours.
-                */
-               printk(KERN_WARNING "macsonic: MAC address in PROM seems "
-                                   "to be invalid, trying CAM\n");
-       } else {
-               printk(KERN_WARNING "macsonic: cannot read MAC address from "
-                                   "PROM, trying CAM\n");
-       }
-
-       /* This only works if MacOS has already initialized the card. */
-
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
-       SONIC_WRITE(SONIC_CEP, 15);
-
-       val = SONIC_READ(SONIC_CAP2);
-       dev->dev_addr[5] = val >> 8;
-       dev->dev_addr[4] = val & 0xff;
-       val = SONIC_READ(SONIC_CAP1);
-       dev->dev_addr[3] = val >> 8;
-       dev->dev_addr[2] = val & 0xff;
-       val = SONIC_READ(SONIC_CAP0);
-       dev->dev_addr[1] = val >> 8;
-       dev->dev_addr[0] = val & 0xff;
-
-       if (!INVALID_MAC(dev->dev_addr))
-               return;
-
-       /* Still nonsense ... messed up someplace! */
-
-       printk(KERN_WARNING "macsonic: MAC address in CAM entry 15 "
-                           "seems invalid, will use a random MAC\n");
-       random_ether_addr(dev->dev_addr);
-}
-
-static int __devinit mac_onboard_sonic_probe(struct net_device *dev)
-{
-       /* Bwahahaha */
-       static int once_is_more_than_enough;
-       struct sonic_local* lp = netdev_priv(dev);
-       int sr;
-       int commslot = 0;
-
-       if (once_is_more_than_enough)
-               return -ENODEV;
-       once_is_more_than_enough = 1;
-
-       if (!MACH_IS_MAC)
-               return -ENODEV;
-
-       if (macintosh_config->ether_type != MAC_ETHER_SONIC)
-               return -ENODEV;
-
-       printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. ");
-
-       /* Bogus probing, on the models which may or may not have
-          Ethernet (BTW, the Ethernet *is* always at the same
-          address, and nothing else lives there, at least if Apple's
-          documentation is to be believed) */
-       if (macintosh_config->ident == MAC_MODEL_Q630 ||
-           macintosh_config->ident == MAC_MODEL_P588 ||
-           macintosh_config->ident == MAC_MODEL_P575 ||
-           macintosh_config->ident == MAC_MODEL_C610) {
-               unsigned long flags;
-               int card_present;
-
-               local_irq_save(flags);
-               card_present = hwreg_present((void*)ONBOARD_SONIC_REGISTERS);
-               local_irq_restore(flags);
-
-               if (!card_present) {
-                       printk("none.\n");
-                       return -ENODEV;
-               }
-               commslot = 1;
-       }
-
-       printk("yes\n");
-
-       /* Danger!  My arms are flailing wildly!  You *must* set lp->reg_offset
-        * and dev->base_addr before using SONIC_READ() or SONIC_WRITE() */
-       dev->base_addr = ONBOARD_SONIC_REGISTERS;
-       if (via_alt_mapping)
-               dev->irq = IRQ_AUTO_3;
-       else
-               dev->irq = IRQ_NUBUS_9;
-
-       if (!sonic_version_printed) {
-               printk(KERN_INFO "%s", version);
-               sonic_version_printed = 1;
-       }
-       printk(KERN_INFO "%s: onboard / comm-slot SONIC at 0x%08lx\n",
-              dev_name(lp->device), dev->base_addr);
-
-       /* The PowerBook's SONIC is 16 bit always. */
-       if (macintosh_config->ident == MAC_MODEL_PB520) {
-               lp->reg_offset = 0;
-               lp->dma_bitmode = SONIC_BITMODE16;
-               sr = SONIC_READ(SONIC_SR);
-       } else if (commslot) {
-               /* Some of the comm-slot cards are 16 bit.  But some
-                  of them are not.  The 32-bit cards use offset 2 and
-                  have known revisions, we try reading the revision
-                  register at offset 2, if we don't get a known revision
-                  we assume 16 bit at offset 0.  */
-               lp->reg_offset = 2;
-               lp->dma_bitmode = SONIC_BITMODE16;
-
-               sr = SONIC_READ(SONIC_SR);
-               if (sr == 0x0004 || sr == 0x0006 || sr == 0x0100 || sr == 0x0101)
-                       /* 83932 is 0x0004 or 0x0006, 83934 is 0x0100 or 0x0101 */
-                       lp->dma_bitmode = SONIC_BITMODE32;
-               else {
-                       lp->dma_bitmode = SONIC_BITMODE16;
-                       lp->reg_offset = 0;
-                       sr = SONIC_READ(SONIC_SR);
-               }
-       } else {
-               /* All onboard cards are at offset 2 with 32 bit DMA. */
-               lp->reg_offset = 2;
-               lp->dma_bitmode = SONIC_BITMODE32;
-               sr = SONIC_READ(SONIC_SR);
-       }
-       printk(KERN_INFO
-              "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
-              dev_name(lp->device), sr, lp->dma_bitmode?32:16, lp->reg_offset);
-
-#if 0 /* This is sometimes useful to find out how MacOS configured the card. */
-       printk(KERN_INFO "%s: DCR: 0x%04x, DCR2: 0x%04x\n", dev_name(lp->device),
-              SONIC_READ(SONIC_DCR) & 0xffff, SONIC_READ(SONIC_DCR2) & 0xffff);
-#endif
-
-       /* Software reset, then initialize control registers. */
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
-
-       SONIC_WRITE(SONIC_DCR, SONIC_DCR_EXBUS | SONIC_DCR_BMS |
-                              SONIC_DCR_RFT1  | SONIC_DCR_TFT0 |
-                              (lp->dma_bitmode ? SONIC_DCR_DW : 0));
-
-       /* This *must* be written back to in order to restore the
-        * extended programmable output bits, as it may not have been
-        * initialised since the hardware reset. */
-       SONIC_WRITE(SONIC_DCR2, 0);
-
-       /* Clear *and* disable interrupts to be on the safe side */
-       SONIC_WRITE(SONIC_IMR, 0);
-       SONIC_WRITE(SONIC_ISR, 0x7fff);
-
-       /* Now look for the MAC address. */
-       mac_onboard_sonic_ethernet_addr(dev);
-
-       /* Shared init code */
-       return macsonic_init(dev);
-}
-
-static int __devinit mac_nubus_sonic_ethernet_addr(struct net_device *dev,
-                                               unsigned long prom_addr,
-                                               int id)
-{
-       int i;
-       for(i = 0; i < 6; i++)
-               dev->dev_addr[i] = SONIC_READ_PROM(i);
-
-       /* Some of the addresses are bit-reversed */
-       if (id != MACSONIC_DAYNA)
-               bit_reverse_addr(dev->dev_addr);
-
-       return 0;
-}
-
-static int __devinit macsonic_ident(struct nubus_dev *ndev)
-{
-       if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC &&
-           ndev->dr_sw == NUBUS_DRSW_SONIC_LC)
-               return MACSONIC_DAYNALINK;
-       if (ndev->dr_hw == NUBUS_DRHW_SONIC &&
-           ndev->dr_sw == NUBUS_DRSW_APPLE) {
-               /* There has to be a better way to do this... */
-               if (strstr(ndev->board->name, "DuoDock"))
-                       return MACSONIC_DUODOCK;
-               else
-                       return MACSONIC_APPLE;
-       }
-
-       if (ndev->dr_hw == NUBUS_DRHW_SMC9194 &&
-           ndev->dr_sw == NUBUS_DRSW_DAYNA)
-               return MACSONIC_DAYNA;
-
-       if (ndev->dr_hw == NUBUS_DRHW_APPLE_SONIC_LC &&
-           ndev->dr_sw == 0) { /* huh? */
-               return MACSONIC_APPLE16;
-       }
-       return -1;
-}
-
-static int __devinit mac_nubus_sonic_probe(struct net_device *dev)
-{
-       static int slots;
-       struct nubus_dev* ndev = NULL;
-       struct sonic_local* lp = netdev_priv(dev);
-       unsigned long base_addr, prom_addr;
-       u16 sonic_dcr;
-       int id = -1;
-       int reg_offset, dma_bitmode;
-
-       /* Find the first SONIC that hasn't been initialized already */
-       while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK,
-                                      NUBUS_TYPE_ETHERNET, ndev)) != NULL)
-       {
-               /* Have we seen it already? */
-               if (slots & (1<<ndev->board->slot))
-                       continue;
-               slots |= 1<<ndev->board->slot;
-
-               /* Is it one of ours? */
-               if ((id = macsonic_ident(ndev)) != -1)
-                       break;
-       }
-
-       if (ndev == NULL)
-               return -ENODEV;
-
-       switch (id) {
-       case MACSONIC_DUODOCK:
-               base_addr = ndev->board->slot_addr + DUODOCK_SONIC_REGISTERS;
-               prom_addr = ndev->board->slot_addr + DUODOCK_SONIC_PROM_BASE;
-               sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT0 | SONIC_DCR_RFT1 |
-                           SONIC_DCR_TFT0;
-               reg_offset = 2;
-               dma_bitmode = SONIC_BITMODE32;
-               break;
-       case MACSONIC_APPLE:
-               base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
-               prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
-               sonic_dcr = SONIC_DCR_BMS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0;
-               reg_offset = 0;
-               dma_bitmode = SONIC_BITMODE32;
-               break;
-       case MACSONIC_APPLE16:
-               base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
-               prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE;
-               sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 |
-                           SONIC_DCR_PO1 | SONIC_DCR_BMS;
-               reg_offset = 0;
-               dma_bitmode = SONIC_BITMODE16;
-               break;
-       case MACSONIC_DAYNALINK:
-               base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS;
-               prom_addr = ndev->board->slot_addr + DAYNALINK_PROM_BASE;
-               sonic_dcr = SONIC_DCR_RFT1 | SONIC_DCR_TFT0 |
-                           SONIC_DCR_PO1 | SONIC_DCR_BMS;
-               reg_offset = 0;
-               dma_bitmode = SONIC_BITMODE16;
-               break;
-       case MACSONIC_DAYNA:
-               base_addr = ndev->board->slot_addr + DAYNA_SONIC_REGISTERS;
-               prom_addr = ndev->board->slot_addr + DAYNA_SONIC_MAC_ADDR;
-               sonic_dcr = SONIC_DCR_BMS |
-                           SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_PO1;
-               reg_offset = 0;
-               dma_bitmode = SONIC_BITMODE16;
-               break;
-       default:
-               printk(KERN_ERR "macsonic: WTF, id is %d\n", id);
-               return -ENODEV;
-       }
-
-       /* Danger!  My arms are flailing wildly!  You *must* set lp->reg_offset
-        * and dev->base_addr before using SONIC_READ() or SONIC_WRITE() */
-       dev->base_addr = base_addr;
-       lp->reg_offset = reg_offset;
-       lp->dma_bitmode = dma_bitmode;
-       dev->irq = SLOT2IRQ(ndev->board->slot);
-
-       if (!sonic_version_printed) {
-               printk(KERN_INFO "%s", version);
-               sonic_version_printed = 1;
-       }
-       printk(KERN_INFO "%s: %s in slot %X\n",
-              dev_name(lp->device), ndev->board->name, ndev->board->slot);
-       printk(KERN_INFO "%s: revision 0x%04x, using %d bit DMA and register offset %d\n",
-              dev_name(lp->device), SONIC_READ(SONIC_SR), dma_bitmode?32:16, reg_offset);
-
-#if 0 /* This is sometimes useful to find out how MacOS configured the card. */
-       printk(KERN_INFO "%s: DCR: 0x%04x, DCR2: 0x%04x\n", dev_name(lp->device),
-              SONIC_READ(SONIC_DCR) & 0xffff, SONIC_READ(SONIC_DCR2) & 0xffff);
-#endif
-
-       /* Software reset, then initialize control registers. */
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
-       SONIC_WRITE(SONIC_DCR, sonic_dcr | (dma_bitmode ? SONIC_DCR_DW : 0));
-       /* This *must* be written back to in order to restore the
-        * extended programmable output bits, since it may not have been
-        * initialised since the hardware reset. */
-       SONIC_WRITE(SONIC_DCR2, 0);
-
-       /* Clear *and* disable interrupts to be on the safe side */
-       SONIC_WRITE(SONIC_IMR, 0);
-       SONIC_WRITE(SONIC_ISR, 0x7fff);
-
-       /* Now look for the MAC address. */
-       if (mac_nubus_sonic_ethernet_addr(dev, prom_addr, id) != 0)
-               return -ENODEV;
-
-       /* Shared init code */
-       return macsonic_init(dev);
-}
-
-static int __devinit mac_sonic_probe(struct platform_device *pdev)
-{
-       struct net_device *dev;
-       struct sonic_local *lp;
-       int err;
-
-       dev = alloc_etherdev(sizeof(struct sonic_local));
-       if (!dev)
-               return -ENOMEM;
-
-       lp = netdev_priv(dev);
-       lp->device = &pdev->dev;
-       SET_NETDEV_DEV(dev, &pdev->dev);
-       platform_set_drvdata(pdev, dev);
-
-       /* This will catch fatal stuff like -ENOMEM as well as success */
-       err = mac_onboard_sonic_probe(dev);
-       if (err == 0)
-               goto found;
-       if (err != -ENODEV)
-               goto out;
-       err = mac_nubus_sonic_probe(dev);
-       if (err)
-               goto out;
-found:
-       err = register_netdev(dev);
-       if (err)
-               goto out;
-
-       printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
-
-       return 0;
-
-out:
-       free_netdev(dev);
-
-       return err;
-}
-
-MODULE_DESCRIPTION("Macintosh SONIC ethernet driver");
-module_param(sonic_debug, int, 0);
-MODULE_PARM_DESC(sonic_debug, "macsonic debug level (1-4)");
-MODULE_ALIAS("platform:macsonic");
-
-#include "sonic.c"
-
-static int __devexit mac_sonic_device_remove (struct platform_device *pdev)
-{
-       struct net_device *dev = platform_get_drvdata(pdev);
-       struct sonic_local* lp = netdev_priv(dev);
-
-       unregister_netdev(dev);
-       dma_free_coherent(lp->device, SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                         lp->descriptors, lp->descriptors_laddr);
-       free_netdev(dev);
-
-       return 0;
-}
-
-static struct platform_driver mac_sonic_driver = {
-       .probe  = mac_sonic_probe,
-       .remove = __devexit_p(mac_sonic_device_remove),
-       .driver = {
-               .name   = mac_sonic_string,
-               .owner  = THIS_MODULE,
-       },
-};
-
-static int __init mac_sonic_init_module(void)
-{
-       return platform_driver_register(&mac_sonic_driver);
-}
-
-static void __exit mac_sonic_cleanup_module(void)
-{
-       platform_driver_unregister(&mac_sonic_driver);
-}
-
-module_init(mac_sonic_init_module);
-module_exit(mac_sonic_cleanup_module);
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
deleted file mode 100644 (file)
index 2962cc6..0000000
+++ /dev/null
@@ -1,3370 +0,0 @@
-/* natsemi.c: A Linux PCI Ethernet driver for the NatSemi DP8381x series. */
-/*
-       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.
-       Drivers based on or derived from this code fall under the GPL and must
-       retain the authorship, copyright and license notice.  This file is not
-       a complete program and may only be used when the entire operating
-       system is licensed under the GPL.  License for under other terms may be
-       available.  Contact the original author for details.
-
-       The original author may be reached as becker@scyld.com, or at
-       Scyld Computing Corporation
-       410 Severn Ave., Suite 210
-       Annapolis MD 21403
-
-       Support information and updates available at
-       http://www.scyld.com/network/netsemi.html
-       [link no longer provides useful info -jgarzik]
-
-
-       TODO:
-       * big endian support with CFG:BEM instead of cpu_to_le32
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/ethtool.h>
-#include <linux/delay.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/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
-#define DRV_NAME       "natsemi"
-#define DRV_VERSION    "2.1"
-#define DRV_RELDATE    "Sept 11, 2006"
-
-#define RX_OFFSET      2
-
-/* Updated to recommendations in pci-skeleton v2.03. */
-
-/* The user-configurable values.
-   These may be modified when a driver module is loaded.*/
-
-#define NATSEMI_DEF_MSG                (NETIF_MSG_DRV          | \
-                                NETIF_MSG_LINK         | \
-                                NETIF_MSG_WOL          | \
-                                NETIF_MSG_RX_ERR       | \
-                                NETIF_MSG_TX_ERR)
-static int debug = -1;
-
-static int mtu;
-
-/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
-   This chip uses a 512 element hash table based on the Ethernet CRC.  */
-static const int multicast_filter_limit = 100;
-
-/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
-   Setting to > 1518 effectively disables this feature. */
-static int rx_copybreak;
-
-static int dspcfg_workaround = 1;
-
-/* Used to pass the media type, etc.
-   Both 'options[]' and 'full_duplex[]' should exist for driver
-   interoperability.
-   The media type is usually passed in 'options[]'.
-*/
-#define MAX_UNITS 8            /* More are supported, limit only on options */
-static int options[MAX_UNITS];
-static int full_duplex[MAX_UNITS];
-
-/* Operational parameters that are set at compile time. */
-
-/* Keep the ring sizes a power of two for compile efficiency.
-   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
-   Making the Tx ring too large decreases the effectiveness of channel
-   bonding and packet priority.
-   There are no ill effects from too-large receive rings. */
-#define TX_RING_SIZE   16
-#define TX_QUEUE_LEN   10 /* Limit ring entries actually used, min 4. */
-#define RX_RING_SIZE   32
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  (2*HZ)
-
-#define NATSEMI_HW_TIMEOUT     400
-#define NATSEMI_TIMER_FREQ     5*HZ
-#define NATSEMI_PG0_NREGS      64
-#define NATSEMI_RFDR_NREGS     8
-#define NATSEMI_PG1_NREGS      4
-#define NATSEMI_NREGS          (NATSEMI_PG0_NREGS + NATSEMI_RFDR_NREGS + \
-                                NATSEMI_PG1_NREGS)
-#define NATSEMI_REGS_VER       1 /* v1 added RFDR registers */
-#define NATSEMI_REGS_SIZE      (NATSEMI_NREGS * sizeof(u32))
-
-/* 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 const char version[] __devinitconst =
-  KERN_INFO DRV_NAME " dp8381x driver, version "
-      DRV_VERSION ", " DRV_RELDATE "\n"
-  "  originally by Donald Becker <becker@scyld.com>\n"
-  "  2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n";
-
-MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
-MODULE_LICENSE("GPL");
-
-module_param(mtu, int, 0);
-module_param(debug, int, 0);
-module_param(rx_copybreak, int, 0);
-module_param(dspcfg_workaround, 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,
-       "DP8381x copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(dspcfg_workaround, "DP8381x: control DspCfg workaround");
-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)");
-
-/*
-                               Theory of Operation
-
-I. Board Compatibility
-
-This driver is designed for National Semiconductor DP83815 PCI Ethernet NIC.
-It also works with other chips in in the DP83810 series.
-
-II. Board-specific settings
-
-This driver requires the PCI interrupt line to be valid.
-It honors the EEPROM-set values.
-
-III. Driver operation
-
-IIIa. Ring buffers
-
-This driver uses two statically allocated fixed-size descriptor lists
-formed into rings by a branch from the final descriptor to the beginning of
-the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE.
-The NatSemi design uses a 'next descriptor' pointer that the driver forms
-into a list.
-
-IIIb/c. Transmit/Receive Structure
-
-This driver uses a zero-copy receive and transmit scheme.
-The driver allocates full frame size skbuffs for the Rx ring buffers at
-open() time and passes the skb->data field to the chip as receive data
-buffers.  When an incoming frame is less than RX_COPYBREAK bytes long,
-a fresh skbuff is allocated and the frame is copied to the new skbuff.
-When the incoming frame is larger, the skbuff is passed directly up the
-protocol stack.  Buffers consumed this way are replaced by newly allocated
-skbuffs in a later phase of receives.
-
-The RX_COPYBREAK value is chosen to trade-off the memory wasted by
-using a full-sized skbuff for small frames vs. the copying costs of larger
-frames.  New boards are typically used in generously configured machines
-and the underfilled buffers have negligible impact compared to the benefit of
-a single allocation size, so the default value of zero results in never
-copying packets.  When copying is done, the cost is usually mitigated by using
-a combined copy/checksum routine.  Copying also preloads the cache, which is
-most useful with small frames.
-
-A subtle aspect of the operation is that unaligned buffers are not permitted
-by the hardware.  Thus the IP header at offset 14 in an ethernet frame isn't
-longword aligned for further processing.  On copies frames are put into the
-skbuff at an offset of "+2", 16-byte aligning the IP header.
-
-IIId. Synchronization
-
-Most operations are synchronized on the np->lock irq spinlock, except the
-receive and transmit paths which are synchronised using a combination of
-hardware descriptor ownership, disabling interrupts and NAPI poll scheduling.
-
-IVb. References
-
-http://www.scyld.com/expert/100mbps.html
-http://www.scyld.com/expert/NWay.html
-Datasheet is available from:
-http://www.national.com/pf/DP/DP83815.html
-
-IVc. Errata
-
-None characterised.
-*/
-
-
-
-/*
- * 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
-
-enum {
-       MII_MCTRL       = 0x15,         /* mode control register */
-       MII_FX_SEL      = 0x0001,       /* 100BASE-FX (fiber) */
-       MII_EN_SCRM     = 0x0004,       /* enable scrambler (tp) */
-};
-
-enum {
-       NATSEMI_FLAG_IGNORE_PHY         = 0x1,
-};
-
-/* array of board data directly indexed by pci_tbl[x].driver_data */
-static struct {
-       const char *name;
-       unsigned long flags;
-       unsigned int eeprom_size;
-} natsemi_pci_info[] __devinitdata = {
-       { "Aculab E1/T1 PMXc cPCI carrier card", NATSEMI_FLAG_IGNORE_PHY, 128 },
-       { "NatSemi DP8381[56]", 0, 24 },
-};
-
-static DEFINE_PCI_DEVICE_TABLE(natsemi_pci_tbl) = {
-       { PCI_VENDOR_ID_NS, 0x0020, 0x12d9,     0x000c,     0, 0, 0 },
-       { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
-       { }     /* terminate list */
-};
-MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl);
-
-/* Offsets to the device registers.
-   Unlike software-only systems, device drivers interact with complex hardware.
-   It's not useful to define symbolic names for every register bit in the
-   device.
-*/
-enum register_offsets {
-       ChipCmd                 = 0x00,
-       ChipConfig              = 0x04,
-       EECtrl                  = 0x08,
-       PCIBusCfg               = 0x0C,
-       IntrStatus              = 0x10,
-       IntrMask                = 0x14,
-       IntrEnable              = 0x18,
-       IntrHoldoff             = 0x1C, /* DP83816 only */
-       TxRingPtr               = 0x20,
-       TxConfig                = 0x24,
-       RxRingPtr               = 0x30,
-       RxConfig                = 0x34,
-       ClkRun                  = 0x3C,
-       WOLCmd                  = 0x40,
-       PauseCmd                = 0x44,
-       RxFilterAddr            = 0x48,
-       RxFilterData            = 0x4C,
-       BootRomAddr             = 0x50,
-       BootRomData             = 0x54,
-       SiliconRev              = 0x58,
-       StatsCtrl               = 0x5C,
-       StatsData               = 0x60,
-       RxPktErrs               = 0x60,
-       RxMissed                = 0x68,
-       RxCRCErrs               = 0x64,
-       BasicControl            = 0x80,
-       BasicStatus             = 0x84,
-       AnegAdv                 = 0x90,
-       AnegPeer                = 0x94,
-       PhyStatus               = 0xC0,
-       MIntrCtrl               = 0xC4,
-       MIntrStatus             = 0xC8,
-       PhyCtrl                 = 0xE4,
-
-       /* These are from the spec, around page 78... on a separate table.
-        * The meaning of these registers depend on the value of PGSEL. */
-       PGSEL                   = 0xCC,
-       PMDCSR                  = 0xE4,
-       TSTDAT                  = 0xFC,
-       DSPCFG                  = 0xF4,
-       SDCFG                   = 0xF8
-};
-/* the values for the 'magic' registers above (PGSEL=1) */
-#define PMDCSR_VAL     0x189c  /* enable preferred adaptation circuitry */
-#define TSTDAT_VAL     0x0
-#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 */
-enum pci_register_offsets {
-       PCIPM                   = 0x44,
-};
-
-enum ChipCmd_bits {
-       ChipReset               = 0x100,
-       RxReset                 = 0x20,
-       TxReset                 = 0x10,
-       RxOff                   = 0x08,
-       RxOn                    = 0x04,
-       TxOff                   = 0x02,
-       TxOn                    = 0x01,
-};
-
-enum ChipConfig_bits {
-       CfgPhyDis               = 0x200,
-       CfgPhyRst               = 0x400,
-       CfgExtPhy               = 0x1000,
-       CfgAnegEnable           = 0x2000,
-       CfgAneg100              = 0x4000,
-       CfgAnegFull             = 0x8000,
-       CfgAnegDone             = 0x8000000,
-       CfgFullDuplex           = 0x20000000,
-       CfgSpeed100             = 0x40000000,
-       CfgLink                 = 0x80000000,
-};
-
-enum EECtrl_bits {
-       EE_ShiftClk             = 0x04,
-       EE_DataIn               = 0x01,
-       EE_ChipSelect           = 0x08,
-       EE_DataOut              = 0x02,
-       MII_Data                = 0x10,
-       MII_Write               = 0x20,
-       MII_ShiftClk            = 0x40,
-};
-
-enum PCIBusCfg_bits {
-       EepromReload            = 0x4,
-};
-
-/* Bits in the interrupt status/mask registers. */
-enum IntrStatus_bits {
-       IntrRxDone              = 0x0001,
-       IntrRxIntr              = 0x0002,
-       IntrRxErr               = 0x0004,
-       IntrRxEarly             = 0x0008,
-       IntrRxIdle              = 0x0010,
-       IntrRxOverrun           = 0x0020,
-       IntrTxDone              = 0x0040,
-       IntrTxIntr              = 0x0080,
-       IntrTxErr               = 0x0100,
-       IntrTxIdle              = 0x0200,
-       IntrTxUnderrun          = 0x0400,
-       StatsMax                = 0x0800,
-       SWInt                   = 0x1000,
-       WOLPkt                  = 0x2000,
-       LinkChange              = 0x4000,
-       IntrHighBits            = 0x8000,
-       RxStatusFIFOOver        = 0x10000,
-       IntrPCIErr              = 0xf00000,
-       RxResetDone             = 0x1000000,
-       TxResetDone             = 0x2000000,
-       IntrAbnormalSummary     = 0xCD20,
-};
-
-/*
- * Default Interrupts:
- * Rx OK, Rx Packet Error, Rx Overrun,
- * Tx OK, Tx Packet Error, Tx Underrun,
- * MIB Service, Phy Interrupt, High Bits,
- * Rx Status FIFO overrun,
- * Received Target Abort, Received Master Abort,
- * Signalled System Error, Received Parity Error
- */
-#define DEFAULT_INTR 0x00f1cd65
-
-enum TxConfig_bits {
-       TxDrthMask              = 0x3f,
-       TxFlthMask              = 0x3f00,
-       TxMxdmaMask             = 0x700000,
-       TxMxdma_512             = 0x0,
-       TxMxdma_4               = 0x100000,
-       TxMxdma_8               = 0x200000,
-       TxMxdma_16              = 0x300000,
-       TxMxdma_32              = 0x400000,
-       TxMxdma_64              = 0x500000,
-       TxMxdma_128             = 0x600000,
-       TxMxdma_256             = 0x700000,
-       TxCollRetry             = 0x800000,
-       TxAutoPad               = 0x10000000,
-       TxMacLoop               = 0x20000000,
-       TxHeartIgn              = 0x40000000,
-       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,
-       RxMxdma_512             = 0x0,
-       RxMxdma_4               = 0x100000,
-       RxMxdma_8               = 0x200000,
-       RxMxdma_16              = 0x300000,
-       RxMxdma_32              = 0x400000,
-       RxMxdma_64              = 0x500000,
-       RxMxdma_128             = 0x600000,
-       RxMxdma_256             = 0x700000,
-       RxAcceptLong            = 0x8000000,
-       RxAcceptTx              = 0x10000000,
-       RxAcceptRunt            = 0x40000000,
-       RxAcceptErr             = 0x80000000
-};
-#define RX_DRTH_VAL            (128/8)
-
-enum ClkRun_bits {
-       PMEEnable               = 0x100,
-       PMEStatus               = 0x8000,
-};
-
-enum WolCmd_bits {
-       WakePhy                 = 0x1,
-       WakeUnicast             = 0x2,
-       WakeMulticast           = 0x4,
-       WakeBroadcast           = 0x8,
-       WakeArp                 = 0x10,
-       WakePMatch0             = 0x20,
-       WakePMatch1             = 0x40,
-       WakePMatch2             = 0x80,
-       WakePMatch3             = 0x100,
-       WakeMagic               = 0x200,
-       WakeMagicSecure         = 0x400,
-       SecureHack              = 0x100000,
-       WokePhy                 = 0x400000,
-       WokeUnicast             = 0x800000,
-       WokeMulticast           = 0x1000000,
-       WokeBroadcast           = 0x2000000,
-       WokeArp                 = 0x4000000,
-       WokePMatch0             = 0x8000000,
-       WokePMatch1             = 0x10000000,
-       WokePMatch2             = 0x20000000,
-       WokePMatch3             = 0x40000000,
-       WokeMagic               = 0x80000000,
-       WakeOptsSummary         = 0x7ff
-};
-
-enum RxFilterAddr_bits {
-       RFCRAddressMask         = 0x3ff,
-       AcceptMulticast         = 0x00200000,
-       AcceptMyPhys            = 0x08000000,
-       AcceptAllPhys           = 0x10000000,
-       AcceptAllMulticast      = 0x20000000,
-       AcceptBroadcast         = 0x40000000,
-       RxFilterEnable          = 0x80000000
-};
-
-enum StatsCtrl_bits {
-       StatsWarn               = 0x1,
-       StatsFreeze             = 0x2,
-       StatsClear              = 0x4,
-       StatsStrobe             = 0x8,
-};
-
-enum MIntrCtrl_bits {
-       MICRIntEn               = 0x2,
-};
-
-enum PhyCtrl_bits {
-       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
-#define SRR_DP83816_A4 0x0504
-#define SRR_DP83816_A5 0x0505
-
-/* The Rx and Tx buffer descriptors. */
-/* Note that using only 32 bit fields simplifies conversion to big-endian
-   architectures. */
-struct netdev_desc {
-       __le32 next_desc;
-       __le32 cmd_status;
-       __le32 addr;
-       __le32 software_use;
-};
-
-/* Bits in network_desc.status */
-enum desc_status_bits {
-       DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000,
-       DescNoCRC=0x10000000, DescPktOK=0x08000000,
-       DescSizeMask=0xfff,
-
-       DescTxAbort=0x04000000, DescTxFIFO=0x02000000,
-       DescTxCarrier=0x01000000, DescTxDefer=0x00800000,
-       DescTxExcDefer=0x00400000, DescTxOOWCol=0x00200000,
-       DescTxExcColl=0x00100000, DescTxCollCount=0x000f0000,
-
-       DescRxAbort=0x04000000, DescRxOver=0x02000000,
-       DescRxDest=0x01800000, DescRxLong=0x00400000,
-       DescRxRunt=0x00200000, DescRxInvalid=0x00100000,
-       DescRxCRC=0x00080000, DescRxAlign=0x00040000,
-       DescRxLoop=0x00020000, DesRxColl=0x00010000,
-};
-
-struct netdev_private {
-       /* Descriptor rings first for alignment */
-       dma_addr_t ring_dma;
-       struct netdev_desc *rx_ring;
-       struct netdev_desc *tx_ring;
-       /* The addresses of receive-in-place skbuffs */
-       struct sk_buff *rx_skbuff[RX_RING_SIZE];
-       dma_addr_t rx_dma[RX_RING_SIZE];
-       /* address of a sent-in-place packet/buffer, for later free() */
-       struct sk_buff *tx_skbuff[TX_RING_SIZE];
-       dma_addr_t tx_dma[TX_RING_SIZE];
-       struct net_device *dev;
-       struct napi_struct napi;
-       /* Media monitoring timer */
-       struct timer_list timer;
-       /* Frequently used values: keep some adjacent for cache effect */
-       struct pci_dev *pci_dev;
-       struct netdev_desc *rx_head_desc;
-       /* Producer/consumer ring indices */
-       unsigned int cur_rx, dirty_rx;
-       unsigned int cur_tx, dirty_tx;
-       /* 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;
-       /* Don't pay attention to the reported link state. */
-       int ignore_phy;
-       /* 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;
-       u32 rx_filter[16];
-       /* FIFO and PCI burst thresholds */
-       u32 tx_config, rx_config;
-       /* original contents of ClkRun register */
-       u32 SavedClkRun;
-       /* silicon revision */
-       u32 srr;
-       /* expected DSPCFG value */
-       u16 dspcfg;
-       int dspcfg_workaround;
-       /* 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 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);
-static int netdev_open(struct net_device *dev);
-static void do_cable_magic(struct net_device *dev);
-static void undo_cable_magic(struct net_device *dev);
-static void check_link(struct net_device *dev);
-static void netdev_timer(unsigned long data);
-static void dump_ring(struct net_device *dev);
-static void ns_tx_timeout(struct net_device *dev);
-static int alloc_ring(struct net_device *dev);
-static void refill_rx(struct net_device *dev);
-static void init_ring(struct net_device *dev);
-static void drain_tx(struct net_device *dev);
-static void drain_ring(struct net_device *dev);
-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 netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
-static irqreturn_t intr_handler(int irq, void *dev_instance);
-static void netdev_error(struct net_device *dev, int intr_status);
-static int natsemi_poll(struct napi_struct *napi, 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);
-static struct net_device_stats *get_stats(struct net_device *dev);
-static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static int netdev_set_wol(struct net_device *dev, u32 newval);
-static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur);
-static int netdev_set_sopass(struct net_device *dev, u8 *newval);
-static int netdev_get_sopass(struct net_device *dev, u8 *data);
-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);
-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;
-
-#define NATSEMI_ATTR(_name) \
-static ssize_t natsemi_show_##_name(struct device *dev, \
-         struct device_attribute *attr, char *buf); \
-        static ssize_t natsemi_set_##_name(struct device *dev, \
-               struct device_attribute *attr, \
-               const char *buf, size_t count); \
-        static DEVICE_ATTR(_name, 0644, natsemi_show_##_name, natsemi_set_##_name)
-
-#define NATSEMI_CREATE_FILE(_dev, _name) \
-         device_create_file(&_dev->dev, &dev_attr_##_name)
-#define NATSEMI_REMOVE_FILE(_dev, _name) \
-         device_remove_file(&_dev->dev, &dev_attr_##_name)
-
-NATSEMI_ATTR(dspcfg_workaround);
-
-static ssize_t natsemi_show_dspcfg_workaround(struct device *dev,
-                                             struct device_attribute *attr,
-                                             char *buf)
-{
-       struct netdev_private *np = netdev_priv(to_net_dev(dev));
-
-       return sprintf(buf, "%s\n", np->dspcfg_workaround ? "on" : "off");
-}
-
-static ssize_t natsemi_set_dspcfg_workaround(struct device *dev,
-                                            struct device_attribute *attr,
-                                            const char *buf, size_t count)
-{
-       struct netdev_private *np = netdev_priv(to_net_dev(dev));
-       int new_setting;
-       unsigned long flags;
-
-        /* Find out the new setting */
-        if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
-                new_setting = 1;
-        else if (!strncmp("off", buf, count - 1) ||
-                 !strncmp("0", buf, count - 1))
-               new_setting = 0;
-       else
-                 return count;
-
-       spin_lock_irqsave(&np->lock, flags);
-
-       np->dspcfg_workaround = new_setting;
-
-       spin_unlock_irqrestore(&np->lock, flags);
-
-       return count;
-}
-
-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, independent 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;
-
-       if (np->ignore_phy)
-               netif_carrier_on(dev);
-       else
-               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 const struct net_device_ops natsemi_netdev_ops = {
-       .ndo_open               = netdev_open,
-       .ndo_stop               = netdev_close,
-       .ndo_start_xmit         = start_tx,
-       .ndo_get_stats          = get_stats,
-       .ndo_set_multicast_list = set_rx_mode,
-       .ndo_change_mtu         = natsemi_change_mtu,
-       .ndo_do_ioctl           = netdev_ioctl,
-       .ndo_tx_timeout         = ns_tx_timeout,
-       .ndo_set_mac_address    = eth_mac_addr,
-       .ndo_validate_addr      = eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       .ndo_poll_controller    = natsemi_poll_controller,
-#endif
-};
-
-static int __devinit natsemi_probe1 (struct pci_dev *pdev,
-       const struct pci_device_id *ent)
-{
-       struct net_device *dev;
-       struct netdev_private *np;
-       int i, option, irq, chip_idx = ent->driver_data;
-       static int find_cnt = -1;
-       resource_size_t iostart;
-       unsigned long iosize;
-       void __iomem *ioaddr;
-       const int pcibar = 1; /* PCI base address register */
-       int prev_eedata;
-       u32 tmp;
-
-/* when built into the kernel, we only print version if device is found */
-#ifndef MODULE
-       static int printed_version;
-       if (!printed_version++)
-               printk(version);
-#endif
-
-       i = pci_enable_device(pdev);
-       if (i) return i;
-
-       /* natsemi has a non-standard PM control register
-        * in PCI config space.  Some boards apparently need
-        * to be brought to D0 in this manner.
-        */
-       pci_read_config_dword(pdev, PCIPM, &tmp);
-       if (tmp & PCI_PM_CTRL_STATE_MASK) {
-               /* D0 state, disable PME assertion */
-               u32 newtmp = tmp & ~PCI_PM_CTRL_STATE_MASK;
-               pci_write_config_dword(pdev, PCIPM, newtmp);
-       }
-
-       find_cnt++;
-       iostart = pci_resource_start(pdev, pcibar);
-       iosize = pci_resource_len(pdev, pcibar);
-       irq = pdev->irq;
-
-       pci_set_master(pdev);
-
-       dev = alloc_etherdev(sizeof (struct netdev_private));
-       if (!dev)
-               return -ENOMEM;
-       SET_NETDEV_DEV(dev, &pdev->dev);
-
-       i = pci_request_regions(pdev, DRV_NAME);
-       if (i)
-               goto err_pci_request_regions;
-
-       ioaddr = ioremap(iostart, iosize);
-       if (!ioaddr) {
-               i = -ENOMEM;
-               goto err_ioremap;
-       }
-
-       /* Work around the dropped serial bit. */
-       prev_eedata = eeprom_read(ioaddr, 6);
-       for (i = 0; i < 3; i++) {
-               int eedata = eeprom_read(ioaddr, i + 7);
-               dev->dev_addr[i*2] = (eedata << 1) + (prev_eedata >> 15);
-               dev->dev_addr[i*2+1] = eedata >> 7;
-               prev_eedata = eedata;
-       }
-
-       /* Store MAC Address in perm_addr */
-       memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN);
-
-       dev->base_addr = (unsigned long __force) ioaddr;
-       dev->irq = irq;
-
-       np = netdev_priv(dev);
-       netif_napi_add(dev, &np->napi, natsemi_poll, 64);
-       np->dev = dev;
-
-       np->pci_dev = pdev;
-       pci_set_drvdata(pdev, dev);
-       np->iosize = iosize;
-       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;
-       if (natsemi_pci_info[chip_idx].flags & NATSEMI_FLAG_IGNORE_PHY)
-               np->ignore_phy = 1;
-       else
-               np->ignore_phy = 0;
-       np->dspcfg_workaround = dspcfg_workaround;
-
-       /* Initial port:
-        * - If configured to ignore the PHY set up for external.
-        * - 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 (np->ignore_phy || 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 we're ignoring the PHY it doesn't matter if we can't
-                * find one. */
-               if (!np->ignore_phy && 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;
-
-       /* The lower four bits are the media type. */
-       if (option) {
-               if (option & 0x200)
-                       np->full_duplex = 1;
-               if (option & 15)
-                       printk(KERN_INFO
-                               "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;
-
-       dev->netdev_ops = &natsemi_netdev_ops;
-       dev->watchdog_timeo = TX_TIMEOUT;
-
-       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;
-
-       if (NATSEMI_CREATE_FILE(pdev, dspcfg_workaround))
-               goto err_create_file;
-
-       if (netif_msg_drv(np)) {
-               printk(KERN_INFO "natsemi %s: %s at %#08llx "
-                      "(%s), %pM, IRQ %d",
-                      dev->name, natsemi_pci_info[chip_idx].name,
-                      (unsigned long long)iostart, pci_name(np->pci_dev),
-                      dev->dev_addr, irq);
-               if (dev->if_port == PORT_TP)
-                       printk(", port TP.\n");
-               else if (np->ignore_phy)
-                       printk(", port MII, ignoring PHY\n");
-               else
-                       printk(", port MII, phy ad %d.\n", np->phy_addr_external);
-       }
-       return 0;
-
- err_create_file:
-       unregister_netdev(dev);
-
- err_register_netdev:
-       iounmap(ioaddr);
-
- err_ioremap:
-       pci_release_regions(pdev);
-       pci_set_drvdata(pdev, NULL);
-
- err_pci_request_regions:
-       free_netdev(dev);
-       return i;
-}
-
-
-/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.
-   The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses. */
-
-/* Delay between EEPROM clock transitions.
-   No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
-   a delay.  Note that pre-2.0.34 kernels had a cache-alignment bug that
-   made udelay() unreliable.
-   The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
-   deprecated.
-*/
-#define eeprom_delay(ee_addr)  readl(ee_addr)
-
-#define EE_Write0 (EE_ChipSelect)
-#define EE_Write1 (EE_ChipSelect | EE_DataIn)
-
-/* The EEPROM commands include the alway-set leading bit. */
-enum EEPROM_Cmds {
-       EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
-};
-
-static int eeprom_read(void __iomem *addr, int location)
-{
-       int i;
-       int retval = 0;
-       void __iomem *ee_addr = addr + EECtrl;
-       int read_cmd = location | EE_ReadCmd;
-
-       writel(EE_Write0, ee_addr);
-
-       /* Shift the read command bits out. */
-       for (i = 10; i >= 0; i--) {
-               short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
-               writel(dataval, ee_addr);
-               eeprom_delay(ee_addr);
-               writel(dataval | EE_ShiftClk, ee_addr);
-               eeprom_delay(ee_addr);
-       }
-       writel(EE_ChipSelect, ee_addr);
-       eeprom_delay(ee_addr);
-
-       for (i = 0; i < 16; i++) {
-               writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
-               eeprom_delay(ee_addr);
-               retval |= (readl(ee_addr) & EE_DataOut) ? 1 << i : 0;
-               writel(EE_ChipSelect, ee_addr);
-               eeprom_delay(ee_addr);
-       }
-
-       /* Terminate the EEPROM access. */
-       writel(EE_Write0, ee_addr);
-       writel(0, ee_addr);
-       return retval;
-}
-
-/* MII transceiver control section.
- * The 83815 series has an internal transceiver, and we present the
- * internal management registers as if they were MII connected.
- * External Phy registers are referenced through the MII interface.
- */
-
-/* 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)
-{
-       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 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;
-}
-
-/* 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 = 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] */
-#define CFG_RESET_SAVE 0xfde000
-/* WCSR bits [0:4] [9:10] */
-#define WCSR_RESET_SAVE 0x61f
-/* RFCR bits [20] [22] [27:31] */
-#define RFCR_RESET_SAVE 0xf8500000
-
-static void natsemi_reset(struct net_device *dev)
-{
-       int i;
-       u32 cfg;
-       u32 wcsr;
-       u32 rfcr;
-       u16 pmatch[3];
-       u16 sopass[3];
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem *ioaddr = ns_ioaddr(dev);
-
-       /*
-        * Resetting the chip causes some registers to be lost.
-        * Natsemi suggests NOT reloading the EEPROM while live, so instead
-        * we save the state that would have been loaded from EEPROM
-        * on a normal power-up (see the spec EEPROM map).  This assumes
-        * whoever calls this will follow up with init_registers() eventually.
-        */
-
-       /* CFG */
-       cfg = readl(ioaddr + ChipConfig) & CFG_RESET_SAVE;
-       /* WCSR */
-       wcsr = readl(ioaddr + WOLCmd) & WCSR_RESET_SAVE;
-       /* RFCR */
-       rfcr = readl(ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
-       /* PMATCH */
-       for (i = 0; i < 3; i++) {
-               writel(i*2, ioaddr + RxFilterAddr);
-               pmatch[i] = readw(ioaddr + RxFilterData);
-       }
-       /* SOPAS */
-       for (i = 0; i < 3; i++) {
-               writel(0xa+(i*2), ioaddr + RxFilterAddr);
-               sopass[i] = readw(ioaddr + RxFilterData);
-       }
-
-       /* now whack the chip */
-       writel(ChipReset, ioaddr + ChipCmd);
-       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
-               if (!(readl(ioaddr + ChipCmd) & ChipReset))
-                       break;
-               udelay(5);
-       }
-       if (i==NATSEMI_HW_TIMEOUT) {
-               printk(KERN_WARNING "%s: reset did not complete in %d usec.\n",
-                       dev->name, i*5);
-       } else if (netif_msg_hw(np)) {
-               printk(KERN_DEBUG "%s: reset completed in %d usec.\n",
-                       dev->name, i*5);
-       }
-
-       /* restore CFG */
-       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(ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
-       writel(wcsr, ioaddr + WOLCmd);
-       /* read RFCR */
-       rfcr |= readl(ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
-       /* restore PMATCH */
-       for (i = 0; i < 3; i++) {
-               writel(i*2, ioaddr + RxFilterAddr);
-               writew(pmatch[i], ioaddr + RxFilterData);
-       }
-       for (i = 0; i < 3; i++) {
-               writel(0xa+(i*2), ioaddr + RxFilterAddr);
-               writew(sopass[i], ioaddr + RxFilterData);
-       }
-       /* restore RFCR */
-       writel(rfcr, ioaddr + RxFilterAddr);
-}
-
-static void reset_rx(struct net_device *dev)
-{
-       int i;
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem *ioaddr = ns_ioaddr(dev);
-
-       np->intr_status &= ~RxResetDone;
-
-       writel(RxReset, ioaddr + ChipCmd);
-
-       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
-               np->intr_status |= readl(ioaddr + IntrStatus);
-               if (np->intr_status & RxResetDone)
-                       break;
-               udelay(15);
-       }
-       if (i==NATSEMI_HW_TIMEOUT) {
-               printk(KERN_WARNING "%s: RX reset did not complete in %d usec.\n",
-                      dev->name, i*15);
-       } else if (netif_msg_hw(np)) {
-               printk(KERN_WARNING "%s: RX reset took %d usec.\n",
-                      dev->name, i*15);
-       }
-}
-
-static void natsemi_reload_eeprom(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem *ioaddr = ns_ioaddr(dev);
-       int i;
-
-       writel(EepromReload, ioaddr + PCIBusCfg);
-       for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
-               udelay(50);
-               if (!(readl(ioaddr + PCIBusCfg) & EepromReload))
-                       break;
-       }
-       if (i==NATSEMI_HW_TIMEOUT) {
-               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 "natsemi %s: EEPROM reloaded in %d usec.\n",
-                       pci_name(np->pci_dev), i*50);
-       }
-}
-
-static void natsemi_stop_rxtx(struct net_device *dev)
-{
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       struct netdev_private *np = netdev_priv(dev);
-       int i;
-
-       writel(RxOff | TxOff, ioaddr + ChipCmd);
-       for(i=0;i< NATSEMI_HW_TIMEOUT;i++) {
-               if ((readl(ioaddr + ChipCmd) & (TxOn|RxOn)) == 0)
-                       break;
-               udelay(5);
-       }
-       if (i==NATSEMI_HW_TIMEOUT) {
-               printk(KERN_WARNING "%s: Tx/Rx process did not stop in %d usec.\n",
-                       dev->name, i*5);
-       } else if (netif_msg_hw(np)) {
-               printk(KERN_DEBUG "%s: Tx/Rx process stopped in %d usec.\n",
-                       dev->name, i*5);
-       }
-}
-
-static int netdev_open(struct net_device *dev)
-{
-       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, IRQF_SHARED, dev->name, dev);
-       if (i) return i;
-
-       if (netif_msg_ifup(np))
-               printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
-                       dev->name, dev->irq);
-       i = alloc_ring(dev);
-       if (i < 0) {
-               free_irq(dev->irq, dev);
-               return i;
-       }
-       napi_enable(&np->napi);
-
-       init_ring(dev);
-       spin_lock_irq(&np->lock);
-       init_registers(dev);
-       /* now set the MAC address according to dev->dev_addr */
-       for (i = 0; i < 3; i++) {
-               u16 mac = (dev->dev_addr[2*i+1]<<8) + dev->dev_addr[2*i];
-
-               writel(i*2, ioaddr + RxFilterAddr);
-               writew(mac, ioaddr + RxFilterData);
-       }
-       writel(np->cur_rx_mode, ioaddr + RxFilterAddr);
-       spin_unlock_irq(&np->lock);
-
-       netif_start_queue(dev);
-
-       if (netif_msg_ifup(np))
-               printk(KERN_DEBUG "%s: Done netdev_open(), status: %#08x.\n",
-                       dev->name, (int)readl(ioaddr + ChipCmd));
-
-       /* Set the timer to check for link beat. */
-       init_timer(&np->timer);
-       np->timer.expires = round_jiffies(jiffies + NATSEMI_TIMER_FREQ);
-       np->timer.data = (unsigned long)dev;
-       np->timer.function = netdev_timer; /* timer handler */
-       add_timer(&np->timer);
-
-       return 0;
-}
-
-static void do_cable_magic(struct net_device *dev)
-{
-       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;
-
-       /*
-        * 100 MBit links with short cables can trip an issue with the chip.
-        * The problem manifests as lots of CRC errors and/or flickering
-        * activity LED while idle.  This process is based on instructions
-        * from engineers at National.
-        */
-       if (readl(ioaddr + ChipConfig) & CfgSpeed100) {
-               u16 data;
-
-               writew(1, ioaddr + PGSEL);
-               /*
-                * coefficient visibility should already be enabled via
-                * DSPCFG | 0x1000
-                */
-               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))) {
-                       np = netdev_priv(dev);
-
-                       /* the bug has been triggered - fix the coefficient */
-                       writew(TSTDAT_FIXED, ioaddr + TSTDAT);
-                       /* lock the value */
-                       data = readw(ioaddr + DSPCFG);
-                       np->dspcfg = data | DSPCFG_LOCK;
-                       writew(np->dspcfg, ioaddr + DSPCFG);
-               }
-               writew(0, ioaddr + PGSEL);
-       }
-}
-
-static void undo_cable_magic(struct net_device *dev)
-{
-       u16 data;
-       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, ioaddr + PGSEL);
-       /* make sure the lock bit is clear */
-       data = readw(ioaddr + DSPCFG);
-       np->dspcfg = data & ~DSPCFG_LOCK;
-       writew(np->dspcfg, ioaddr + DSPCFG);
-       writew(0, ioaddr + PGSEL);
-}
-
-static void check_link(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       int duplex = np->duplex;
-       u16 bmsr;
-
-       /* If we are ignoring the PHY then don't try reading it. */
-       if (np->ignore_phy)
-               goto propagate_state;
-
-       /* 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 (!(bmsr & BMSR_LSTATUS)) {
-               if (netif_carrier_ok(dev)) {
-                       if (netif_msg_link(np))
-                               printk(KERN_NOTICE "%s: link down.\n",
-                                      dev->name);
-                       netif_carrier_off(dev);
-                       undo_cable_magic(dev);
-               }
-               return;
-       }
-       if (!netif_carrier_ok(dev)) {
-               if (netif_msg_link(np))
-                       printk(KERN_NOTICE "%s: link up.\n", dev->name);
-               netif_carrier_on(dev);
-               do_cable_magic(dev);
-       }
-
-       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;
-       }
-
-propagate_state:
-       /* if duplex is set then bit 28 must be set, too */
-       if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
-               if (netif_msg_link(np))
-                       printk(KERN_INFO
-                               "%s: Setting %s-duplex based on negotiated "
-                               "link capability.\n", dev->name,
-                               duplex ? "full" : "half");
-               if (duplex) {
-                       np->rx_config |= RxAcceptTx;
-                       np->tx_config |= TxCarrierIgn | TxHeartIgn;
-               } else {
-                       np->rx_config &= ~RxAcceptTx;
-                       np->tx_config &= ~(TxCarrierIgn | TxHeartIgn);
-               }
-               writel(np->tx_config, ioaddr + TxConfig);
-               writel(np->rx_config, ioaddr + RxConfig);
-       }
-}
-
-static void init_registers(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-
-       init_phy_fixup(dev);
-
-       /* clear any interrupts that are pending, such as wake events */
-       readl(ioaddr + IntrStatus);
-
-       writel(np->ring_dma, ioaddr + RxRingPtr);
-       writel(np->ring_dma + RX_RING_SIZE * sizeof(struct netdev_desc),
-               ioaddr + TxRingPtr);
-
-       /* Initialize other registers.
-        * Configure the PCI bus bursts and FIFO thresholds.
-        * Configure for standard, in-spec Ethernet.
-        * Start with half-duplex. check_link will update
-        * to the correct settings.
-        */
-
-       /* DRTH: 2: start tx if 64 bytes are in the fifo
-        * FLTH: 0x10: refill with next packet if 512 bytes are free
-        * MXDMA: 0: up to 256 byte bursts.
-        *      MXDMA must be <= FLTH
-        * ECRETRY=1
-        * ATP=1
-        */
-       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 | 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:
-        * The PME bit is initialized from the EEPROM contents.
-        * PCI cards probably have PME disabled, but motherboard
-        * implementations may have PME set to enable WakeOnLan.
-        * With PME set the chip will scan incoming packets but
-        * nothing will be written to memory. */
-       np->SavedClkRun = readl(ioaddr + ClkRun);
-       writel(np->SavedClkRun & ~PMEEnable, ioaddr + ClkRun);
-       if (np->SavedClkRun & PMEStatus && netif_msg_wol(np)) {
-               printk(KERN_NOTICE "%s: Wake-up event %#08x\n",
-                       dev->name, readl(ioaddr + WOLCmd));
-       }
-
-       check_link(dev);
-       __set_rx_mode(dev);
-
-       /* Enable interrupts by setting the interrupt mask. */
-       writel(DEFAULT_INTR, ioaddr + IntrMask);
-       natsemi_irq_enable(dev);
-
-       writel(RxOn | TxOn, ioaddr + ChipCmd);
-       writel(StatsClear, ioaddr + StatsCtrl); /* Clear Stats */
-}
-
-/*
- * netdev_timer:
- * Purpose:
- * 1) check for link changes. Usually they are handled by the MII interrupt
- *    but it doesn't hurt to check twice.
- * 2) check for sudden death of the NIC:
- *    It seems that a reference set for this chip went out with incorrect info,
- *    and there exist boards that aren't quite right.  An unexpected voltage
- *    drop can cause the PHY to get itself in a weird state (basically reset).
- *    NOTE: this only seems to affect revC chips.  The user can disable
- *    this check via dspcfg_workaround sysfs option.
- * 3) check of death of the RX path due to OOM
- */
-static void netdev_timer(unsigned long data)
-{
-       struct net_device *dev = (struct net_device *)data;
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       int next_tick = NATSEMI_TIMER_FREQ;
-
-       if (netif_msg_timer(np)) {
-               /* DO NOT read the IntrStatus register,
-                * a read clears any pending interrupts.
-                */
-               printk(KERN_DEBUG "%s: Media selection timer tick.\n",
-                       dev->name);
-       }
-
-       if (dev->if_port == PORT_TP) {
-               u16 dspcfg;
-
-               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 (np->dspcfg_workaround && dspcfg != np->dspcfg) {
-                       if (!netif_queue_stopped(dev)) {
-                               spin_unlock_irq(&np->lock);
-                               if (netif_msg_drv(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 {
-                       /* init_registers() calls check_link() for the above case */
-                       check_link(dev);
-                       spin_unlock_irq(&np->lock);
-               }
-       } else {
-               spin_lock_irq(&np->lock);
-               check_link(dev);
-               spin_unlock_irq(&np->lock);
-       }
-       if (np->oom) {
-               disable_irq(dev->irq);
-               np->oom = 0;
-               refill_rx(dev);
-               enable_irq(dev->irq);
-               if (!np->oom) {
-                       writel(RxOn, ioaddr + ChipCmd);
-               } else {
-                       next_tick = 1;
-               }
-       }
-
-       if (next_tick > 1)
-               mod_timer(&np->timer, round_jiffies(jiffies + next_tick));
-       else
-               mod_timer(&np->timer, jiffies + next_tick);
-}
-
-static void dump_ring(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-
-       if (netif_msg_pktdata(np)) {
-               int i;
-               printk(KERN_DEBUG "  Tx ring at %p:\n", np->tx_ring);
-               for (i = 0; i < TX_RING_SIZE; i++) {
-                       printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
-                               i, np->tx_ring[i].next_desc,
-                               np->tx_ring[i].cmd_status,
-                               np->tx_ring[i].addr);
-               }
-               printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
-               for (i = 0; i < RX_RING_SIZE; i++) {
-                       printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
-                               i, np->rx_ring[i].next_desc,
-                               np->rx_ring[i].cmd_status,
-                               np->rx_ring[i].addr);
-               }
-       }
-}
-
-static void ns_tx_timeout(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-
-       disable_irq(dev->irq);
-       spin_lock_irq(&np->lock);
-       if (!np->hands_off) {
-               if (netif_msg_tx_err(np))
-                       printk(KERN_WARNING
-                               "%s: Transmit timed out, status %#08x,"
-                               " resetting...\n",
-                               dev->name, readl(ioaddr + IntrStatus));
-               dump_ring(dev);
-
-               natsemi_reset(dev);
-               reinit_ring(dev);
-               init_registers(dev);
-       } else {
-               printk(KERN_WARNING
-                       "%s: tx_timeout while in hands_off state?\n",
-                       dev->name);
-       }
-       spin_unlock_irq(&np->lock);
-       enable_irq(dev->irq);
-
-       dev->trans_start = jiffies; /* prevent tx timeout */
-       dev->stats.tx_errors++;
-       netif_wake_queue(dev);
-}
-
-static int alloc_ring(struct net_device *dev)
-{
-       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);
-       if (!np->rx_ring)
-               return -ENOMEM;
-       np->tx_ring = &np->rx_ring[RX_RING_SIZE];
-       return 0;
-}
-
-static void refill_rx(struct net_device *dev)
-{
-       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+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->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);
-       }
-       if (np->cur_rx - np->dirty_rx == RX_RING_SIZE) {
-               if (netif_msg_rx_err(np))
-                       printk(KERN_WARNING "%s: going OOM.\n", dev->name);
-               np->oom = 1;
-       }
-}
-
-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 = netdev_priv(dev);
-       int i;
-
-       /* 1) TX ring */
-       np->dirty_tx = np->cur_tx = 0;
-       for (i = 0; i < TX_RING_SIZE; i++) {
-               np->tx_skbuff[i] = NULL;
-               np->tx_ring[i].next_desc = cpu_to_le32(np->ring_dma
-                       +sizeof(struct netdev_desc)
-                       *((i+1)%TX_RING_SIZE+RX_RING_SIZE));
-               np->tx_ring[i].cmd_status = 0;
-       }
-
-       /* 2) RX ring */
-       np->dirty_rx = 0;
-       np->cur_rx = RX_RING_SIZE;
-       np->oom = 0;
-       set_bufsize(dev);
-
-       np->rx_head_desc = &np->rx_ring[0];
-
-       /* Please be careful before changing this loop - at least gcc-2.95.1
-        * miscompiles it otherwise.
-        */
-       /* Initialize all Rx descriptors. */
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               np->rx_ring[i].next_desc = cpu_to_le32(np->ring_dma
-                               +sizeof(struct netdev_desc)
-                               *((i+1)%RX_RING_SIZE));
-               np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
-               np->rx_skbuff[i] = NULL;
-       }
-       refill_rx(dev);
-       dump_ring(dev);
-}
-
-static void drain_tx(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       int i;
-
-       for (i = 0; i < TX_RING_SIZE; i++) {
-               if (np->tx_skbuff[i]) {
-                       pci_unmap_single(np->pci_dev,
-                               np->tx_dma[i], np->tx_skbuff[i]->len,
-                               PCI_DMA_TODEVICE);
-                       dev_kfree_skb(np->tx_skbuff[i]);
-                       dev->stats.tx_dropped++;
-               }
-               np->tx_skbuff[i] = NULL;
-       }
-}
-
-static void drain_rx(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       unsigned int buflen = np->rx_buf_sz;
-       int i;
-
-       /* Free all the skbuffs in the Rx queue. */
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               np->rx_ring[i].cmd_status = 0;
-               np->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
-               if (np->rx_skbuff[i]) {
-                       pci_unmap_single(np->pci_dev, np->rx_dma[i],
-                               buflen + NATSEMI_PADDING,
-                               PCI_DMA_FROMDEVICE);
-                       dev_kfree_skb(np->rx_skbuff[i]);
-               }
-               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 = 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_rx(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       int i;
-
-       /* RX Ring */
-       np->dirty_rx = 0;
-       np->cur_rx = RX_RING_SIZE;
-       np->rx_head_desc = &np->rx_ring[0];
-       /* Initialize all Rx descriptors. */
-       for (i = 0; i < RX_RING_SIZE; i++)
-               np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
-
-       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 netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       unsigned entry;
-       unsigned long flags;
-
-       /* Note: Ordering is important here, set the field with the
-          "ownership" bit last, and only then increment cur_tx. */
-
-       /* Calculate the next Tx descriptor entry. */
-       entry = np->cur_tx % TX_RING_SIZE;
-
-       np->tx_skbuff[entry] = skb;
-       np->tx_dma[entry] = pci_map_single(np->pci_dev,
-                               skb->data,skb->len, PCI_DMA_TODEVICE);
-
-       np->tx_ring[entry].addr = cpu_to_le32(np->tx_dma[entry]);
-
-       spin_lock_irqsave(&np->lock, flags);
-
-       if (!np->hands_off) {
-               np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn | skb->len);
-               /* StrongARM: Explicitly cache flush np->tx_ring and
-                * skb->data,skb->len. */
-               wmb();
-               np->cur_tx++;
-               if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) {
-                       netdev_tx_done(dev);
-                       if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1)
-                               netif_stop_queue(dev);
-               }
-               /* Wake the potentially-idle transmit channel. */
-               writel(TxOn, ioaddr + ChipCmd);
-       } else {
-               dev_kfree_skb_irq(skb);
-               dev->stats.tx_dropped++;
-       }
-       spin_unlock_irqrestore(&np->lock, flags);
-
-       if (netif_msg_tx_queued(np)) {
-               printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
-                       dev->name, np->cur_tx, entry);
-       }
-       return NETDEV_TX_OK;
-}
-
-static void netdev_tx_done(struct net_device *dev)
-{
-       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;
-               if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescOwn))
-                       break;
-               if (netif_msg_tx_done(np))
-                       printk(KERN_DEBUG
-                               "%s: tx frame #%d finished, status %#08x.\n",
-                                       dev->name, np->dirty_tx,
-                                       le32_to_cpu(np->tx_ring[entry].cmd_status));
-               if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescPktOK)) {
-                       dev->stats.tx_packets++;
-                       dev->stats.tx_bytes += np->tx_skbuff[entry]->len;
-               } else { /* Various Tx errors */
-                       int tx_status =
-                               le32_to_cpu(np->tx_ring[entry].cmd_status);
-                       if (tx_status & (DescTxAbort|DescTxExcColl))
-                               dev->stats.tx_aborted_errors++;
-                       if (tx_status & DescTxFIFO)
-                               dev->stats.tx_fifo_errors++;
-                       if (tx_status & DescTxCarrier)
-                               dev->stats.tx_carrier_errors++;
-                       if (tx_status & DescTxOOWCol)
-                               dev->stats.tx_window_errors++;
-                       dev->stats.tx_errors++;
-               }
-               pci_unmap_single(np->pci_dev,np->tx_dma[entry],
-                                       np->tx_skbuff[entry]->len,
-                                       PCI_DMA_TODEVICE);
-               /* Free the original skb. */
-               dev_kfree_skb_irq(np->tx_skbuff[entry]);
-               np->tx_skbuff[entry] = NULL;
-       }
-       if (netif_queue_stopped(dev) &&
-           np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
-               /* The ring is no longer full, wake queue. */
-               netif_wake_queue(dev);
-       }
-}
-
-/* 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 = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-
-       /* Reading IntrStatus automatically acknowledges so don't do
-        * that while interrupts are disabled, (for example, while a
-        * poll is scheduled).  */
-       if (np->hands_off || !readl(ioaddr + IntrEnable))
-               return IRQ_NONE;
-
-       np->intr_status = readl(ioaddr + IntrStatus);
-
-       if (!np->intr_status)
-               return IRQ_NONE;
-
-       if (netif_msg_intr(np))
-               printk(KERN_DEBUG
-                      "%s: Interrupt, status %#08x, mask %#08x.\n",
-                      dev->name, np->intr_status,
-                      readl(ioaddr + IntrMask));
-
-       prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
-
-       if (napi_schedule_prep(&np->napi)) {
-               /* Disable interrupts and register for poll */
-               natsemi_irq_disable(dev);
-               __napi_schedule(&np->napi);
-       } else
-               printk(KERN_WARNING
-                      "%s: Ignoring interrupt, status %#08x, mask %#08x.\n",
-                      dev->name, np->intr_status,
-                      readl(ioaddr + IntrMask));
-
-       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 napi_struct *napi, int budget)
-{
-       struct netdev_private *np = container_of(napi, struct netdev_private, napi);
-       struct net_device *dev = np->dev;
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       int work_done = 0;
-
-       do {
-               if (netif_msg_intr(np))
-                       printk(KERN_DEBUG
-                              "%s: Poll, status %#08x, mask %#08x.\n",
-                              dev->name, np->intr_status,
-                              readl(ioaddr + IntrMask));
-
-               /* netdev_rx() may read IntrStatus again if the RX state
-                * machine falls over so do it first. */
-               if (np->intr_status &
-                   (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
-                    IntrRxErr | IntrRxOverrun)) {
-                       netdev_rx(dev, &work_done, budget);
-               }
-
-               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 (np->intr_status & IntrAbnormalSummary)
-                       netdev_error(dev, np->intr_status);
-
-               if (work_done >= budget)
-                       return work_done;
-
-               np->intr_status = readl(ioaddr + IntrStatus);
-       } while (np->intr_status);
-
-       napi_complete(napi);
-
-       /* Reenable interrupts providing nothing is trying to shut
-        * the chip down. */
-       spin_lock(&np->lock);
-       if (!np->hands_off)
-               natsemi_irq_enable(dev);
-       spin_unlock(&np->lock);
-
-       return work_done;
-}
-
-/* This routine is logically part of the interrupt handler, but separated
-   for clarity and better register allocation. */
-static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
-{
-       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;
-       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) {
-                               unsigned long flags;
-
-                               if (netif_msg_rx_err(np))
-                                       printk(KERN_WARNING
-                                               "%s: Oversized(?) Ethernet "
-                                               "frame spanned multiple "
-                                               "buffers, entry %#08x "
-                                               "status %#08x.\n", dev->name,
-                                               np->cur_rx, desc_status);
-                               dev->stats.rx_length_errors++;
-
-                               /* The RX state machine has probably
-                                * locked up beneath us.  Follow the
-                                * reset procedure documented in
-                                * AN-1287. */
-
-                               spin_lock_irqsave(&np->lock, flags);
-                               reset_rx(dev);
-                               reinit_rx(dev);
-                               writel(np->ring_dma, ioaddr + RxRingPtr);
-                               check_link(dev);
-                               spin_unlock_irqrestore(&np->lock, flags);
-
-                               /* We'll enable RX on exit from this
-                                * function. */
-                               break;
-
-                       } else {
-                               /* There was an error. */
-                               dev->stats.rx_errors++;
-                               if (desc_status & (DescRxAbort|DescRxOver))
-                                       dev->stats.rx_over_errors++;
-                               if (desc_status & (DescRxLong|DescRxRunt))
-                                       dev->stats.rx_length_errors++;
-                               if (desc_status & (DescRxInvalid|DescRxAlign))
-                                       dev->stats.rx_frame_errors++;
-                               if (desc_status & DescRxCRC)
-                                       dev->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. */
-                       /* Check if the packet is long enough to accept
-                        * without copying to a minimally-sized skbuff. */
-                       if (pkt_len < rx_copybreak &&
-                           (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
-                               /* 16 byte align the IP header */
-                               skb_reserve(skb, RX_OFFSET);
-                               pci_dma_sync_single_for_cpu(np->pci_dev,
-                                       np->rx_dma[entry],
-                                       buflen,
-                                       PCI_DMA_FROMDEVICE);
-                               skb_copy_to_linear_data(skb,
-                                       np->rx_skbuff[entry]->data, pkt_len);
-                               skb_put(skb, pkt_len);
-                               pci_dma_sync_single_for_device(np->pci_dev,
-                                       np->rx_dma[entry],
-                                       buflen,
-                                       PCI_DMA_FROMDEVICE);
-                       } else {
-                               pci_unmap_single(np->pci_dev, np->rx_dma[entry],
-                                                buflen + NATSEMI_PADDING,
-                                                PCI_DMA_FROMDEVICE);
-                               skb_put(skb = np->rx_skbuff[entry], pkt_len);
-                               np->rx_skbuff[entry] = NULL;
-                       }
-                       skb->protocol = eth_type_trans(skb, dev);
-                       netif_receive_skb(skb);
-                       dev->stats.rx_packets++;
-                       dev->stats.rx_bytes += pkt_len;
-               }
-               entry = (++np->cur_rx) % RX_RING_SIZE;
-               np->rx_head_desc = &np->rx_ring[entry];
-               desc_status = le32_to_cpu(np->rx_head_desc->cmd_status);
-       }
-       refill_rx(dev);
-
-       /* Restart Rx engine if stopped. */
-       if (np->oom)
-               mod_timer(&np->timer, jiffies + 1);
-       else
-               writel(RxOn, ioaddr + ChipCmd);
-}
-
-static void netdev_error(struct net_device *dev, int intr_status)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-
-       spin_lock(&np->lock);
-       if (intr_status & LinkChange) {
-               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,
-                               np->advertising, lpa);
-               }
-
-               /* read MII int status to clear the flag */
-               readw(ioaddr + MIntrStatus);
-               check_link(dev);
-       }
-       if (intr_status & StatsMax) {
-               __get_stats(dev);
-       }
-       if (intr_status & IntrTxUnderrun) {
-               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)) {
-               int wol_status = readl(ioaddr + WOLCmd);
-               printk(KERN_NOTICE "%s: Link wake-up event %#08x\n",
-                       dev->name, wol_status);
-       }
-       if (intr_status & RxStatusFIFOOver) {
-               if (netif_msg_rx_err(np) && netif_msg_intr(np)) {
-                       printk(KERN_NOTICE "%s: Rx status FIFO overrun\n",
-                               dev->name);
-               }
-               dev->stats.rx_fifo_errors++;
-               dev->stats.rx_errors++;
-       }
-       /* Hmmmmm, it's not clear how to recover from PCI faults. */
-       if (intr_status & IntrPCIErr) {
-               printk(KERN_NOTICE "%s: PCI error %#08x\n", dev->name,
-                       intr_status & IntrPCIErr);
-               dev->stats.tx_fifo_errors++;
-               dev->stats.tx_errors++;
-               dev->stats.rx_fifo_errors++;
-               dev->stats.rx_errors++;
-       }
-       spin_unlock(&np->lock);
-}
-
-static void __get_stats(struct net_device *dev)
-{
-       void __iomem * ioaddr = ns_ioaddr(dev);
-
-       /* The chip only need report frame silently dropped. */
-       dev->stats.rx_crc_errors += readl(ioaddr + RxCRCErrs);
-       dev->stats.rx_missed_errors += readl(ioaddr + RxMissed);
-}
-
-static struct net_device_stats *get_stats(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-
-       /* The chip only need report frame silently dropped. */
-       spin_lock_irq(&np->lock);
-       if (netif_running(dev) && !np->hands_off)
-               __get_stats(dev);
-       spin_unlock_irq(&np->lock);
-
-       return &dev->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)
-{
-       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. */
-               rx_mode = RxFilterEnable | AcceptBroadcast
-                       | AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
-       } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
-                  (dev->flags & IFF_ALLMULTI)) {
-               rx_mode = RxFilterEnable | AcceptBroadcast
-                       | AcceptAllMulticast | AcceptMyPhys;
-       } else {
-               struct netdev_hw_addr *ha;
-               int i;
-
-               memset(mc_filter, 0, sizeof(mc_filter));
-               netdev_for_each_mc_addr(ha, dev) {
-                       int b = (ether_crc(ETH_ALEN, ha->addr) >> 23) & 0x1ff;
-                       mc_filter[b/8] |= (1 << (b & 0x07));
-               }
-               rx_mode = RxFilterEnable | AcceptBroadcast
-                       | AcceptMulticast | AcceptMyPhys;
-               for (i = 0; i < 64; i += 2) {
-                       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 = netdev_priv(dev);
-       spin_lock_irq(&np->lock);
-       if (!np->hands_off)
-               __set_rx_mode(dev);
-       spin_unlock_irq(&np->lock);
-}
-
-static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
-       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);
-}
-
-static int get_regs_len(struct net_device *dev)
-{
-       return NATSEMI_REGS_SIZE;
-}
-
-static int get_eeprom_len(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       return np->eeprom_size;
-}
-
-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;
-}
-
-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;
-}
-
-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);
-}
-
-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;
-}
-
-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);
-}
-
-static u32 get_msglevel(struct net_device *dev)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       return np->msg_enable;
-}
-
-static void set_msglevel(struct net_device *dev, u32 val)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       np->msg_enable = val;
-}
-
-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;
-}
-
-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;
-}
-
-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;
-
-       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 = 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)
-               data |= WakePhy;
-       if (newval & WAKE_UCAST)
-               data |= WakeUnicast;
-       if (newval & WAKE_MCAST)
-               data |= WakeMulticast;
-       if (newval & WAKE_BCAST)
-               data |= WakeBroadcast;
-       if (newval & WAKE_ARP)
-               data |= WakeArp;
-       if (newval & WAKE_MAGIC)
-               data |= WakeMagic;
-       if (np->srr >= SRR_DP83815_D) {
-               if (newval & WAKE_MAGICSECURE) {
-                       data |= WakeMagicSecure;
-               }
-       }
-
-       writel(data, ioaddr + WOLCmd);
-
-       return 0;
-}
-
-static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur)
-{
-       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);
-
-       if (np->srr >= SRR_DP83815_D) {
-               /* SOPASS works on revD and higher */
-               *supported |= WAKE_MAGICSECURE;
-       }
-       *cur = 0;
-
-       /* translate from chip bitmasks */
-       if (regval & WakePhy)
-               *cur |= WAKE_PHY;
-       if (regval & WakeUnicast)
-               *cur |= WAKE_UCAST;
-       if (regval & WakeMulticast)
-               *cur |= WAKE_MCAST;
-       if (regval & WakeBroadcast)
-               *cur |= WAKE_BCAST;
-       if (regval & WakeArp)
-               *cur |= WAKE_ARP;
-       if (regval & WakeMagic)
-               *cur |= WAKE_MAGIC;
-       if (regval & WakeMagicSecure) {
-               /* this can be on in revC, but it's broken */
-               *cur |= WAKE_MAGICSECURE;
-       }
-
-       return 0;
-}
-
-static int netdev_set_sopass(struct net_device *dev, u8 *newval)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       u16 *sval = (u16 *)newval;
-       u32 addr;
-
-       if (np->srr < SRR_DP83815_D) {
-               return 0;
-       }
-
-       /* enable writing to these registers by disabling the RX filter */
-       addr = readl(ioaddr + RxFilterAddr) & ~RFCRAddressMask;
-       addr &= ~RxFilterEnable;
-       writel(addr, ioaddr + RxFilterAddr);
-
-       /* write the three words to (undocumented) RFCR vals 0xa, 0xc, 0xe */
-       writel(addr | 0xa, ioaddr + RxFilterAddr);
-       writew(sval[0], ioaddr + RxFilterData);
-
-       writel(addr | 0xc, ioaddr + RxFilterAddr);
-       writew(sval[1], ioaddr + RxFilterData);
-
-       writel(addr | 0xe, ioaddr + RxFilterAddr);
-       writew(sval[2], ioaddr + RxFilterData);
-
-       /* re-enable the RX filter */
-       writel(addr | RxFilterEnable, ioaddr + RxFilterAddr);
-
-       return 0;
-}
-
-static int netdev_get_sopass(struct net_device *dev, u8 *data)
-{
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       u16 *sval = (u16 *)data;
-       u32 addr;
-
-       if (np->srr < SRR_DP83815_D) {
-               sval[0] = sval[1] = sval[2] = 0;
-               return 0;
-       }
-
-       /* read the three words from (undocumented) RFCR vals 0xa, 0xc, 0xe */
-       addr = readl(ioaddr + RxFilterAddr) & ~RFCRAddressMask;
-
-       writel(addr | 0xa, ioaddr + RxFilterAddr);
-       sval[0] = readw(ioaddr + RxFilterData);
-
-       writel(addr | 0xc, ioaddr + RxFilterAddr);
-       sval[1] = readw(ioaddr + RxFilterData);
-
-       writel(addr | 0xe, ioaddr + RxFilterAddr);
-       sval[2] = readw(ioaddr + RxFilterData);
-
-       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->port        = dev->if_port;
-       ethtool_cmd_speed_set(ecmd, np->speed);
-       ecmd->duplex      = np->duplex;
-       ecmd->autoneg     = np->autoneg;
-       ecmd->advertising = 0;
-       if (np->advertising & ADVERTISE_10HALF)
-               ecmd->advertising |= ADVERTISED_10baseT_Half;
-       if (np->advertising & ADVERTISE_10FULL)
-               ecmd->advertising |= ADVERTISED_10baseT_Full;
-       if (np->advertising & ADVERTISE_100HALF)
-               ecmd->advertising |= ADVERTISED_100baseT_Half;
-       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...
-        */
-
-       /* 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;
-               tmp = mii_nway_result(
-                       np->advertising & mdio_read(dev, MII_LPA));
-               if (tmp == LPA_100FULL || tmp == LPA_100HALF)
-                       ethtool_cmd_speed_set(ecmd, SPEED_100);
-               else
-                       ethtool_cmd_speed_set(ecmd, SPEED_10);
-               if (tmp == LPA_100FULL || tmp == LPA_10FULL)
-                       ecmd->duplex = DUPLEX_FULL;
-               else
-                       ecmd->duplex = DUPLEX_HALF;
-       }
-
-       /* ignore maxtxpkt, maxrxpkt for now */
-
-       return 0;
-}
-
-static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
-{
-       struct netdev_private *np = netdev_priv(dev);
-
-       if (ecmd->port != PORT_TP && ecmd->port != PORT_MII && ecmd->port != PORT_FIBRE)
-               return -EINVAL;
-       if (ecmd->transceiver != XCVR_INTERNAL && ecmd->transceiver != XCVR_EXTERNAL)
-               return -EINVAL;
-       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) {
-               u32 speed = ethtool_cmd_speed(ecmd);
-               if (speed != SPEED_10 && speed != SPEED_100)
-                       return -EINVAL;
-               if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
-                       return -EINVAL;
-       } else {
-               return -EINVAL;
-       }
-
-       /*
-        * If we're ignoring the PHY then autoneg and the internal
-        * transceiver are really not going to work so don't let the
-        * user select them.
-        */
-       if (np->ignore_phy && (ecmd->autoneg == AUTONEG_ENABLE ||
-                              ecmd->port == PORT_TP))
-               return -EINVAL;
-
-       /*
-        * 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 */
-
-       /* 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 {
-               np->speed  = ethtool_cmd_speed(ecmd);
-               np->duplex = ecmd->duplex;
-               /* user overriding the initial full duplex parm? */
-               if (np->duplex == DUPLEX_HALF)
-                       np->full_duplex = 0;
-       }
-
-       /* 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;
-}
-
-static int netdev_get_regs(struct net_device *dev, u8 *buf)
-{
-       int i;
-       int j;
-       u32 rfcr;
-       u32 *rbuf = (u32 *)buf;
-       void __iomem * ioaddr = ns_ioaddr(dev);
-
-       /* 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, 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(ioaddr + RxFilterAddr);
-       for (j = 0; j < NATSEMI_RFDR_NREGS; j++) {
-               writel(j*2, ioaddr + RxFilterAddr);
-               rbuf[i++] = readw(ioaddr + RxFilterData);
-       }
-       writel(rfcr, ioaddr + RxFilterAddr);
-
-       /* the interrupt status is clear-on-read - see if we missed any */
-       if (rbuf[4] & rbuf[5]) {
-               printk(KERN_WARNING
-                       "%s: shoot, we dropped an interrupt (%#08x)\n",
-                       dev->name, rbuf[4] & rbuf[5]);
-       }
-
-       return 0;
-}
-
-#define SWAP_BITS(x)   ( (((x) & 0x0001) << 15) | (((x) & 0x0002) << 13) \
-                       | (((x) & 0x0004) << 11) | (((x) & 0x0008) << 9)  \
-                       | (((x) & 0x0010) << 7)  | (((x) & 0x0020) << 5)  \
-                       | (((x) & 0x0040) << 3)  | (((x) & 0x0080) << 1)  \
-                       | (((x) & 0x0100) >> 1)  | (((x) & 0x0200) >> 3)  \
-                       | (((x) & 0x0400) >> 5)  | (((x) & 0x0800) >> 7)  \
-                       | (((x) & 0x1000) >> 9)  | (((x) & 0x2000) >> 11) \
-                       | (((x) & 0x4000) >> 13) | (((x) & 0x8000) >> 15) )
-
-static int netdev_get_eeprom(struct net_device *dev, u8 *buf)
-{
-       int i;
-       u16 *ebuf = (u16 *)buf;
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       struct netdev_private *np = netdev_priv(dev);
-
-       /* eeprom_read reads 16 bits, and indexes by 16 bits */
-       for (i = 0; i < np->eeprom_size/2; i++) {
-               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. */
-               ebuf[i] = SWAP_BITS(ebuf[i]);
-       }
-       return 0;
-}
-
-static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-       struct mii_ioctl_data *data = if_mii(rq);
-       struct netdev_private *np = netdev_priv(dev);
-
-       switch(cmd) {
-       case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
-               data->phy_id = np->phy_addr_external;
-               /* Fall Through */
-
-       case SIOCGMIIREG:               /* Read MII PHY register. */
-               /* 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. */
-               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;
-       }
-}
-
-static void enable_wol_mode(struct net_device *dev, int enable_intr)
-{
-       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",
-                       dev->name);
-
-       /* For WOL we must restart the rx process in silent mode.
-        * Write NULL to the RxRingPtr. Only possible if
-        * rx process is stopped
-        */
-       writel(0, ioaddr + RxRingPtr);
-
-       /* read WoL status to clear */
-       readl(ioaddr + WOLCmd);
-
-       /* PME on, clear status */
-       writel(np->SavedClkRun | PMEEnable | PMEStatus, ioaddr + ClkRun);
-
-       /* and restart the rx process */
-       writel(RxOn, ioaddr + ChipCmd);
-
-       if (enable_intr) {
-               /* enable the WOL interrupt.
-                * Could be used to send a netlink message.
-                */
-               writel(WOLPkt | LinkChange, ioaddr + IntrMask);
-               natsemi_irq_enable(dev);
-       }
-}
-
-static int netdev_close(struct net_device *dev)
-{
-       void __iomem * ioaddr = ns_ioaddr(dev);
-       struct netdev_private *np = netdev_priv(dev);
-
-       if (netif_msg_ifdown(np))
-               printk(KERN_DEBUG
-                       "%s: Shutting down ethercard, status was %#04x.\n",
-                       dev->name, (int)readl(ioaddr + ChipCmd));
-       if (netif_msg_pktdata(np))
-               printk(KERN_DEBUG
-                       "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
-                       dev->name, np->cur_tx, np->dirty_tx,
-                       np->cur_rx, np->dirty_rx);
-
-       napi_disable(&np->napi);
-
-       /*
-        * FIXME: what if someone tries to close a device
-        * that is suspended?
-        * Should we reenable the nic to switch to
-        * the final WOL settings?
-        */
-
-       del_timer_sync(&np->timer);
-       disable_irq(dev->irq);
-       spin_lock_irq(&np->lock);
-       natsemi_irq_disable(dev);
-       np->hands_off = 1;
-       spin_unlock_irq(&np->lock);
-       enable_irq(dev->irq);
-
-       free_irq(dev->irq, dev);
-
-       /* Interrupt disabled, interrupt handler released,
-        * queue stopped, timer deleted, rtnl_lock held
-        * All async codepaths that access the driver are disabled.
-        */
-       spin_lock_irq(&np->lock);
-       np->hands_off = 0;
-       readl(ioaddr + IntrMask);
-       readw(ioaddr + MIntrStatus);
-
-       /* Freeze Stats */
-       writel(StatsFreeze, ioaddr + StatsCtrl);
-
-       /* Stop the chip's Tx and Rx processes. */
-       natsemi_stop_rxtx(dev);
-
-       __get_stats(dev);
-       spin_unlock_irq(&np->lock);
-
-       /* clear the carrier last - an interrupt could reenable it otherwise */
-       netif_carrier_off(dev);
-       netif_stop_queue(dev);
-
-       dump_ring(dev);
-       drain_ring(dev);
-       free_ring(dev);
-
-       {
-               u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
-               if (wol) {
-                       /* restart the NIC in WOL mode.
-                        * The nic must be stopped for this.
-                        */
-                       enable_wol_mode(dev, 0);
-               } else {
-                       /* Restore PME enable bit unmolested */
-                       writel(np->SavedClkRun, ioaddr + ClkRun);
-               }
-       }
-       return 0;
-}
-
-
-static void __devexit natsemi_remove1 (struct pci_dev *pdev)
-{
-       struct net_device *dev = pci_get_drvdata(pdev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-
-       NATSEMI_REMOVE_FILE(pdev, dspcfg_workaround);
-       unregister_netdev (dev);
-       pci_release_regions (pdev);
-       iounmap(ioaddr);
-       free_netdev (dev);
-       pci_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-
-/*
- * The ns83815 chip doesn't have explicit RxStop bits.
- * Kicking the Rx or Tx process for a new packet reenables the Rx process
- * of the nic, thus this function must be very careful:
- *
- * suspend/resume synchronization:
- * entry points:
- *   netdev_open, netdev_close, netdev_ioctl, set_rx_mode, intr_handler,
- *   start_tx, ns_tx_timeout
- *
- * No function accesses the hardware without checking np->hands_off.
- *     the check occurs under spin_lock_irq(&np->lock);
- * exceptions:
- *     * netdev_ioctl: noncritical access.
- *     * netdev_open: cannot happen due to the device_detach
- *     * netdev_close: doesn't hurt.
- *     * 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
- *              napi_disable().
- *
- * Interrupts must be disabled, otherwise hands_off can cause irq storms.
- */
-
-static int natsemi_suspend (struct pci_dev *pdev, pm_message_t state)
-{
-       struct net_device *dev = pci_get_drvdata (pdev);
-       struct netdev_private *np = netdev_priv(dev);
-       void __iomem * ioaddr = ns_ioaddr(dev);
-
-       rtnl_lock();
-       if (netif_running (dev)) {
-               del_timer_sync(&np->timer);
-
-               disable_irq(dev->irq);
-               spin_lock_irq(&np->lock);
-
-               natsemi_irq_disable(dev);
-               np->hands_off = 1;
-               natsemi_stop_rxtx(dev);
-               netif_stop_queue(dev);
-
-               spin_unlock_irq(&np->lock);
-               enable_irq(dev->irq);
-
-               napi_disable(&np->napi);
-
-               /* Update the error counts. */
-               __get_stats(dev);
-
-               /* pci_power_off(pdev, -1); */
-               drain_ring(dev);
-               {
-                       u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
-                       /* Restore PME enable bit */
-                       if (wol) {
-                               /* restart the NIC in WOL mode.
-                                * The nic must be stopped for this.
-                                * FIXME: use the WOL interrupt
-                                */
-                               enable_wol_mode(dev, 0);
-                       } else {
-                               /* Restore PME enable bit unmolested */
-                               writel(np->SavedClkRun, ioaddr + ClkRun);
-                       }
-               }
-       }
-       netif_device_detach(dev);
-       rtnl_unlock();
-       return 0;
-}
-
-
-static int natsemi_resume (struct pci_dev *pdev)
-{
-       struct net_device *dev = pci_get_drvdata (pdev);
-       struct netdev_private *np = netdev_priv(dev);
-       int ret = 0;
-
-       rtnl_lock();
-       if (netif_device_present(dev))
-               goto out;
-       if (netif_running(dev)) {
-               BUG_ON(!np->hands_off);
-               ret = pci_enable_device(pdev);
-               if (ret < 0) {
-                       dev_err(&pdev->dev,
-                               "pci_enable_device() failed: %d\n", ret);
-                       goto out;
-               }
-       /*      pci_power_on(pdev); */
-
-               napi_enable(&np->napi);
-
-               natsemi_reset(dev);
-               init_ring(dev);
-               disable_irq(dev->irq);
-               spin_lock_irq(&np->lock);
-               np->hands_off = 0;
-               init_registers(dev);
-               netif_device_attach(dev);
-               spin_unlock_irq(&np->lock);
-               enable_irq(dev->irq);
-
-               mod_timer(&np->timer, round_jiffies(jiffies + 1*HZ));
-       }
-       netif_device_attach(dev);
-out:
-       rtnl_unlock();
-       return ret;
-}
-
-#endif /* CONFIG_PM */
-
-static struct pci_driver natsemi_driver = {
-       .name           = DRV_NAME,
-       .id_table       = natsemi_pci_tbl,
-       .probe          = natsemi_probe1,
-       .remove         = __devexit_p(natsemi_remove1),
-#ifdef CONFIG_PM
-       .suspend        = natsemi_suspend,
-       .resume         = natsemi_resume,
-#endif
-};
-
-static int __init natsemi_init_mod (void)
-{
-/* when a module, this is printed whether or not devices are found in probe */
-#ifdef MODULE
-       printk(version);
-#endif
-
-       return pci_register_driver(&natsemi_driver);
-}
-
-static void __exit natsemi_exit_mod (void)
-{
-       pci_unregister_driver (&natsemi_driver);
-}
-
-module_init(natsemi_init_mod);
-module_exit(natsemi_exit_mod);
-
diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c
deleted file mode 100644 (file)
index e736aec..0000000
+++ /dev/null
@@ -1,2312 +0,0 @@
-#define VERSION "0.23"
-/* ns83820.c by Benjamin LaHaise with contributions.
- *
- * Questions/comments/discussion to linux-ns83820@kvack.org.
- *
- * $Revision: 1.34.2.23 $
- *
- * Copyright 2001 Benjamin LaHaise.
- * Copyright 2001, 2002 Red Hat.
- *
- * Mmmm, chocolate vanilla mocha...
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *
- * ChangeLog
- * =========
- *     20010414        0.1 - created
- *     20010622        0.2 - basic rx and tx.
- *     20010711        0.3 - added duplex and link state detection support.
- *     20010713        0.4 - zero copy, no hangs.
- *                     0.5 - 64 bit dma support (davem will hate me for this)
- *                         - disable jumbo frames to avoid tx hangs
- *                         - work around tx deadlocks on my 1.02 card via
- *                           fiddling with TXCFG
- *     20010810        0.6 - use pci dma api for ringbuffers, work on ia64
- *     20010816        0.7 - misc cleanups
- *     20010826        0.8 - fix critical zero copy bugs
- *                     0.9 - internal experiment
- *     20010827        0.10 - fix ia64 unaligned access.
- *     20010906        0.11 - accept all packets with checksum errors as
- *                            otherwise fragments get lost
- *                          - fix >> 32 bugs
- *                     0.12 - add statistics counters
- *                          - add allmulti/promisc support
- *     20011009        0.13 - hotplug support, other smaller pci api cleanups
- *     20011204        0.13a - optical transceiver support added
- *                             by Michael Clark <michael@metaparadigm.com>
- *     20011205        0.13b - call register_netdev earlier in initialization
- *                             suppress duplicate link status messages
- *     20011117        0.14 - ethtool GDRVINFO, GLINK support from jgarzik
- *     20011204        0.15    get ppc (big endian) working
- *     20011218        0.16    various cleanups
- *     20020310        0.17    speedups
- *     20020610        0.18 -  actually use the pci dma api for highmem
- *                          -  remove pci latency register fiddling
- *                     0.19 -  better bist support
- *                          -  add ihr and reset_phy parameters
- *                          -  gmii bus probing
- *                          -  fix missed txok introduced during performance
- *                             tuning
- *                     0.20 -  fix stupid RFEN thinko.  i am such a smurf.
- *     20040828        0.21 -  add hardware vlan accleration
- *                             by Neil Horman <nhorman@redhat.com>
- *     20050406        0.22 -  improved DAC ifdefs from Andi Kleen
- *                          -  removal of dead code from Adrian Bunk
- *                          -  fix half duplex collision behaviour
- * Driver Overview
- * ===============
- *
- * This driver was originally written for the National Semiconductor
- * 83820 chip, a 10/100/1000 Mbps 64 bit PCI ethernet NIC.  Hopefully
- * this code will turn out to be a) clean, b) correct, and c) fast.
- * With that in mind, I'm aiming to split the code up as much as
- * reasonably possible.  At present there are X major sections that
- * break down into a) packet receive, b) packet transmit, c) link
- * management, d) initialization and configuration.  Where possible,
- * these code paths are designed to run in parallel.
- *
- * This driver has been tested and found to work with the following
- * cards (in no particular order):
- *
- *     Cameo           SOHO-GA2000T    SOHO-GA2500T
- *     D-Link          DGE-500T
- *     PureData        PDP8023Z-TG
- *     SMC             SMC9452TX       SMC9462TX
- *     Netgear         GA621
- *
- * Special thanks to SMC for providing hardware to test this driver on.
- *
- * Reports of success or failure would be greatly appreciated.
- */
-//#define dprintk              printk
-#define dprintk(x...)          do { } while (0)
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-#include <linux/workqueue.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/ip.h>  /* for iph */
-#include <linux/in.h>  /* for IPPROTO_... */
-#include <linux/compiler.h>
-#include <linux/prefetch.h>
-#include <linux/ethtool.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/if_vlan.h>
-#include <linux/rtnetlink.h>
-#include <linux/jiffies.h>
-#include <linux/slab.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
-#define DRV_NAME "ns83820"
-
-/* Global parameters.  See module_param near the bottom. */
-static int ihr = 2;
-static int reset_phy = 0;
-static int lnksts = 0;         /* CFG_LNKSTS bit polarity */
-
-/* Dprintk is used for more interesting debug events */
-#undef Dprintk
-#define        Dprintk                 dprintk
-
-/* tunables */
-#define RX_BUF_SIZE    1500    /* 8192 */
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
-#define NS83820_VLAN_ACCEL_SUPPORT
-#endif
-
-/* Must not exceed ~65000. */
-#define NR_RX_DESC     64
-#define NR_TX_DESC     128
-
-/* not tunable */
-#define REAL_RX_BUF_SIZE (RX_BUF_SIZE + 14)    /* rx/tx mac addr + type */
-
-#define MIN_TX_DESC_FREE       8
-
-/* register defines */
-#define CFGCS          0x04
-
-#define CR_TXE         0x00000001
-#define CR_TXD         0x00000002
-/* Ramit : Here's a tip, don't do a RXD immediately followed by an RXE
- * The Receive engine skips one descriptor and moves
- * onto the next one!! */
-#define CR_RXE         0x00000004
-#define CR_RXD         0x00000008
-#define CR_TXR         0x00000010
-#define CR_RXR         0x00000020
-#define CR_SWI         0x00000080
-#define CR_RST         0x00000100
-
-#define PTSCR_EEBIST_FAIL       0x00000001
-#define PTSCR_EEBIST_EN         0x00000002
-#define PTSCR_EELOAD_EN         0x00000004
-#define PTSCR_RBIST_FAIL        0x000001b8
-#define PTSCR_RBIST_DONE        0x00000200
-#define PTSCR_RBIST_EN          0x00000400
-#define PTSCR_RBIST_RST         0x00002000
-
-#define MEAR_EEDI              0x00000001
-#define MEAR_EEDO              0x00000002
-#define MEAR_EECLK             0x00000004
-#define MEAR_EESEL             0x00000008
-#define MEAR_MDIO              0x00000010
-#define MEAR_MDDIR             0x00000020
-#define MEAR_MDC               0x00000040
-
-#define ISR_TXDESC3    0x40000000
-#define ISR_TXDESC2    0x20000000
-#define ISR_TXDESC1    0x10000000
-#define ISR_TXDESC0    0x08000000
-#define ISR_RXDESC3    0x04000000
-#define ISR_RXDESC2    0x02000000
-#define ISR_RXDESC1    0x01000000
-#define ISR_RXDESC0    0x00800000
-#define ISR_TXRCMP     0x00400000
-#define ISR_RXRCMP     0x00200000
-#define ISR_DPERR      0x00100000
-#define ISR_SSERR      0x00080000
-#define ISR_RMABT      0x00040000
-#define ISR_RTABT      0x00020000
-#define ISR_RXSOVR     0x00010000
-#define ISR_HIBINT     0x00008000
-#define ISR_PHY                0x00004000
-#define ISR_PME                0x00002000
-#define ISR_SWI                0x00001000
-#define ISR_MIB                0x00000800
-#define ISR_TXURN      0x00000400
-#define ISR_TXIDLE     0x00000200
-#define ISR_TXERR      0x00000100
-#define ISR_TXDESC     0x00000080
-#define ISR_TXOK       0x00000040
-#define ISR_RXORN      0x00000020
-#define ISR_RXIDLE     0x00000010
-#define ISR_RXEARLY    0x00000008
-#define ISR_RXERR      0x00000004
-#define ISR_RXDESC     0x00000002
-#define ISR_RXOK       0x00000001
-
-#define TXCFG_CSI      0x80000000
-#define TXCFG_HBI      0x40000000
-#define TXCFG_MLB      0x20000000
-#define TXCFG_ATP      0x10000000
-#define TXCFG_ECRETRY  0x00800000
-#define TXCFG_BRST_DIS 0x00080000
-#define TXCFG_MXDMA1024        0x00000000
-#define TXCFG_MXDMA512 0x00700000
-#define TXCFG_MXDMA256 0x00600000
-#define TXCFG_MXDMA128 0x00500000
-#define TXCFG_MXDMA64  0x00400000
-#define TXCFG_MXDMA32  0x00300000
-#define TXCFG_MXDMA16  0x00200000
-#define TXCFG_MXDMA8   0x00100000
-
-#define CFG_LNKSTS     0x80000000
-#define CFG_SPDSTS     0x60000000
-#define CFG_SPDSTS1    0x40000000
-#define CFG_SPDSTS0    0x20000000
-#define CFG_DUPSTS     0x10000000
-#define CFG_TBI_EN     0x01000000
-#define CFG_MODE_1000  0x00400000
-/* Ramit : Dont' ever use AUTO_1000, it never works and is buggy.
- * Read the Phy response and then configure the MAC accordingly */
-#define CFG_AUTO_1000  0x00200000
-#define CFG_PINT_CTL   0x001c0000
-#define CFG_PINT_DUPSTS        0x00100000
-#define CFG_PINT_LNKSTS        0x00080000
-#define CFG_PINT_SPDSTS        0x00040000
-#define CFG_TMRTEST    0x00020000
-#define CFG_MRM_DIS    0x00010000
-#define CFG_MWI_DIS    0x00008000
-#define CFG_T64ADDR    0x00004000
-#define CFG_PCI64_DET  0x00002000
-#define CFG_DATA64_EN  0x00001000
-#define CFG_M64ADDR    0x00000800
-#define CFG_PHY_RST    0x00000400
-#define CFG_PHY_DIS    0x00000200
-#define CFG_EXTSTS_EN  0x00000100
-#define CFG_REQALG     0x00000080
-#define CFG_SB         0x00000040
-#define CFG_POW                0x00000020
-#define CFG_EXD                0x00000010
-#define CFG_PESEL      0x00000008
-#define CFG_BROM_DIS   0x00000004
-#define CFG_EXT_125    0x00000002
-#define CFG_BEM                0x00000001
-
-#define EXTSTS_UDPPKT  0x00200000
-#define EXTSTS_TCPPKT  0x00080000
-#define EXTSTS_IPPKT   0x00020000
-#define EXTSTS_VPKT    0x00010000
-#define EXTSTS_VTG_MASK        0x0000ffff
-
-#define SPDSTS_POLARITY        (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0))
-
-#define MIBC_MIBS      0x00000008
-#define MIBC_ACLR      0x00000004
-#define MIBC_FRZ       0x00000002
-#define MIBC_WRN       0x00000001
-
-#define PCR_PSEN       (1 << 31)
-#define PCR_PS_MCAST   (1 << 30)
-#define PCR_PS_DA      (1 << 29)
-#define PCR_STHI_8     (3 << 23)
-#define PCR_STLO_4     (1 << 23)
-#define PCR_FFHI_8K    (3 << 21)
-#define PCR_FFLO_4K    (1 << 21)
-#define PCR_PAUSE_CNT  0xFFFE
-
-#define RXCFG_AEP      0x80000000
-#define RXCFG_ARP      0x40000000
-#define RXCFG_STRIPCRC 0x20000000
-#define RXCFG_RX_FD    0x10000000
-#define RXCFG_ALP      0x08000000
-#define RXCFG_AIRL     0x04000000
-#define RXCFG_MXDMA512 0x00700000
-#define RXCFG_DRTH     0x0000003e
-#define RXCFG_DRTH0    0x00000002
-
-#define RFCR_RFEN      0x80000000
-#define RFCR_AAB       0x40000000
-#define RFCR_AAM       0x20000000
-#define RFCR_AAU       0x10000000
-#define RFCR_APM       0x08000000
-#define RFCR_APAT      0x07800000
-#define RFCR_APAT3     0x04000000
-#define RFCR_APAT2     0x02000000
-#define RFCR_APAT1     0x01000000
-#define RFCR_APAT0     0x00800000
-#define RFCR_AARP      0x00400000
-#define RFCR_MHEN      0x00200000
-#define RFCR_UHEN      0x00100000
-#define RFCR_ULM       0x00080000
-
-#define VRCR_RUDPE     0x00000080
-#define VRCR_RTCPE     0x00000040
-#define VRCR_RIPE      0x00000020
-#define VRCR_IPEN      0x00000010
-#define VRCR_DUTF      0x00000008
-#define VRCR_DVTF      0x00000004
-#define VRCR_VTREN     0x00000002
-#define VRCR_VTDEN     0x00000001
-
-#define VTCR_PPCHK     0x00000008
-#define VTCR_GCHK      0x00000004
-#define VTCR_VPPTI     0x00000002
-#define VTCR_VGTI      0x00000001
-
-#define CR             0x00
-#define CFG            0x04
-#define MEAR           0x08
-#define PTSCR          0x0c
-#define        ISR             0x10
-#define        IMR             0x14
-#define        IER             0x18
-#define        IHR             0x1c
-#define TXDP           0x20
-#define TXDP_HI                0x24
-#define TXCFG          0x28
-#define GPIOR          0x2c
-#define RXDP           0x30
-#define RXDP_HI                0x34
-#define RXCFG          0x38
-#define PQCR           0x3c
-#define WCSR           0x40
-#define PCR            0x44
-#define RFCR           0x48
-#define RFDR           0x4c
-
-#define SRR            0x58
-
-#define VRCR           0xbc
-#define VTCR           0xc0
-#define VDR            0xc4
-#define CCSR           0xcc
-
-#define TBICR          0xe0
-#define TBISR          0xe4
-#define TANAR          0xe8
-#define TANLPAR                0xec
-#define TANER          0xf0
-#define TESR           0xf4
-
-#define TBICR_MR_AN_ENABLE     0x00001000
-#define TBICR_MR_RESTART_AN    0x00000200
-
-#define TBISR_MR_LINK_STATUS   0x00000020
-#define TBISR_MR_AN_COMPLETE   0x00000004
-
-#define TANAR_PS2              0x00000100
-#define TANAR_PS1              0x00000080
-#define TANAR_HALF_DUP                 0x00000040
-#define TANAR_FULL_DUP                 0x00000020
-
-#define GPIOR_GP5_OE           0x00000200
-#define GPIOR_GP4_OE           0x00000100
-#define GPIOR_GP3_OE           0x00000080
-#define GPIOR_GP2_OE           0x00000040
-#define GPIOR_GP1_OE           0x00000020
-#define GPIOR_GP3_OUT          0x00000004
-#define GPIOR_GP1_OUT          0x00000001
-
-#define LINK_AUTONEGOTIATE     0x01
-#define LINK_DOWN              0x02
-#define LINK_UP                        0x04
-
-#define HW_ADDR_LEN    sizeof(dma_addr_t)
-#define desc_addr_set(desc, addr)                              \
-       do {                                                    \
-               ((desc)[0] = cpu_to_le32(addr));                \
-               if (HW_ADDR_LEN == 8)                           \
-                       (desc)[1] = cpu_to_le32(((u64)addr) >> 32);     \
-       } while(0)
-#define desc_addr_get(desc)                                    \
-       (le32_to_cpu((desc)[0]) | \
-       (HW_ADDR_LEN == 8 ? ((dma_addr_t)le32_to_cpu((desc)[1]))<<32 : 0))
-
-#define DESC_LINK              0
-#define DESC_BUFPTR            (DESC_LINK + HW_ADDR_LEN/4)
-#define DESC_CMDSTS            (DESC_BUFPTR + HW_ADDR_LEN/4)
-#define DESC_EXTSTS            (DESC_CMDSTS + 4/4)
-
-#define CMDSTS_OWN     0x80000000
-#define CMDSTS_MORE    0x40000000
-#define CMDSTS_INTR    0x20000000
-#define CMDSTS_ERR     0x10000000
-#define CMDSTS_OK      0x08000000
-#define CMDSTS_RUNT    0x00200000
-#define CMDSTS_LEN_MASK        0x0000ffff
-
-#define CMDSTS_DEST_MASK       0x01800000
-#define CMDSTS_DEST_SELF       0x00800000
-#define CMDSTS_DEST_MULTI      0x01000000
-
-#define DESC_SIZE      8               /* Should be cache line sized */
-
-struct rx_info {
-       spinlock_t      lock;
-       int             up;
-       unsigned long   idle;
-
-       struct sk_buff  *skbs[NR_RX_DESC];
-
-       __le32          *next_rx_desc;
-       u16             next_rx, next_empty;
-
-       __le32          *descs;
-       dma_addr_t      phy_descs;
-};
-
-
-struct ns83820 {
-       u8                      __iomem *base;
-
-       struct pci_dev          *pci_dev;
-       struct net_device       *ndev;
-
-       struct rx_info          rx_info;
-       struct tasklet_struct   rx_tasklet;
-
-       unsigned                ihr;
-       struct work_struct      tq_refill;
-
-       /* protects everything below.  irqsave when using. */
-       spinlock_t              misc_lock;
-
-       u32                     CFG_cache;
-
-       u32                     MEAR_cache;
-       u32                     IMR_cache;
-
-       unsigned                linkstate;
-
-       spinlock_t      tx_lock;
-
-       u16             tx_done_idx;
-       u16             tx_idx;
-       volatile u16    tx_free_idx;    /* idx of free desc chain */
-       u16             tx_intr_idx;
-
-       atomic_t        nr_tx_skbs;
-       struct sk_buff  *tx_skbs[NR_TX_DESC];
-
-       char            pad[16] __attribute__((aligned(16)));
-       __le32          *tx_descs;
-       dma_addr_t      tx_phy_descs;
-
-       struct timer_list       tx_watchdog;
-};
-
-static inline struct ns83820 *PRIV(struct net_device *dev)
-{
-       return netdev_priv(dev);
-}
-
-#define __kick_rx(dev) writel(CR_RXE, dev->base + CR)
-
-static inline void kick_rx(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       dprintk("kick_rx: maybe kicking\n");
-       if (test_and_clear_bit(0, &dev->rx_info.idle)) {
-               dprintk("actually kicking\n");
-               writel(dev->rx_info.phy_descs +
-                       (4 * DESC_SIZE * dev->rx_info.next_rx),
-                      dev->base + RXDP);
-               if (dev->rx_info.next_rx == dev->rx_info.next_empty)
-                       printk(KERN_DEBUG "%s: uh-oh: next_rx == next_empty???\n",
-                               ndev->name);
-               __kick_rx(dev);
-       }
-}
-
-//free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC
-#define start_tx_okay(dev)     \
-       (((NR_TX_DESC-2 + dev->tx_done_idx - dev->tx_free_idx) % NR_TX_DESC) > MIN_TX_DESC_FREE)
-
-/* Packet Receiver
- *
- * The hardware supports linked lists of receive descriptors for
- * which ownership is transferred back and forth by means of an
- * ownership bit.  While the hardware does support the use of a
- * ring for receive descriptors, we only make use of a chain in
- * an attempt to reduce bus traffic under heavy load scenarios.
- * This will also make bugs a bit more obvious.  The current code
- * only makes use of a single rx chain; I hope to implement
- * priority based rx for version 1.0.  Goal: even under overload
- * conditions, still route realtime traffic with as low jitter as
- * possible.
- */
-static inline void build_rx_desc(struct ns83820 *dev, __le32 *desc, dma_addr_t link, dma_addr_t buf, u32 cmdsts, u32 extsts)
-{
-       desc_addr_set(desc + DESC_LINK, link);
-       desc_addr_set(desc + DESC_BUFPTR, buf);
-       desc[DESC_EXTSTS] = cpu_to_le32(extsts);
-       mb();
-       desc[DESC_CMDSTS] = cpu_to_le32(cmdsts);
-}
-
-#define nr_rx_empty(dev) ((NR_RX_DESC-2 + dev->rx_info.next_rx - dev->rx_info.next_empty) % NR_RX_DESC)
-static inline int ns83820_add_rx_skb(struct ns83820 *dev, struct sk_buff *skb)
-{
-       unsigned next_empty;
-       u32 cmdsts;
-       __le32 *sg;
-       dma_addr_t buf;
-
-       next_empty = dev->rx_info.next_empty;
-
-       /* don't overrun last rx marker */
-       if (unlikely(nr_rx_empty(dev) <= 2)) {
-               kfree_skb(skb);
-               return 1;
-       }
-
-#if 0
-       dprintk("next_empty[%d] nr_used[%d] next_rx[%d]\n",
-               dev->rx_info.next_empty,
-               dev->rx_info.nr_used,
-               dev->rx_info.next_rx
-               );
-#endif
-
-       sg = dev->rx_info.descs + (next_empty * DESC_SIZE);
-       BUG_ON(NULL != dev->rx_info.skbs[next_empty]);
-       dev->rx_info.skbs[next_empty] = skb;
-
-       dev->rx_info.next_empty = (next_empty + 1) % NR_RX_DESC;
-       cmdsts = REAL_RX_BUF_SIZE | CMDSTS_INTR;
-       buf = pci_map_single(dev->pci_dev, skb->data,
-                            REAL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
-       build_rx_desc(dev, sg, 0, buf, cmdsts, 0);
-       /* update link of previous rx */
-       if (likely(next_empty != dev->rx_info.next_rx))
-               dev->rx_info.descs[((NR_RX_DESC + next_empty - 1) % NR_RX_DESC) * DESC_SIZE] = cpu_to_le32(dev->rx_info.phy_descs + (next_empty * DESC_SIZE * 4));
-
-       return 0;
-}
-
-static inline int rx_refill(struct net_device *ndev, gfp_t gfp)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       unsigned i;
-       unsigned long flags = 0;
-
-       if (unlikely(nr_rx_empty(dev) <= 2))
-               return 0;
-
-       dprintk("rx_refill(%p)\n", ndev);
-       if (gfp == GFP_ATOMIC)
-               spin_lock_irqsave(&dev->rx_info.lock, flags);
-       for (i=0; i<NR_RX_DESC; i++) {
-               struct sk_buff *skb;
-               long res;
-
-               /* extra 16 bytes for alignment */
-               skb = __netdev_alloc_skb(ndev, REAL_RX_BUF_SIZE+16, gfp);
-               if (unlikely(!skb))
-                       break;
-
-               skb_reserve(skb, skb->data - PTR_ALIGN(skb->data, 16));
-               if (gfp != GFP_ATOMIC)
-                       spin_lock_irqsave(&dev->rx_info.lock, flags);
-               res = ns83820_add_rx_skb(dev, skb);
-               if (gfp != GFP_ATOMIC)
-                       spin_unlock_irqrestore(&dev->rx_info.lock, flags);
-               if (res) {
-                       i = 1;
-                       break;
-               }
-       }
-       if (gfp == GFP_ATOMIC)
-               spin_unlock_irqrestore(&dev->rx_info.lock, flags);
-
-       return i ? 0 : -ENOMEM;
-}
-
-static void rx_refill_atomic(struct net_device *ndev)
-{
-       rx_refill(ndev, GFP_ATOMIC);
-}
-
-/* REFILL */
-static inline void queue_refill(struct work_struct *work)
-{
-       struct ns83820 *dev = container_of(work, struct ns83820, tq_refill);
-       struct net_device *ndev = dev->ndev;
-
-       rx_refill(ndev, GFP_KERNEL);
-       if (dev->rx_info.up)
-               kick_rx(ndev);
-}
-
-static inline void clear_rx_desc(struct ns83820 *dev, unsigned i)
-{
-       build_rx_desc(dev, dev->rx_info.descs + (DESC_SIZE * i), 0, 0, CMDSTS_OWN, 0);
-}
-
-static void phy_intr(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       static const char *speeds[] = { "10", "100", "1000", "1000(?)", "1000F" };
-       u32 cfg, new_cfg;
-       u32 tbisr, tanar, tanlpar;
-       int speed, fullduplex, newlinkstate;
-
-       cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
-
-       if (dev->CFG_cache & CFG_TBI_EN) {
-               /* we have an optical transceiver */
-               tbisr = readl(dev->base + TBISR);
-               tanar = readl(dev->base + TANAR);
-               tanlpar = readl(dev->base + TANLPAR);
-               dprintk("phy_intr: tbisr=%08x, tanar=%08x, tanlpar=%08x\n",
-                       tbisr, tanar, tanlpar);
-
-               if ( (fullduplex = (tanlpar & TANAR_FULL_DUP) &&
-                     (tanar & TANAR_FULL_DUP)) ) {
-
-                       /* both of us are full duplex */
-                       writel(readl(dev->base + TXCFG)
-                              | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
-                              dev->base + TXCFG);
-                       writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
-                              dev->base + RXCFG);
-                       /* Light up full duplex LED */
-                       writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT,
-                              dev->base + GPIOR);
-
-               } else if (((tanlpar & TANAR_HALF_DUP) &&
-                           (tanar & TANAR_HALF_DUP)) ||
-                          ((tanlpar & TANAR_FULL_DUP) &&
-                           (tanar & TANAR_HALF_DUP)) ||
-                          ((tanlpar & TANAR_HALF_DUP) &&
-                           (tanar & TANAR_FULL_DUP))) {
-
-                       /* one or both of us are half duplex */
-                       writel((readl(dev->base + TXCFG)
-                               & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP,
-                              dev->base + TXCFG);
-                       writel(readl(dev->base + RXCFG) & ~RXCFG_RX_FD,
-                              dev->base + RXCFG);
-                       /* Turn off full duplex LED */
-                       writel(readl(dev->base + GPIOR) & ~GPIOR_GP1_OUT,
-                              dev->base + GPIOR);
-               }
-
-               speed = 4; /* 1000F */
-
-       } else {
-               /* we have a copper transceiver */
-               new_cfg = dev->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS);
-
-               if (cfg & CFG_SPDSTS1)
-                       new_cfg |= CFG_MODE_1000;
-               else
-                       new_cfg &= ~CFG_MODE_1000;
-
-               speed = ((cfg / CFG_SPDSTS0) & 3);
-               fullduplex = (cfg & CFG_DUPSTS);
-
-               if (fullduplex) {
-                       new_cfg |= CFG_SB;
-                       writel(readl(dev->base + TXCFG)
-                                       | TXCFG_CSI | TXCFG_HBI,
-                              dev->base + TXCFG);
-                       writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
-                              dev->base + RXCFG);
-               } else {
-                       writel(readl(dev->base + TXCFG)
-                                       & ~(TXCFG_CSI | TXCFG_HBI),
-                              dev->base + TXCFG);
-                       writel(readl(dev->base + RXCFG) & ~(RXCFG_RX_FD),
-                              dev->base + RXCFG);
-               }
-
-               if ((cfg & CFG_LNKSTS) &&
-                   ((new_cfg ^ dev->CFG_cache) != 0)) {
-                       writel(new_cfg, dev->base + CFG);
-                       dev->CFG_cache = new_cfg;
-               }
-
-               dev->CFG_cache &= ~CFG_SPDSTS;
-               dev->CFG_cache |= cfg & CFG_SPDSTS;
-       }
-
-       newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN;
-
-       if (newlinkstate & LINK_UP &&
-           dev->linkstate != newlinkstate) {
-               netif_start_queue(ndev);
-               netif_wake_queue(ndev);
-               printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n",
-                       ndev->name,
-                       speeds[speed],
-                       fullduplex ? "full" : "half");
-       } else if (newlinkstate & LINK_DOWN &&
-                  dev->linkstate != newlinkstate) {
-               netif_stop_queue(ndev);
-               printk(KERN_INFO "%s: link now down.\n", ndev->name);
-       }
-
-       dev->linkstate = newlinkstate;
-}
-
-static int ns83820_setup_rx(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       unsigned i;
-       int ret;
-
-       dprintk("ns83820_setup_rx(%p)\n", ndev);
-
-       dev->rx_info.idle = 1;
-       dev->rx_info.next_rx = 0;
-       dev->rx_info.next_rx_desc = dev->rx_info.descs;
-       dev->rx_info.next_empty = 0;
-
-       for (i=0; i<NR_RX_DESC; i++)
-               clear_rx_desc(dev, i);
-
-       writel(0, dev->base + RXDP_HI);
-       writel(dev->rx_info.phy_descs, dev->base + RXDP);
-
-       ret = rx_refill(ndev, GFP_KERNEL);
-       if (!ret) {
-               dprintk("starting receiver\n");
-               /* prevent the interrupt handler from stomping on us */
-               spin_lock_irq(&dev->rx_info.lock);
-
-               writel(0x0001, dev->base + CCSR);
-               writel(0, dev->base + RFCR);
-               writel(0x7fc00000, dev->base + RFCR);
-               writel(0xffc00000, dev->base + RFCR);
-
-               dev->rx_info.up = 1;
-
-               phy_intr(ndev);
-
-               /* Okay, let it rip */
-               spin_lock(&dev->misc_lock);
-               dev->IMR_cache |= ISR_PHY;
-               dev->IMR_cache |= ISR_RXRCMP;
-               //dev->IMR_cache |= ISR_RXERR;
-               //dev->IMR_cache |= ISR_RXOK;
-               dev->IMR_cache |= ISR_RXORN;
-               dev->IMR_cache |= ISR_RXSOVR;
-               dev->IMR_cache |= ISR_RXDESC;
-               dev->IMR_cache |= ISR_RXIDLE;
-               dev->IMR_cache |= ISR_TXDESC;
-               dev->IMR_cache |= ISR_TXIDLE;
-
-               writel(dev->IMR_cache, dev->base + IMR);
-               writel(1, dev->base + IER);
-               spin_unlock(&dev->misc_lock);
-
-               kick_rx(ndev);
-
-               spin_unlock_irq(&dev->rx_info.lock);
-       }
-       return ret;
-}
-
-static void ns83820_cleanup_rx(struct ns83820 *dev)
-{
-       unsigned i;
-       unsigned long flags;
-
-       dprintk("ns83820_cleanup_rx(%p)\n", dev);
-
-       /* disable receive interrupts */
-       spin_lock_irqsave(&dev->misc_lock, flags);
-       dev->IMR_cache &= ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY | ISR_RXIDLE);
-       writel(dev->IMR_cache, dev->base + IMR);
-       spin_unlock_irqrestore(&dev->misc_lock, flags);
-
-       /* synchronize with the interrupt handler and kill it */
-       dev->rx_info.up = 0;
-       synchronize_irq(dev->pci_dev->irq);
-
-       /* touch the pci bus... */
-       readl(dev->base + IMR);
-
-       /* assumes the transmitter is already disabled and reset */
-       writel(0, dev->base + RXDP_HI);
-       writel(0, dev->base + RXDP);
-
-       for (i=0; i<NR_RX_DESC; i++) {
-               struct sk_buff *skb = dev->rx_info.skbs[i];
-               dev->rx_info.skbs[i] = NULL;
-               clear_rx_desc(dev, i);
-               kfree_skb(skb);
-       }
-}
-
-static void ns83820_rx_kick(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       /*if (nr_rx_empty(dev) >= NR_RX_DESC/4)*/ {
-               if (dev->rx_info.up) {
-                       rx_refill_atomic(ndev);
-                       kick_rx(ndev);
-               }
-       }
-
-       if (dev->rx_info.up && nr_rx_empty(dev) > NR_RX_DESC*3/4)
-               schedule_work(&dev->tq_refill);
-       else
-               kick_rx(ndev);
-       if (dev->rx_info.idle)
-               printk(KERN_DEBUG "%s: BAD\n", ndev->name);
-}
-
-/* rx_irq
- *
- */
-static void rx_irq(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       struct rx_info *info = &dev->rx_info;
-       unsigned next_rx;
-       int rx_rc, len;
-       u32 cmdsts;
-       __le32 *desc;
-       unsigned long flags;
-       int nr = 0;
-
-       dprintk("rx_irq(%p)\n", ndev);
-       dprintk("rxdp: %08x, descs: %08lx next_rx[%d]: %p next_empty[%d]: %p\n",
-               readl(dev->base + RXDP),
-               (long)(dev->rx_info.phy_descs),
-               (int)dev->rx_info.next_rx,
-               (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_rx)),
-               (int)dev->rx_info.next_empty,
-               (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_empty))
-               );
-
-       spin_lock_irqsave(&info->lock, flags);
-       if (!info->up)
-               goto out;
-
-       dprintk("walking descs\n");
-       next_rx = info->next_rx;
-       desc = info->next_rx_desc;
-       while ((CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) &&
-              (cmdsts != CMDSTS_OWN)) {
-               struct sk_buff *skb;
-               u32 extsts = le32_to_cpu(desc[DESC_EXTSTS]);
-               dma_addr_t bufptr = desc_addr_get(desc + DESC_BUFPTR);
-
-               dprintk("cmdsts: %08x\n", cmdsts);
-               dprintk("link: %08x\n", cpu_to_le32(desc[DESC_LINK]));
-               dprintk("extsts: %08x\n", extsts);
-
-               skb = info->skbs[next_rx];
-               info->skbs[next_rx] = NULL;
-               info->next_rx = (next_rx + 1) % NR_RX_DESC;
-
-               mb();
-               clear_rx_desc(dev, next_rx);
-
-               pci_unmap_single(dev->pci_dev, bufptr,
-                                RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
-               len = cmdsts & CMDSTS_LEN_MASK;
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
-               /* NH: As was mentioned below, this chip is kinda
-                * brain dead about vlan tag stripping.  Frames
-                * that are 64 bytes with a vlan header appended
-                * like arp frames, or pings, are flagged as Runts
-                * when the tag is stripped and hardware.  This
-                * also means that the OK bit in the descriptor
-                * is cleared when the frame comes in so we have
-                * to do a specific length check here to make sure
-                * the frame would have been ok, had we not stripped
-                * the tag.
-                */
-               if (likely((CMDSTS_OK & cmdsts) ||
-                       ((cmdsts & CMDSTS_RUNT) && len >= 56))) {
-#else
-               if (likely(CMDSTS_OK & cmdsts)) {
-#endif
-                       skb_put(skb, len);
-                       if (unlikely(!skb))
-                               goto netdev_mangle_me_harder_failed;
-                       if (cmdsts & CMDSTS_DEST_MULTI)
-                               ndev->stats.multicast++;
-                       ndev->stats.rx_packets++;
-                       ndev->stats.rx_bytes += len;
-                       if ((extsts & 0x002a0000) && !(extsts & 0x00540000)) {
-                               skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       } else {
-                               skb_checksum_none_assert(skb);
-                       }
-                       skb->protocol = eth_type_trans(skb, ndev);
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
-                       if(extsts & EXTSTS_VPKT) {
-                               unsigned short tag;
-
-                               tag = ntohs(extsts & EXTSTS_VTG_MASK);
-                               __vlan_hwaccel_put_tag(skb, tag);
-                       }
-#endif
-                       rx_rc = netif_rx(skb);
-                       if (NET_RX_DROP == rx_rc) {
-netdev_mangle_me_harder_failed:
-                               ndev->stats.rx_dropped++;
-                       }
-               } else {
-                       kfree_skb(skb);
-               }
-
-               nr++;
-               next_rx = info->next_rx;
-               desc = info->descs + (DESC_SIZE * next_rx);
-       }
-       info->next_rx = next_rx;
-       info->next_rx_desc = info->descs + (DESC_SIZE * next_rx);
-
-out:
-       if (0 && !nr) {
-               Dprintk("dazed: cmdsts_f: %08x\n", cmdsts);
-       }
-
-       spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static void rx_action(unsigned long _dev)
-{
-       struct net_device *ndev = (void *)_dev;
-       struct ns83820 *dev = PRIV(ndev);
-       rx_irq(ndev);
-       writel(ihr, dev->base + IHR);
-
-       spin_lock_irq(&dev->misc_lock);
-       dev->IMR_cache |= ISR_RXDESC;
-       writel(dev->IMR_cache, dev->base + IMR);
-       spin_unlock_irq(&dev->misc_lock);
-
-       rx_irq(ndev);
-       ns83820_rx_kick(ndev);
-}
-
-/* Packet Transmit code
- */
-static inline void kick_tx(struct ns83820 *dev)
-{
-       dprintk("kick_tx(%p): tx_idx=%d free_idx=%d\n",
-               dev, dev->tx_idx, dev->tx_free_idx);
-       writel(CR_TXE, dev->base + CR);
-}
-
-/* No spinlock needed on the transmit irq path as the interrupt handler is
- * serialized.
- */
-static void do_tx_done(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       u32 cmdsts, tx_done_idx;
-       __le32 *desc;
-
-       dprintk("do_tx_done(%p)\n", ndev);
-       tx_done_idx = dev->tx_done_idx;
-       desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
-
-       dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n",
-               tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS]));
-       while ((tx_done_idx != dev->tx_free_idx) &&
-              !(CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) ) {
-               struct sk_buff *skb;
-               unsigned len;
-               dma_addr_t addr;
-
-               if (cmdsts & CMDSTS_ERR)
-                       ndev->stats.tx_errors++;
-               if (cmdsts & CMDSTS_OK)
-                       ndev->stats.tx_packets++;
-               if (cmdsts & CMDSTS_OK)
-                       ndev->stats.tx_bytes += cmdsts & 0xffff;
-
-               dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n",
-                       tx_done_idx, dev->tx_free_idx, cmdsts);
-               skb = dev->tx_skbs[tx_done_idx];
-               dev->tx_skbs[tx_done_idx] = NULL;
-               dprintk("done(%p)\n", skb);
-
-               len = cmdsts & CMDSTS_LEN_MASK;
-               addr = desc_addr_get(desc + DESC_BUFPTR);
-               if (skb) {
-                       pci_unmap_single(dev->pci_dev,
-                                       addr,
-                                       len,
-                                       PCI_DMA_TODEVICE);
-                       dev_kfree_skb_irq(skb);
-                       atomic_dec(&dev->nr_tx_skbs);
-               } else
-                       pci_unmap_page(dev->pci_dev,
-                                       addr,
-                                       len,
-                                       PCI_DMA_TODEVICE);
-
-               tx_done_idx = (tx_done_idx + 1) % NR_TX_DESC;
-               dev->tx_done_idx = tx_done_idx;
-               desc[DESC_CMDSTS] = cpu_to_le32(0);
-               mb();
-               desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
-       }
-
-       /* Allow network stack to resume queueing packets after we've
-        * finished transmitting at least 1/4 of the packets in the queue.
-        */
-       if (netif_queue_stopped(ndev) && start_tx_okay(dev)) {
-               dprintk("start_queue(%p)\n", ndev);
-               netif_start_queue(ndev);
-               netif_wake_queue(ndev);
-       }
-}
-
-static void ns83820_cleanup_tx(struct ns83820 *dev)
-{
-       unsigned i;
-
-       for (i=0; i<NR_TX_DESC; i++) {
-               struct sk_buff *skb = dev->tx_skbs[i];
-               dev->tx_skbs[i] = NULL;
-               if (skb) {
-                       __le32 *desc = dev->tx_descs + (i * DESC_SIZE);
-                       pci_unmap_single(dev->pci_dev,
-                                       desc_addr_get(desc + DESC_BUFPTR),
-                                       le32_to_cpu(desc[DESC_CMDSTS]) & CMDSTS_LEN_MASK,
-                                       PCI_DMA_TODEVICE);
-                       dev_kfree_skb_irq(skb);
-                       atomic_dec(&dev->nr_tx_skbs);
-               }
-       }
-
-       memset(dev->tx_descs, 0, NR_TX_DESC * DESC_SIZE * 4);
-}
-
-/* transmit routine.  This code relies on the network layer serializing
- * its calls in, but will run happily in parallel with the interrupt
- * handler.  This code currently has provisions for fragmenting tx buffers
- * while trying to track down a bug in either the zero copy code or
- * the tx fifo (hence the MAX_FRAG_LEN).
- */
-static netdev_tx_t ns83820_hard_start_xmit(struct sk_buff *skb,
-                                          struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       u32 free_idx, cmdsts, extsts;
-       int nr_free, nr_frags;
-       unsigned tx_done_idx, last_idx;
-       dma_addr_t buf;
-       unsigned len;
-       skb_frag_t *frag;
-       int stopped = 0;
-       int do_intr = 0;
-       volatile __le32 *first_desc;
-
-       dprintk("ns83820_hard_start_xmit\n");
-
-       nr_frags =  skb_shinfo(skb)->nr_frags;
-again:
-       if (unlikely(dev->CFG_cache & CFG_LNKSTS)) {
-               netif_stop_queue(ndev);
-               if (unlikely(dev->CFG_cache & CFG_LNKSTS))
-                       return NETDEV_TX_BUSY;
-               netif_start_queue(ndev);
-       }
-
-       last_idx = free_idx = dev->tx_free_idx;
-       tx_done_idx = dev->tx_done_idx;
-       nr_free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC;
-       nr_free -= 1;
-       if (nr_free <= nr_frags) {
-               dprintk("stop_queue - not enough(%p)\n", ndev);
-               netif_stop_queue(ndev);
-
-               /* Check again: we may have raced with a tx done irq */
-               if (dev->tx_done_idx != tx_done_idx) {
-                       dprintk("restart queue(%p)\n", ndev);
-                       netif_start_queue(ndev);
-                       goto again;
-               }
-               return NETDEV_TX_BUSY;
-       }
-
-       if (free_idx == dev->tx_intr_idx) {
-               do_intr = 1;
-               dev->tx_intr_idx = (dev->tx_intr_idx + NR_TX_DESC/4) % NR_TX_DESC;
-       }
-
-       nr_free -= nr_frags;
-       if (nr_free < MIN_TX_DESC_FREE) {
-               dprintk("stop_queue - last entry(%p)\n", ndev);
-               netif_stop_queue(ndev);
-               stopped = 1;
-       }
-
-       frag = skb_shinfo(skb)->frags;
-       if (!nr_frags)
-               frag = NULL;
-       extsts = 0;
-       if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               extsts |= EXTSTS_IPPKT;
-               if (IPPROTO_TCP == ip_hdr(skb)->protocol)
-                       extsts |= EXTSTS_TCPPKT;
-               else if (IPPROTO_UDP == ip_hdr(skb)->protocol)
-                       extsts |= EXTSTS_UDPPKT;
-       }
-
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
-       if(vlan_tx_tag_present(skb)) {
-               /* fetch the vlan tag info out of the
-                * ancillary data if the vlan code
-                * is using hw vlan acceleration
-                */
-               short tag = vlan_tx_tag_get(skb);
-               extsts |= (EXTSTS_VPKT | htons(tag));
-       }
-#endif
-
-       len = skb->len;
-       if (nr_frags)
-               len -= skb->data_len;
-       buf = pci_map_single(dev->pci_dev, skb->data, len, PCI_DMA_TODEVICE);
-
-       first_desc = dev->tx_descs + (free_idx * DESC_SIZE);
-
-       for (;;) {
-               volatile __le32 *desc = dev->tx_descs + (free_idx * DESC_SIZE);
-
-               dprintk("frag[%3u]: %4u @ 0x%08Lx\n", free_idx, len,
-                       (unsigned long long)buf);
-               last_idx = free_idx;
-               free_idx = (free_idx + 1) % NR_TX_DESC;
-               desc[DESC_LINK] = cpu_to_le32(dev->tx_phy_descs + (free_idx * DESC_SIZE * 4));
-               desc_addr_set(desc + DESC_BUFPTR, buf);
-               desc[DESC_EXTSTS] = cpu_to_le32(extsts);
-
-               cmdsts = ((nr_frags) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0);
-               cmdsts |= (desc == first_desc) ? 0 : CMDSTS_OWN;
-               cmdsts |= len;
-               desc[DESC_CMDSTS] = cpu_to_le32(cmdsts);
-
-               if (!nr_frags)
-                       break;
-
-               buf = pci_map_page(dev->pci_dev, frag->page,
-                                  frag->page_offset,
-                                  frag->size, PCI_DMA_TODEVICE);
-               dprintk("frag: buf=%08Lx  page=%08lx offset=%08lx\n",
-                       (long long)buf, (long) page_to_pfn(frag->page),
-                       frag->page_offset);
-               len = frag->size;
-               frag++;
-               nr_frags--;
-       }
-       dprintk("done pkt\n");
-
-       spin_lock_irq(&dev->tx_lock);
-       dev->tx_skbs[last_idx] = skb;
-       first_desc[DESC_CMDSTS] |= cpu_to_le32(CMDSTS_OWN);
-       dev->tx_free_idx = free_idx;
-       atomic_inc(&dev->nr_tx_skbs);
-       spin_unlock_irq(&dev->tx_lock);
-
-       kick_tx(dev);
-
-       /* Check again: we may have raced with a tx done irq */
-       if (stopped && (dev->tx_done_idx != tx_done_idx) && start_tx_okay(dev))
-               netif_start_queue(ndev);
-
-       return NETDEV_TX_OK;
-}
-
-static void ns83820_update_stats(struct ns83820 *dev)
-{
-       struct net_device *ndev = dev->ndev;
-       u8 __iomem *base = dev->base;
-
-       /* the DP83820 will freeze counters, so we need to read all of them */
-       ndev->stats.rx_errors           += readl(base + 0x60) & 0xffff;
-       ndev->stats.rx_crc_errors       += readl(base + 0x64) & 0xffff;
-       ndev->stats.rx_missed_errors    += readl(base + 0x68) & 0xffff;
-       ndev->stats.rx_frame_errors     += readl(base + 0x6c) & 0xffff;
-       /*ndev->stats.rx_symbol_errors +=*/ readl(base + 0x70);
-       ndev->stats.rx_length_errors    += readl(base + 0x74) & 0xffff;
-       ndev->stats.rx_length_errors    += readl(base + 0x78) & 0xffff;
-       /*ndev->stats.rx_badopcode_errors += */ readl(base + 0x7c);
-       /*ndev->stats.rx_pause_count += */  readl(base + 0x80);
-       /*ndev->stats.tx_pause_count += */  readl(base + 0x84);
-       ndev->stats.tx_carrier_errors   += readl(base + 0x88) & 0xff;
-}
-
-static struct net_device_stats *ns83820_get_stats(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-
-       /* somewhat overkill */
-       spin_lock_irq(&dev->misc_lock);
-       ns83820_update_stats(dev);
-       spin_unlock_irq(&dev->misc_lock);
-
-       return &ndev->stats;
-}
-
-/* Let ethtool retrieve info */
-static int ns83820_get_settings(struct net_device *ndev,
-                               struct ethtool_cmd *cmd)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       u32 cfg, tanar, tbicr;
-       int fullduplex   = 0;
-
-       /*
-        * Here's the list of available ethtool commands from other drivers:
-        *      cmd->advertising =
-        *      ethtool_cmd_speed_set(cmd, ...)
-        *      cmd->duplex =
-        *      cmd->port = 0;
-        *      cmd->phy_address =
-        *      cmd->transceiver = 0;
-        *      cmd->autoneg =
-        *      cmd->maxtxpkt = 0;
-        *      cmd->maxrxpkt = 0;
-        */
-
-       /* read current configuration */
-       cfg   = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
-       tanar = readl(dev->base + TANAR);
-       tbicr = readl(dev->base + TBICR);
-
-       fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0;
-
-       cmd->supported = SUPPORTED_Autoneg;
-
-       if (dev->CFG_cache & CFG_TBI_EN) {
-               /* we have optical interface */
-               cmd->supported |= SUPPORTED_1000baseT_Half |
-                                       SUPPORTED_1000baseT_Full |
-                                       SUPPORTED_FIBRE;
-               cmd->port       = PORT_FIBRE;
-       } else {
-               /* we have copper */
-               cmd->supported |= SUPPORTED_10baseT_Half |
-                       SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half |
-                       SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Half |
-                       SUPPORTED_1000baseT_Full |
-                       SUPPORTED_MII;
-               cmd->port = PORT_MII;
-       }
-
-       cmd->duplex = fullduplex ? DUPLEX_FULL : DUPLEX_HALF;
-       switch (cfg / CFG_SPDSTS0 & 3) {
-       case 2:
-               ethtool_cmd_speed_set(cmd, SPEED_1000);
-               break;
-       case 1:
-               ethtool_cmd_speed_set(cmd, SPEED_100);
-               break;
-       default:
-               ethtool_cmd_speed_set(cmd, SPEED_10);
-               break;
-       }
-       cmd->autoneg = (tbicr & TBICR_MR_AN_ENABLE)
-               ? AUTONEG_ENABLE : AUTONEG_DISABLE;
-       return 0;
-}
-
-/* Let ethool change settings*/
-static int ns83820_set_settings(struct net_device *ndev,
-                               struct ethtool_cmd *cmd)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       u32 cfg, tanar;
-       int have_optical = 0;
-       int fullduplex   = 0;
-
-       /* read current configuration */
-       cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
-       tanar = readl(dev->base + TANAR);
-
-       if (dev->CFG_cache & CFG_TBI_EN) {
-               /* we have optical */
-               have_optical = 1;
-               fullduplex   = (tanar & TANAR_FULL_DUP);
-
-       } else {
-               /* we have copper */
-               fullduplex = cfg & CFG_DUPSTS;
-       }
-
-       spin_lock_irq(&dev->misc_lock);
-       spin_lock(&dev->tx_lock);
-
-       /* Set duplex */
-       if (cmd->duplex != fullduplex) {
-               if (have_optical) {
-                       /*set full duplex*/
-                       if (cmd->duplex == DUPLEX_FULL) {
-                               /* force full duplex */
-                               writel(readl(dev->base + TXCFG)
-                                       | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
-                                       dev->base + TXCFG);
-                               writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
-                                       dev->base + RXCFG);
-                               /* Light up full duplex LED */
-                               writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT,
-                                       dev->base + GPIOR);
-                       } else {
-                               /*TODO: set half duplex */
-                       }
-
-               } else {
-                       /*we have copper*/
-                       /* TODO: Set duplex for copper cards */
-               }
-               printk(KERN_INFO "%s: Duplex set via ethtool\n",
-               ndev->name);
-       }
-
-       /* Set autonegotiation */
-       if (1) {
-               if (cmd->autoneg == AUTONEG_ENABLE) {
-                       /* restart auto negotiation */
-                       writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN,
-                               dev->base + TBICR);
-                       writel(TBICR_MR_AN_ENABLE, dev->base + TBICR);
-                               dev->linkstate = LINK_AUTONEGOTIATE;
-
-                       printk(KERN_INFO "%s: autoneg enabled via ethtool\n",
-                               ndev->name);
-               } else {
-                       /* disable auto negotiation */
-                       writel(0x00000000, dev->base + TBICR);
-               }
-
-               printk(KERN_INFO "%s: autoneg %s via ethtool\n", ndev->name,
-                               cmd->autoneg ? "ENABLED" : "DISABLED");
-       }
-
-       phy_intr(ndev);
-       spin_unlock(&dev->tx_lock);
-       spin_unlock_irq(&dev->misc_lock);
-
-       return 0;
-}
-/* end ethtool get/set support -df */
-
-static void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       strcpy(info->driver, "ns83820");
-       strcpy(info->version, VERSION);
-       strcpy(info->bus_info, pci_name(dev->pci_dev));
-}
-
-static u32 ns83820_get_link(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       u32 cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
-       return cfg & CFG_LNKSTS ? 1 : 0;
-}
-
-static const struct ethtool_ops ops = {
-       .get_settings    = ns83820_get_settings,
-       .set_settings    = ns83820_set_settings,
-       .get_drvinfo     = ns83820_get_drvinfo,
-       .get_link        = ns83820_get_link
-};
-
-static inline void ns83820_disable_interrupts(struct ns83820 *dev)
-{
-       writel(0, dev->base + IMR);
-       writel(0, dev->base + IER);
-       readl(dev->base + IER);
-}
-
-/* this function is called in irq context from the ISR */
-static void ns83820_mib_isr(struct ns83820 *dev)
-{
-       unsigned long flags;
-       spin_lock_irqsave(&dev->misc_lock, flags);
-       ns83820_update_stats(dev);
-       spin_unlock_irqrestore(&dev->misc_lock, flags);
-}
-
-static void ns83820_do_isr(struct net_device *ndev, u32 isr);
-static irqreturn_t ns83820_irq(int foo, void *data)
-{
-       struct net_device *ndev = data;
-       struct ns83820 *dev = PRIV(ndev);
-       u32 isr;
-       dprintk("ns83820_irq(%p)\n", ndev);
-
-       dev->ihr = 0;
-
-       isr = readl(dev->base + ISR);
-       dprintk("irq: %08x\n", isr);
-       ns83820_do_isr(ndev, isr);
-       return IRQ_HANDLED;
-}
-
-static void ns83820_do_isr(struct net_device *ndev, u32 isr)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       unsigned long flags;
-
-#ifdef DEBUG
-       if (isr & ~(ISR_PHY | ISR_RXDESC | ISR_RXEARLY | ISR_RXOK | ISR_RXERR | ISR_TXIDLE | ISR_TXOK | ISR_TXDESC))
-               Dprintk("odd isr? 0x%08x\n", isr);
-#endif
-
-       if (ISR_RXIDLE & isr) {
-               dev->rx_info.idle = 1;
-               Dprintk("oh dear, we are idle\n");
-               ns83820_rx_kick(ndev);
-       }
-
-       if ((ISR_RXDESC | ISR_RXOK) & isr) {
-               prefetch(dev->rx_info.next_rx_desc);
-
-               spin_lock_irqsave(&dev->misc_lock, flags);
-               dev->IMR_cache &= ~(ISR_RXDESC | ISR_RXOK);
-               writel(dev->IMR_cache, dev->base + IMR);
-               spin_unlock_irqrestore(&dev->misc_lock, flags);
-
-               tasklet_schedule(&dev->rx_tasklet);
-               //rx_irq(ndev);
-               //writel(4, dev->base + IHR);
-       }
-
-       if ((ISR_RXIDLE | ISR_RXORN | ISR_RXDESC | ISR_RXOK | ISR_RXERR) & isr)
-               ns83820_rx_kick(ndev);
-
-       if (unlikely(ISR_RXSOVR & isr)) {
-               //printk("overrun: rxsovr\n");
-               ndev->stats.rx_fifo_errors++;
-       }
-
-       if (unlikely(ISR_RXORN & isr)) {
-               //printk("overrun: rxorn\n");
-               ndev->stats.rx_fifo_errors++;
-       }
-
-       if ((ISR_RXRCMP & isr) && dev->rx_info.up)
-               writel(CR_RXE, dev->base + CR);
-
-       if (ISR_TXIDLE & isr) {
-               u32 txdp;
-               txdp = readl(dev->base + TXDP);
-               dprintk("txdp: %08x\n", txdp);
-               txdp -= dev->tx_phy_descs;
-               dev->tx_idx = txdp / (DESC_SIZE * 4);
-               if (dev->tx_idx >= NR_TX_DESC) {
-                       printk(KERN_ALERT "%s: BUG -- txdp out of range\n", ndev->name);
-                       dev->tx_idx = 0;
-               }
-               /* The may have been a race between a pci originated read
-                * and the descriptor update from the cpu.  Just in case,
-                * kick the transmitter if the hardware thinks it is on a
-                * different descriptor than we are.
-                */
-               if (dev->tx_idx != dev->tx_free_idx)
-                       kick_tx(dev);
-       }
-
-       /* Defer tx ring processing until more than a minimum amount of
-        * work has accumulated
-        */
-       if ((ISR_TXDESC | ISR_TXIDLE | ISR_TXOK | ISR_TXERR) & isr) {
-               spin_lock_irqsave(&dev->tx_lock, flags);
-               do_tx_done(ndev);
-               spin_unlock_irqrestore(&dev->tx_lock, flags);
-
-               /* Disable TxOk if there are no outstanding tx packets.
-                */
-               if ((dev->tx_done_idx == dev->tx_free_idx) &&
-                   (dev->IMR_cache & ISR_TXOK)) {
-                       spin_lock_irqsave(&dev->misc_lock, flags);
-                       dev->IMR_cache &= ~ISR_TXOK;
-                       writel(dev->IMR_cache, dev->base + IMR);
-                       spin_unlock_irqrestore(&dev->misc_lock, flags);
-               }
-       }
-
-       /* The TxIdle interrupt can come in before the transmit has
-        * completed.  Normally we reap packets off of the combination
-        * of TxDesc and TxIdle and leave TxOk disabled (since it
-        * occurs on every packet), but when no further irqs of this
-        * nature are expected, we must enable TxOk.
-        */
-       if ((ISR_TXIDLE & isr) && (dev->tx_done_idx != dev->tx_free_idx)) {
-               spin_lock_irqsave(&dev->misc_lock, flags);
-               dev->IMR_cache |= ISR_TXOK;
-               writel(dev->IMR_cache, dev->base + IMR);
-               spin_unlock_irqrestore(&dev->misc_lock, flags);
-       }
-
-       /* MIB interrupt: one of the statistics counters is about to overflow */
-       if (unlikely(ISR_MIB & isr))
-               ns83820_mib_isr(dev);
-
-       /* PHY: Link up/down/negotiation state change */
-       if (unlikely(ISR_PHY & isr))
-               phy_intr(ndev);
-
-#if 0  /* Still working on the interrupt mitigation strategy */
-       if (dev->ihr)
-               writel(dev->ihr, dev->base + IHR);
-#endif
-}
-
-static void ns83820_do_reset(struct ns83820 *dev, u32 which)
-{
-       Dprintk("resetting chip...\n");
-       writel(which, dev->base + CR);
-       do {
-               schedule();
-       } while (readl(dev->base + CR) & which);
-       Dprintk("okay!\n");
-}
-
-static int ns83820_stop(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-
-       /* FIXME: protect against interrupt handler? */
-       del_timer_sync(&dev->tx_watchdog);
-
-       ns83820_disable_interrupts(dev);
-
-       dev->rx_info.up = 0;
-       synchronize_irq(dev->pci_dev->irq);
-
-       ns83820_do_reset(dev, CR_RST);
-
-       synchronize_irq(dev->pci_dev->irq);
-
-       spin_lock_irq(&dev->misc_lock);
-       dev->IMR_cache &= ~(ISR_TXURN | ISR_TXIDLE | ISR_TXERR | ISR_TXDESC | ISR_TXOK);
-       spin_unlock_irq(&dev->misc_lock);
-
-       ns83820_cleanup_rx(dev);
-       ns83820_cleanup_tx(dev);
-
-       return 0;
-}
-
-static void ns83820_tx_timeout(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-        u32 tx_done_idx;
-       __le32 *desc;
-       unsigned long flags;
-
-       spin_lock_irqsave(&dev->tx_lock, flags);
-
-       tx_done_idx = dev->tx_done_idx;
-       desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
-
-       printk(KERN_INFO "%s: tx_timeout: tx_done_idx=%d free_idx=%d cmdsts=%08x\n",
-               ndev->name,
-               tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS]));
-
-#if defined(DEBUG)
-       {
-               u32 isr;
-               isr = readl(dev->base + ISR);
-               printk("irq: %08x imr: %08x\n", isr, dev->IMR_cache);
-               ns83820_do_isr(ndev, isr);
-       }
-#endif
-
-       do_tx_done(ndev);
-
-       tx_done_idx = dev->tx_done_idx;
-       desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
-
-       printk(KERN_INFO "%s: after: tx_done_idx=%d free_idx=%d cmdsts=%08x\n",
-               ndev->name,
-               tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS]));
-
-       spin_unlock_irqrestore(&dev->tx_lock, flags);
-}
-
-static void ns83820_tx_watch(unsigned long data)
-{
-       struct net_device *ndev = (void *)data;
-       struct ns83820 *dev = PRIV(ndev);
-
-#if defined(DEBUG)
-       printk("ns83820_tx_watch: %u %u %d\n",
-               dev->tx_done_idx, dev->tx_free_idx, atomic_read(&dev->nr_tx_skbs)
-               );
-#endif
-
-       if (time_after(jiffies, dev_trans_start(ndev) + 1*HZ) &&
-           dev->tx_done_idx != dev->tx_free_idx) {
-               printk(KERN_DEBUG "%s: ns83820_tx_watch: %u %u %d\n",
-                       ndev->name,
-                       dev->tx_done_idx, dev->tx_free_idx,
-                       atomic_read(&dev->nr_tx_skbs));
-               ns83820_tx_timeout(ndev);
-       }
-
-       mod_timer(&dev->tx_watchdog, jiffies + 2*HZ);
-}
-
-static int ns83820_open(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       unsigned i;
-       u32 desc;
-       int ret;
-
-       dprintk("ns83820_open\n");
-
-       writel(0, dev->base + PQCR);
-
-       ret = ns83820_setup_rx(ndev);
-       if (ret)
-               goto failed;
-
-       memset(dev->tx_descs, 0, 4 * NR_TX_DESC * DESC_SIZE);
-       for (i=0; i<NR_TX_DESC; i++) {
-               dev->tx_descs[(i * DESC_SIZE) + DESC_LINK]
-                               = cpu_to_le32(
-                                 dev->tx_phy_descs
-                                 + ((i+1) % NR_TX_DESC) * DESC_SIZE * 4);
-       }
-
-       dev->tx_idx = 0;
-       dev->tx_done_idx = 0;
-       desc = dev->tx_phy_descs;
-       writel(0, dev->base + TXDP_HI);
-       writel(desc, dev->base + TXDP);
-
-       init_timer(&dev->tx_watchdog);
-       dev->tx_watchdog.data = (unsigned long)ndev;
-       dev->tx_watchdog.function = ns83820_tx_watch;
-       mod_timer(&dev->tx_watchdog, jiffies + 2*HZ);
-
-       netif_start_queue(ndev);        /* FIXME: wait for phy to come up */
-
-       return 0;
-
-failed:
-       ns83820_stop(ndev);
-       return ret;
-}
-
-static void ns83820_getmac(struct ns83820 *dev, u8 *mac)
-{
-       unsigned i;
-       for (i=0; i<3; i++) {
-               u32 data;
-
-               /* Read from the perfect match memory: this is loaded by
-                * the chip from the EEPROM via the EELOAD self test.
-                */
-               writel(i*2, dev->base + RFCR);
-               data = readl(dev->base + RFDR);
-
-               *mac++ = data;
-               *mac++ = data >> 8;
-       }
-}
-
-static int ns83820_change_mtu(struct net_device *ndev, int new_mtu)
-{
-       if (new_mtu > RX_BUF_SIZE)
-               return -EINVAL;
-       ndev->mtu = new_mtu;
-       return 0;
-}
-
-static void ns83820_set_multicast(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       u8 __iomem *rfcr = dev->base + RFCR;
-       u32 and_mask = 0xffffffff;
-       u32 or_mask = 0;
-       u32 val;
-
-       if (ndev->flags & IFF_PROMISC)
-               or_mask |= RFCR_AAU | RFCR_AAM;
-       else
-               and_mask &= ~(RFCR_AAU | RFCR_AAM);
-
-       if (ndev->flags & IFF_ALLMULTI || netdev_mc_count(ndev))
-               or_mask |= RFCR_AAM;
-       else
-               and_mask &= ~RFCR_AAM;
-
-       spin_lock_irq(&dev->misc_lock);
-       val = (readl(rfcr) & and_mask) | or_mask;
-       /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */
-       writel(val & ~RFCR_RFEN, rfcr);
-       writel(val, rfcr);
-       spin_unlock_irq(&dev->misc_lock);
-}
-
-static void ns83820_run_bist(struct net_device *ndev, const char *name, u32 enable, u32 done, u32 fail)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       int timed_out = 0;
-       unsigned long start;
-       u32 status;
-       int loops = 0;
-
-       dprintk("%s: start %s\n", ndev->name, name);
-
-       start = jiffies;
-
-       writel(enable, dev->base + PTSCR);
-       for (;;) {
-               loops++;
-               status = readl(dev->base + PTSCR);
-               if (!(status & enable))
-                       break;
-               if (status & done)
-                       break;
-               if (status & fail)
-                       break;
-               if (time_after_eq(jiffies, start + HZ)) {
-                       timed_out = 1;
-                       break;
-               }
-               schedule_timeout_uninterruptible(1);
-       }
-
-       if (status & fail)
-               printk(KERN_INFO "%s: %s failed! (0x%08x & 0x%08x)\n",
-                       ndev->name, name, status, fail);
-       else if (timed_out)
-               printk(KERN_INFO "%s: run_bist %s timed out! (%08x)\n",
-                       ndev->name, name, status);
-
-       dprintk("%s: done %s in %d loops\n", ndev->name, name, loops);
-}
-
-#ifdef PHY_CODE_IS_FINISHED
-static void ns83820_mii_write_bit(struct ns83820 *dev, int bit)
-{
-       /* drive MDC low */
-       dev->MEAR_cache &= ~MEAR_MDC;
-       writel(dev->MEAR_cache, dev->base + MEAR);
-       readl(dev->base + MEAR);
-
-       /* enable output, set bit */
-       dev->MEAR_cache |= MEAR_MDDIR;
-       if (bit)
-               dev->MEAR_cache |= MEAR_MDIO;
-       else
-               dev->MEAR_cache &= ~MEAR_MDIO;
-
-       /* set the output bit */
-       writel(dev->MEAR_cache, dev->base + MEAR);
-       readl(dev->base + MEAR);
-
-       /* Wait.  Max clock rate is 2.5MHz, this way we come in under 1MHz */
-       udelay(1);
-
-       /* drive MDC high causing the data bit to be latched */
-       dev->MEAR_cache |= MEAR_MDC;
-       writel(dev->MEAR_cache, dev->base + MEAR);
-       readl(dev->base + MEAR);
-
-       /* Wait again... */
-       udelay(1);
-}
-
-static int ns83820_mii_read_bit(struct ns83820 *dev)
-{
-       int bit;
-
-       /* drive MDC low, disable output */
-       dev->MEAR_cache &= ~MEAR_MDC;
-       dev->MEAR_cache &= ~MEAR_MDDIR;
-       writel(dev->MEAR_cache, dev->base + MEAR);
-       readl(dev->base + MEAR);
-
-       /* Wait.  Max clock rate is 2.5MHz, this way we come in under 1MHz */
-       udelay(1);
-
-       /* drive MDC high causing the data bit to be latched */
-       bit = (readl(dev->base + MEAR) & MEAR_MDIO) ? 1 : 0;
-       dev->MEAR_cache |= MEAR_MDC;
-       writel(dev->MEAR_cache, dev->base + MEAR);
-
-       /* Wait again... */
-       udelay(1);
-
-       return bit;
-}
-
-static unsigned ns83820_mii_read_reg(struct ns83820 *dev, unsigned phy, unsigned reg)
-{
-       unsigned data = 0;
-       int i;
-
-       /* read some garbage so that we eventually sync up */
-       for (i=0; i<64; i++)
-               ns83820_mii_read_bit(dev);
-
-       ns83820_mii_write_bit(dev, 0);  /* start */
-       ns83820_mii_write_bit(dev, 1);
-       ns83820_mii_write_bit(dev, 1);  /* opcode read */
-       ns83820_mii_write_bit(dev, 0);
-
-       /* write out the phy address: 5 bits, msb first */
-       for (i=0; i<5; i++)
-               ns83820_mii_write_bit(dev, phy & (0x10 >> i));
-
-       /* write out the register address, 5 bits, msb first */
-       for (i=0; i<5; i++)
-               ns83820_mii_write_bit(dev, reg & (0x10 >> i));
-
-       ns83820_mii_read_bit(dev);      /* turn around cycles */
-       ns83820_mii_read_bit(dev);
-
-       /* read in the register data, 16 bits msb first */
-       for (i=0; i<16; i++) {
-               data <<= 1;
-               data |= ns83820_mii_read_bit(dev);
-       }
-
-       return data;
-}
-
-static unsigned ns83820_mii_write_reg(struct ns83820 *dev, unsigned phy, unsigned reg, unsigned data)
-{
-       int i;
-
-       /* read some garbage so that we eventually sync up */
-       for (i=0; i<64; i++)
-               ns83820_mii_read_bit(dev);
-
-       ns83820_mii_write_bit(dev, 0);  /* start */
-       ns83820_mii_write_bit(dev, 1);
-       ns83820_mii_write_bit(dev, 0);  /* opcode read */
-       ns83820_mii_write_bit(dev, 1);
-
-       /* write out the phy address: 5 bits, msb first */
-       for (i=0; i<5; i++)
-               ns83820_mii_write_bit(dev, phy & (0x10 >> i));
-
-       /* write out the register address, 5 bits, msb first */
-       for (i=0; i<5; i++)
-               ns83820_mii_write_bit(dev, reg & (0x10 >> i));
-
-       ns83820_mii_read_bit(dev);      /* turn around cycles */
-       ns83820_mii_read_bit(dev);
-
-       /* read in the register data, 16 bits msb first */
-       for (i=0; i<16; i++)
-               ns83820_mii_write_bit(dev, (data >> (15 - i)) & 1);
-
-       return data;
-}
-
-static void ns83820_probe_phy(struct net_device *ndev)
-{
-       struct ns83820 *dev = PRIV(ndev);
-       static int first;
-       int i;
-#define MII_PHYIDR1    0x02
-#define MII_PHYIDR2    0x03
-
-#if 0
-       if (!first) {
-               unsigned tmp;
-               ns83820_mii_read_reg(dev, 1, 0x09);
-               ns83820_mii_write_reg(dev, 1, 0x10, 0x0d3e);
-
-               tmp = ns83820_mii_read_reg(dev, 1, 0x00);
-               ns83820_mii_write_reg(dev, 1, 0x00, tmp | 0x8000);
-               udelay(1300);
-               ns83820_mii_read_reg(dev, 1, 0x09);
-       }
-#endif
-       first = 1;
-
-       for (i=1; i<2; i++) {
-               int j;
-               unsigned a, b;
-               a = ns83820_mii_read_reg(dev, i, MII_PHYIDR1);
-               b = ns83820_mii_read_reg(dev, i, MII_PHYIDR2);
-
-               //printk("%s: phy %d: 0x%04x 0x%04x\n",
-               //      ndev->name, i, a, b);
-
-               for (j=0; j<0x16; j+=4) {
-                       dprintk("%s: [0x%02x] %04x %04x %04x %04x\n",
-                               ndev->name, j,
-                               ns83820_mii_read_reg(dev, i, 0 + j),
-                               ns83820_mii_read_reg(dev, i, 1 + j),
-                               ns83820_mii_read_reg(dev, i, 2 + j),
-                               ns83820_mii_read_reg(dev, i, 3 + j)
-                               );
-               }
-       }
-       {
-               unsigned a, b;
-               /* read firmware version: memory addr is 0x8402 and 0x8403 */
-               ns83820_mii_write_reg(dev, 1, 0x16, 0x000d);
-               ns83820_mii_write_reg(dev, 1, 0x1e, 0x810e);
-               a = ns83820_mii_read_reg(dev, 1, 0x1d);
-
-               ns83820_mii_write_reg(dev, 1, 0x16, 0x000d);
-               ns83820_mii_write_reg(dev, 1, 0x1e, 0x810e);
-               b = ns83820_mii_read_reg(dev, 1, 0x1d);
-               dprintk("version: 0x%04x 0x%04x\n", a, b);
-       }
-}
-#endif
-
-static const struct net_device_ops netdev_ops = {
-       .ndo_open               = ns83820_open,
-       .ndo_stop               = ns83820_stop,
-       .ndo_start_xmit         = ns83820_hard_start_xmit,
-       .ndo_get_stats          = ns83820_get_stats,
-       .ndo_change_mtu         = ns83820_change_mtu,
-       .ndo_set_multicast_list = ns83820_set_multicast,
-       .ndo_validate_addr      = eth_validate_addr,
-       .ndo_set_mac_address    = eth_mac_addr,
-       .ndo_tx_timeout         = ns83820_tx_timeout,
-};
-
-static int __devinit ns83820_init_one(struct pci_dev *pci_dev,
-                                     const struct pci_device_id *id)
-{
-       struct net_device *ndev;
-       struct ns83820 *dev;
-       long addr;
-       int err;
-       int using_dac = 0;
-
-       /* See if we can set the dma mask early on; failure is fatal. */
-       if (sizeof(dma_addr_t) == 8 &&
-               !pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64))) {
-               using_dac = 1;
-       } else if (!pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
-               using_dac = 0;
-       } else {
-               dev_warn(&pci_dev->dev, "pci_set_dma_mask failed!\n");
-               return -ENODEV;
-       }
-
-       ndev = alloc_etherdev(sizeof(struct ns83820));
-       err = -ENOMEM;
-       if (!ndev)
-               goto out;
-
-       dev = PRIV(ndev);
-       dev->ndev = ndev;
-
-       spin_lock_init(&dev->rx_info.lock);
-       spin_lock_init(&dev->tx_lock);
-       spin_lock_init(&dev->misc_lock);
-       dev->pci_dev = pci_dev;
-
-       SET_NETDEV_DEV(ndev, &pci_dev->dev);
-
-       INIT_WORK(&dev->tq_refill, queue_refill);
-       tasklet_init(&dev->rx_tasklet, rx_action, (unsigned long)ndev);
-
-       err = pci_enable_device(pci_dev);
-       if (err) {
-               dev_info(&pci_dev->dev, "pci_enable_dev failed: %d\n", err);
-               goto out_free;
-       }
-
-       pci_set_master(pci_dev);
-       addr = pci_resource_start(pci_dev, 1);
-       dev->base = ioremap_nocache(addr, PAGE_SIZE);
-       dev->tx_descs = pci_alloc_consistent(pci_dev,
-                       4 * DESC_SIZE * NR_TX_DESC, &dev->tx_phy_descs);
-       dev->rx_info.descs = pci_alloc_consistent(pci_dev,
-                       4 * DESC_SIZE * NR_RX_DESC, &dev->rx_info.phy_descs);
-       err = -ENOMEM;
-       if (!dev->base || !dev->tx_descs || !dev->rx_info.descs)
-               goto out_disable;
-
-       dprintk("%p: %08lx  %p: %08lx\n",
-               dev->tx_descs, (long)dev->tx_phy_descs,
-               dev->rx_info.descs, (long)dev->rx_info.phy_descs);
-
-       ns83820_disable_interrupts(dev);
-
-       dev->IMR_cache = 0;
-
-       err = request_irq(pci_dev->irq, ns83820_irq, IRQF_SHARED,
-                         DRV_NAME, ndev);
-       if (err) {
-               dev_info(&pci_dev->dev, "unable to register irq %d, err %d\n",
-                       pci_dev->irq, err);
-               goto out_disable;
-       }
-
-       /*
-        * FIXME: we are holding rtnl_lock() over obscenely long area only
-        * because some of the setup code uses dev->name.  It's Wrong(tm) -
-        * we should be using driver-specific names for all that stuff.
-        * For now that will do, but we really need to come back and kill
-        * most of the dev_alloc_name() users later.
-        */
-       rtnl_lock();
-       err = dev_alloc_name(ndev, ndev->name);
-       if (err < 0) {
-               dev_info(&pci_dev->dev, "unable to get netdev name: %d\n", err);
-               goto out_free_irq;
-       }
-
-       printk("%s: ns83820.c: 0x22c: %08x, subsystem: %04x:%04x\n",
-               ndev->name, le32_to_cpu(readl(dev->base + 0x22c)),
-               pci_dev->subsystem_vendor, pci_dev->subsystem_device);
-
-       ndev->netdev_ops = &netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ops);
-       ndev->watchdog_timeo = 5 * HZ;
-       pci_set_drvdata(pci_dev, ndev);
-
-       ns83820_do_reset(dev, CR_RST);
-
-       /* Must reset the ram bist before running it */
-       writel(PTSCR_RBIST_RST, dev->base + PTSCR);
-       ns83820_run_bist(ndev, "sram bist",   PTSCR_RBIST_EN,
-                        PTSCR_RBIST_DONE, PTSCR_RBIST_FAIL);
-       ns83820_run_bist(ndev, "eeprom bist", PTSCR_EEBIST_EN, 0,
-                        PTSCR_EEBIST_FAIL);
-       ns83820_run_bist(ndev, "eeprom load", PTSCR_EELOAD_EN, 0, 0);
-
-       /* I love config registers */
-       dev->CFG_cache = readl(dev->base + CFG);
-
-       if ((dev->CFG_cache & CFG_PCI64_DET)) {
-               printk(KERN_INFO "%s: detected 64 bit PCI data bus.\n",
-                       ndev->name);
-               /*dev->CFG_cache |= CFG_DATA64_EN;*/
-               if (!(dev->CFG_cache & CFG_DATA64_EN))
-                       printk(KERN_INFO "%s: EEPROM did not enable 64 bit bus.  Disabled.\n",
-                               ndev->name);
-       } else
-               dev->CFG_cache &= ~(CFG_DATA64_EN);
-
-       dev->CFG_cache &= (CFG_TBI_EN  | CFG_MRM_DIS   | CFG_MWI_DIS |
-                          CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 |
-                          CFG_M64ADDR);
-       dev->CFG_cache |= CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS |
-                         CFG_EXTSTS_EN   | CFG_EXD         | CFG_PESEL;
-       dev->CFG_cache |= CFG_REQALG;
-       dev->CFG_cache |= CFG_POW;
-       dev->CFG_cache |= CFG_TMRTEST;
-
-       /* When compiled with 64 bit addressing, we must always enable
-        * the 64 bit descriptor format.
-        */
-       if (sizeof(dma_addr_t) == 8)
-               dev->CFG_cache |= CFG_M64ADDR;
-       if (using_dac)
-               dev->CFG_cache |= CFG_T64ADDR;
-
-       /* Big endian mode does not seem to do what the docs suggest */
-       dev->CFG_cache &= ~CFG_BEM;
-
-       /* setup optical transceiver if we have one */
-       if (dev->CFG_cache & CFG_TBI_EN) {
-               printk(KERN_INFO "%s: enabling optical transceiver\n",
-                       ndev->name);
-               writel(readl(dev->base + GPIOR) | 0x3e8, dev->base + GPIOR);
-
-               /* setup auto negotiation feature advertisement */
-               writel(readl(dev->base + TANAR)
-                      | TANAR_HALF_DUP | TANAR_FULL_DUP,
-                      dev->base + TANAR);
-
-               /* start auto negotiation */
-               writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN,
-                      dev->base + TBICR);
-               writel(TBICR_MR_AN_ENABLE, dev->base + TBICR);
-               dev->linkstate = LINK_AUTONEGOTIATE;
-
-               dev->CFG_cache |= CFG_MODE_1000;
-       }
-
-       writel(dev->CFG_cache, dev->base + CFG);
-       dprintk("CFG: %08x\n", dev->CFG_cache);
-
-       if (reset_phy) {
-               printk(KERN_INFO "%s: resetting phy\n", ndev->name);
-               writel(dev->CFG_cache | CFG_PHY_RST, dev->base + CFG);
-               msleep(10);
-               writel(dev->CFG_cache, dev->base + CFG);
-       }
-
-#if 0  /* Huh?  This sets the PCI latency register.  Should be done via
-        * the PCI layer.  FIXME.
-        */
-       if (readl(dev->base + SRR))
-               writel(readl(dev->base+0x20c) | 0xfe00, dev->base + 0x20c);
-#endif
-
-       /* Note!  The DMA burst size interacts with packet
-        * transmission, such that the largest packet that
-        * can be transmitted is 8192 - FLTH - burst size.
-        * If only the transmit fifo was larger...
-        */
-       /* Ramit : 1024 DMA is not a good idea, it ends up banging
-        * some DELL and COMPAQ SMP systems */
-       writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA512
-               | ((1600 / 32) * 0x100),
-               dev->base + TXCFG);
-
-       /* Flush the interrupt holdoff timer */
-       writel(0x000, dev->base + IHR);
-       writel(0x100, dev->base + IHR);
-       writel(0x000, dev->base + IHR);
-
-       /* Set Rx to full duplex, don't accept runt, errored, long or length
-        * range errored packets.  Use 512 byte DMA.
-        */
-       /* Ramit : 1024 DMA is not a good idea, it ends up banging
-        * some DELL and COMPAQ SMP systems
-        * Turn on ALP, only we are accpeting Jumbo Packets */
-       writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD
-               | RXCFG_STRIPCRC
-               //| RXCFG_ALP
-               | (RXCFG_MXDMA512) | 0, dev->base + RXCFG);
-
-       /* Disable priority queueing */
-       writel(0, dev->base + PQCR);
-
-       /* Enable IP checksum validation and detetion of VLAN headers.
-        * Note: do not set the reject options as at least the 0x102
-        * revision of the chip does not properly accept IP fragments
-        * at least for UDP.
-        */
-       /* Ramit : Be sure to turn on RXCFG_ARP if VLAN's are enabled, since
-        * the MAC it calculates the packetsize AFTER stripping the VLAN
-        * header, and if a VLAN Tagged packet of 64 bytes is received (like
-        * a ping with a VLAN header) then the card, strips the 4 byte VLAN
-        * tag and then checks the packet size, so if RXCFG_ARP is not enabled,
-        * it discrards it!.  These guys......
-        * also turn on tag stripping if hardware acceleration is enabled
-        */
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
-#define VRCR_INIT_VALUE (VRCR_IPEN|VRCR_VTDEN|VRCR_VTREN)
-#else
-#define VRCR_INIT_VALUE (VRCR_IPEN|VRCR_VTDEN)
-#endif
-       writel(VRCR_INIT_VALUE, dev->base + VRCR);
-
-       /* Enable per-packet TCP/UDP/IP checksumming
-        * and per packet vlan tag insertion if
-        * vlan hardware acceleration is enabled
-        */
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
-#define VTCR_INIT_VALUE (VTCR_PPCHK|VTCR_VPPTI)
-#else
-#define VTCR_INIT_VALUE VTCR_PPCHK
-#endif
-       writel(VTCR_INIT_VALUE, dev->base + VTCR);
-
-       /* Ramit : Enable async and sync pause frames */
-       /* writel(0, dev->base + PCR); */
-       writel((PCR_PS_MCAST | PCR_PS_DA | PCR_PSEN | PCR_FFLO_4K |
-               PCR_FFHI_8K | PCR_STLO_4 | PCR_STHI_8 | PCR_PAUSE_CNT),
-               dev->base + PCR);
-
-       /* Disable Wake On Lan */
-       writel(0, dev->base + WCSR);
-
-       ns83820_getmac(dev, ndev->dev_addr);
-
-       /* Yes, we support dumb IP checksum on transmit */
-       ndev->features |= NETIF_F_SG;
-       ndev->features |= NETIF_F_IP_CSUM;
-
-#ifdef NS83820_VLAN_ACCEL_SUPPORT
-       /* We also support hardware vlan acceleration */
-       ndev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-#endif
-
-       if (using_dac) {
-               printk(KERN_INFO "%s: using 64 bit addressing.\n",
-                       ndev->name);
-               ndev->features |= NETIF_F_HIGHDMA;
-       }
-
-       printk(KERN_INFO "%s: ns83820 v" VERSION ": DP83820 v%u.%u: %pM io=0x%08lx irq=%d f=%s\n",
-               ndev->name,
-               (unsigned)readl(dev->base + SRR) >> 8,
-               (unsigned)readl(dev->base + SRR) & 0xff,
-               ndev->dev_addr, addr, pci_dev->irq,
-               (ndev->features & NETIF_F_HIGHDMA) ? "h,sg" : "sg"
-               );
-
-#ifdef PHY_CODE_IS_FINISHED
-       ns83820_probe_phy(ndev);
-#endif
-
-       err = register_netdevice(ndev);
-       if (err) {
-               printk(KERN_INFO "ns83820: unable to register netdev: %d\n", err);
-               goto out_cleanup;
-       }
-       rtnl_unlock();
-
-       return 0;
-
-out_cleanup:
-       ns83820_disable_interrupts(dev); /* paranoia */
-out_free_irq:
-       rtnl_unlock();
-       free_irq(pci_dev->irq, ndev);
-out_disable:
-       if (dev->base)
-               iounmap(dev->base);
-       pci_free_consistent(pci_dev, 4 * DESC_SIZE * NR_TX_DESC, dev->tx_descs, dev->tx_phy_descs);
-       pci_free_consistent(pci_dev, 4 * DESC_SIZE * NR_RX_DESC, dev->rx_info.descs, dev->rx_info.phy_descs);
-       pci_disable_device(pci_dev);
-out_free:
-       free_netdev(ndev);
-       pci_set_drvdata(pci_dev, NULL);
-out:
-       return err;
-}
-
-static void __devexit ns83820_remove_one(struct pci_dev *pci_dev)
-{
-       struct net_device *ndev = pci_get_drvdata(pci_dev);
-       struct ns83820 *dev = PRIV(ndev); /* ok even if NULL */
-
-       if (!ndev)                      /* paranoia */
-               return;
-
-       ns83820_disable_interrupts(dev); /* paranoia */
-
-       unregister_netdev(ndev);
-       free_irq(dev->pci_dev->irq, ndev);
-       iounmap(dev->base);
-       pci_free_consistent(dev->pci_dev, 4 * DESC_SIZE * NR_TX_DESC,
-                       dev->tx_descs, dev->tx_phy_descs);
-       pci_free_consistent(dev->pci_dev, 4 * DESC_SIZE * NR_RX_DESC,
-                       dev->rx_info.descs, dev->rx_info.phy_descs);
-       pci_disable_device(dev->pci_dev);
-       free_netdev(ndev);
-       pci_set_drvdata(pci_dev, NULL);
-}
-
-static DEFINE_PCI_DEVICE_TABLE(ns83820_pci_tbl) = {
-       { 0x100b, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, .driver_data = 0, },
-       { 0, },
-};
-
-static struct pci_driver driver = {
-       .name           = "ns83820",
-       .id_table       = ns83820_pci_tbl,
-       .probe          = ns83820_init_one,
-       .remove         = __devexit_p(ns83820_remove_one),
-#if 0  /* FIXME: implement */
-       .suspend        = ,
-       .resume         = ,
-#endif
-};
-
-
-static int __init ns83820_init(void)
-{
-       printk(KERN_INFO "ns83820.c: National Semiconductor DP83820 10/100/1000 driver.\n");
-       return pci_register_driver(&driver);
-}
-
-static void __exit ns83820_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-MODULE_AUTHOR("Benjamin LaHaise <bcrl@kvack.org>");
-MODULE_DESCRIPTION("National Semiconductor DP83820 10/100/1000 driver");
-MODULE_LICENSE("GPL");
-
-MODULE_DEVICE_TABLE(pci, ns83820_pci_tbl);
-
-module_param(lnksts, int, 0);
-MODULE_PARM_DESC(lnksts, "Polarity of LNKSTS bit");
-
-module_param(ihr, int, 0);
-MODULE_PARM_DESC(ihr, "Time in 100 us increments to delay interrupts (range 0-127)");
-
-module_param(reset_phy, int, 0);
-MODULE_PARM_DESC(reset_phy, "Set to 1 to reset the PHY on startup");
-
-module_init(ns83820_init);
-module_exit(ns83820_exit);
diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c
deleted file mode 100644 (file)
index 26e25d7..0000000
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * sonic.c
- *
- * (C) 2005 Finn Thain
- *
- * Converted to DMA API, added zero-copy buffer handling, and
- * (from the mac68k project) introduced dhd's support for 16-bit cards.
- *
- * (C) 1996,1998 by Thomas Bogendoerfer (tsbogend@alpha.franken.de)
- *
- * This driver is based on work from Andreas Busse, but most of
- * the code is rewritten.
- *
- * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
- *
- *    Core code included by system sonic drivers
- *
- * And... partially rewritten again by David Huggins-Daines in order
- * to cope with screwed up Macintosh NICs that may or may not use
- * 16-bit DMA.
- *
- * (C) 1999 David Huggins-Daines <dhd@debian.org>
- *
- */
-
-/*
- * Sources: Olivetti M700-10 Risc Personal Computer hardware handbook,
- * National Semiconductors data sheet for the DP83932B Sonic Ethernet
- * controller, and the files "8390.c" and "skeleton.c" in this directory.
- *
- * Additional sources: Nat Semi data sheet for the DP83932C and Nat Semi
- * Application Note AN-746, the files "lance.c" and "ibmlana.c". See also
- * the NetBSD file "sys/arch/mac68k/dev/if_sn.c".
- */
-
-
-
-/*
- * Open/initialize the SONIC controller.
- *
- * This routine should set everything up anew at each open, even
- *  registers that "should" only need to be set once at boot, so that
- *  there is non-reboot way to recover if something goes wrong.
- */
-static int sonic_open(struct net_device *dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       int i;
-
-       if (sonic_debug > 2)
-               printk("sonic_open: initializing sonic driver.\n");
-
-       for (i = 0; i < SONIC_NUM_RRS; i++) {
-               struct sk_buff *skb = dev_alloc_skb(SONIC_RBSIZE + 2);
-               if (skb == NULL) {
-                       while(i > 0) { /* free any that were allocated successfully */
-                               i--;
-                               dev_kfree_skb(lp->rx_skb[i]);
-                               lp->rx_skb[i] = NULL;
-                       }
-                       printk(KERN_ERR "%s: couldn't allocate receive buffers\n",
-                              dev->name);
-                       return -ENOMEM;
-               }
-               /* align IP header unless DMA requires otherwise */
-               if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
-                       skb_reserve(skb, 2);
-               lp->rx_skb[i] = skb;
-       }
-
-       for (i = 0; i < SONIC_NUM_RRS; i++) {
-               dma_addr_t laddr = dma_map_single(lp->device, skb_put(lp->rx_skb[i], SONIC_RBSIZE),
-                                                 SONIC_RBSIZE, DMA_FROM_DEVICE);
-               if (!laddr) {
-                       while(i > 0) { /* free any that were mapped successfully */
-                               i--;
-                               dma_unmap_single(lp->device, lp->rx_laddr[i], SONIC_RBSIZE, DMA_FROM_DEVICE);
-                               lp->rx_laddr[i] = (dma_addr_t)0;
-                       }
-                       for (i = 0; i < SONIC_NUM_RRS; i++) {
-                               dev_kfree_skb(lp->rx_skb[i]);
-                               lp->rx_skb[i] = NULL;
-                       }
-                       printk(KERN_ERR "%s: couldn't map rx DMA buffers\n",
-                              dev->name);
-                       return -ENOMEM;
-               }
-               lp->rx_laddr[i] = laddr;
-       }
-
-       /*
-        * Initialize the SONIC
-        */
-       sonic_init(dev);
-
-       netif_start_queue(dev);
-
-       if (sonic_debug > 2)
-               printk("sonic_open: Initialization done.\n");
-
-       return 0;
-}
-
-
-/*
- * Close the SONIC device
- */
-static int sonic_close(struct net_device *dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       int i;
-
-       if (sonic_debug > 2)
-               printk("sonic_close\n");
-
-       netif_stop_queue(dev);
-
-       /*
-        * stop the SONIC, disable interrupts
-        */
-       SONIC_WRITE(SONIC_IMR, 0);
-       SONIC_WRITE(SONIC_ISR, 0x7fff);
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
-
-       /* unmap and free skbs that haven't been transmitted */
-       for (i = 0; i < SONIC_NUM_TDS; i++) {
-               if(lp->tx_laddr[i]) {
-                       dma_unmap_single(lp->device, lp->tx_laddr[i], lp->tx_len[i], DMA_TO_DEVICE);
-                       lp->tx_laddr[i] = (dma_addr_t)0;
-               }
-               if(lp->tx_skb[i]) {
-                       dev_kfree_skb(lp->tx_skb[i]);
-                       lp->tx_skb[i] = NULL;
-               }
-       }
-
-       /* unmap and free the receive buffers */
-       for (i = 0; i < SONIC_NUM_RRS; i++) {
-               if(lp->rx_laddr[i]) {
-                       dma_unmap_single(lp->device, lp->rx_laddr[i], SONIC_RBSIZE, DMA_FROM_DEVICE);
-                       lp->rx_laddr[i] = (dma_addr_t)0;
-               }
-               if(lp->rx_skb[i]) {
-                       dev_kfree_skb(lp->rx_skb[i]);
-                       lp->rx_skb[i] = NULL;
-               }
-       }
-
-       return 0;
-}
-
-static void sonic_tx_timeout(struct net_device *dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       int i;
-       /*
-        * put the Sonic into software-reset mode and
-        * disable all interrupts before releasing DMA buffers
-        */
-       SONIC_WRITE(SONIC_IMR, 0);
-       SONIC_WRITE(SONIC_ISR, 0x7fff);
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
-       /* We could resend the original skbs. Easier to re-initialise. */
-       for (i = 0; i < SONIC_NUM_TDS; i++) {
-               if(lp->tx_laddr[i]) {
-                       dma_unmap_single(lp->device, lp->tx_laddr[i], lp->tx_len[i], DMA_TO_DEVICE);
-                       lp->tx_laddr[i] = (dma_addr_t)0;
-               }
-               if(lp->tx_skb[i]) {
-                       dev_kfree_skb(lp->tx_skb[i]);
-                       lp->tx_skb[i] = NULL;
-               }
-       }
-       /* Try to restart the adaptor. */
-       sonic_init(dev);
-       lp->stats.tx_errors++;
-       dev->trans_start = jiffies; /* prevent tx timeout */
-       netif_wake_queue(dev);
-}
-
-/*
- * transmit packet
- *
- * Appends new TD during transmission thus avoiding any TX interrupts
- * until we run out of TDs.
- * This routine interacts closely with the ISR in that it may,
- *   set tx_skb[i]
- *   reset the status flags of the new TD
- *   set and reset EOL flags
- *   stop the tx queue
- * The ISR interacts with this routine in various ways. It may,
- *   reset tx_skb[i]
- *   test the EOL and status flags of the TDs
- *   wake the tx queue
- * Concurrently with all of this, the SONIC is potentially writing to
- * the status flags of the TDs.
- * Until some mutual exclusion is added, this code will not work with SMP. However,
- * MIPS Jazz machines and m68k Macs were all uni-processor machines.
- */
-
-static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       dma_addr_t laddr;
-       int length;
-       int entry = lp->next_tx;
-
-       if (sonic_debug > 2)
-               printk("sonic_send_packet: skb=%p, dev=%p\n", skb, dev);
-
-       length = skb->len;
-       if (length < ETH_ZLEN) {
-               if (skb_padto(skb, ETH_ZLEN))
-                       return NETDEV_TX_OK;
-               length = ETH_ZLEN;
-       }
-
-       /*
-        * Map the packet data into the logical DMA address space
-        */
-
-       laddr = dma_map_single(lp->device, skb->data, length, DMA_TO_DEVICE);
-       if (!laddr) {
-               printk(KERN_ERR "%s: failed to map tx DMA buffer.\n", dev->name);
-               dev_kfree_skb(skb);
-               return NETDEV_TX_BUSY;
-       }
-
-       sonic_tda_put(dev, entry, SONIC_TD_STATUS, 0);       /* clear status */
-       sonic_tda_put(dev, entry, SONIC_TD_FRAG_COUNT, 1);   /* single fragment */
-       sonic_tda_put(dev, entry, SONIC_TD_PKTSIZE, length); /* length of packet */
-       sonic_tda_put(dev, entry, SONIC_TD_FRAG_PTR_L, laddr & 0xffff);
-       sonic_tda_put(dev, entry, SONIC_TD_FRAG_PTR_H, laddr >> 16);
-       sonic_tda_put(dev, entry, SONIC_TD_FRAG_SIZE, length);
-       sonic_tda_put(dev, entry, SONIC_TD_LINK,
-               sonic_tda_get(dev, entry, SONIC_TD_LINK) | SONIC_EOL);
-
-       /*
-        * Must set tx_skb[entry] only after clearing status, and
-        * before clearing EOL and before stopping queue
-        */
-       wmb();
-       lp->tx_len[entry] = length;
-       lp->tx_laddr[entry] = laddr;
-       lp->tx_skb[entry] = skb;
-
-       wmb();
-       sonic_tda_put(dev, lp->eol_tx, SONIC_TD_LINK,
-                                 sonic_tda_get(dev, lp->eol_tx, SONIC_TD_LINK) & ~SONIC_EOL);
-       lp->eol_tx = entry;
-
-       lp->next_tx = (entry + 1) & SONIC_TDS_MASK;
-       if (lp->tx_skb[lp->next_tx] != NULL) {
-               /* The ring is full, the ISR has yet to process the next TD. */
-               if (sonic_debug > 3)
-                       printk("%s: stopping queue\n", dev->name);
-               netif_stop_queue(dev);
-               /* after this packet, wait for ISR to free up some TDAs */
-       } else netif_start_queue(dev);
-
-       if (sonic_debug > 2)
-               printk("sonic_send_packet: issuing Tx command\n");
-
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP);
-
-       return NETDEV_TX_OK;
-}
-
-/*
- * The typical workload of the driver:
- * Handle the network interface interrupts.
- */
-static irqreturn_t sonic_interrupt(int irq, void *dev_id)
-{
-       struct net_device *dev = dev_id;
-       struct sonic_local *lp = netdev_priv(dev);
-       int status;
-
-       if (!(status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT))
-               return IRQ_NONE;
-
-       do {
-               if (status & SONIC_INT_PKTRX) {
-                       if (sonic_debug > 2)
-                               printk("%s: packet rx\n", dev->name);
-                       sonic_rx(dev);  /* got packet(s) */
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_PKTRX); /* clear the interrupt */
-               }
-
-               if (status & SONIC_INT_TXDN) {
-                       int entry = lp->cur_tx;
-                       int td_status;
-                       int freed_some = 0;
-
-                       /* At this point, cur_tx is the index of a TD that is one of:
-                        *   unallocated/freed                          (status set   & tx_skb[entry] clear)
-                        *   allocated and sent                         (status set   & tx_skb[entry] set  )
-                        *   allocated and not yet sent                 (status clear & tx_skb[entry] set  )
-                        *   still being allocated by sonic_send_packet (status clear & tx_skb[entry] clear)
-                        */
-
-                       if (sonic_debug > 2)
-                               printk("%s: tx done\n", dev->name);
-
-                       while (lp->tx_skb[entry] != NULL) {
-                               if ((td_status = sonic_tda_get(dev, entry, SONIC_TD_STATUS)) == 0)
-                                       break;
-
-                               if (td_status & 0x0001) {
-                                       lp->stats.tx_packets++;
-                                       lp->stats.tx_bytes += sonic_tda_get(dev, entry, SONIC_TD_PKTSIZE);
-                               } else {
-                                       lp->stats.tx_errors++;
-                                       if (td_status & 0x0642)
-                                               lp->stats.tx_aborted_errors++;
-                                       if (td_status & 0x0180)
-                                               lp->stats.tx_carrier_errors++;
-                                       if (td_status & 0x0020)
-                                               lp->stats.tx_window_errors++;
-                                       if (td_status & 0x0004)
-                                               lp->stats.tx_fifo_errors++;
-                               }
-
-                               /* We must free the original skb */
-                               dev_kfree_skb_irq(lp->tx_skb[entry]);
-                               lp->tx_skb[entry] = NULL;
-                               /* and unmap DMA buffer */
-                               dma_unmap_single(lp->device, lp->tx_laddr[entry], lp->tx_len[entry], DMA_TO_DEVICE);
-                               lp->tx_laddr[entry] = (dma_addr_t)0;
-                               freed_some = 1;
-
-                               if (sonic_tda_get(dev, entry, SONIC_TD_LINK) & SONIC_EOL) {
-                                       entry = (entry + 1) & SONIC_TDS_MASK;
-                                       break;
-                               }
-                               entry = (entry + 1) & SONIC_TDS_MASK;
-                       }
-
-                       if (freed_some || lp->tx_skb[entry] == NULL)
-                               netif_wake_queue(dev);  /* The ring is no longer full */
-                       lp->cur_tx = entry;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_TXDN); /* clear the interrupt */
-               }
-
-               /*
-                * check error conditions
-                */
-               if (status & SONIC_INT_RFO) {
-                       if (sonic_debug > 1)
-                               printk("%s: rx fifo overrun\n", dev->name);
-                       lp->stats.rx_fifo_errors++;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RFO); /* clear the interrupt */
-               }
-               if (status & SONIC_INT_RDE) {
-                       if (sonic_debug > 1)
-                               printk("%s: rx descriptors exhausted\n", dev->name);
-                       lp->stats.rx_dropped++;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RDE); /* clear the interrupt */
-               }
-               if (status & SONIC_INT_RBAE) {
-                       if (sonic_debug > 1)
-                               printk("%s: rx buffer area exceeded\n", dev->name);
-                       lp->stats.rx_dropped++;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RBAE); /* clear the interrupt */
-               }
-
-               /* counter overruns; all counters are 16bit wide */
-               if (status & SONIC_INT_FAE) {
-                       lp->stats.rx_frame_errors += 65536;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_FAE); /* clear the interrupt */
-               }
-               if (status & SONIC_INT_CRC) {
-                       lp->stats.rx_crc_errors += 65536;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_CRC); /* clear the interrupt */
-               }
-               if (status & SONIC_INT_MP) {
-                       lp->stats.rx_missed_errors += 65536;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_MP); /* clear the interrupt */
-               }
-
-               /* transmit error */
-               if (status & SONIC_INT_TXER) {
-                       if ((SONIC_READ(SONIC_TCR) & SONIC_TCR_FU) && (sonic_debug > 2))
-                               printk(KERN_ERR "%s: tx fifo underrun\n", dev->name);
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_TXER); /* clear the interrupt */
-               }
-
-               /* bus retry */
-               if (status & SONIC_INT_BR) {
-                       printk(KERN_ERR "%s: Bus retry occurred! Device interrupt disabled.\n",
-                               dev->name);
-                       /* ... to help debug DMA problems causing endless interrupts. */
-                       /* Bounce the eth interface to turn on the interrupt again. */
-                       SONIC_WRITE(SONIC_IMR, 0);
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_BR); /* clear the interrupt */
-               }
-
-               /* load CAM done */
-               if (status & SONIC_INT_LCD)
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_LCD); /* clear the interrupt */
-       } while((status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT));
-       return IRQ_HANDLED;
-}
-
-/*
- * We have a good packet(s), pass it/them up the network stack.
- */
-static void sonic_rx(struct net_device *dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       int status;
-       int entry = lp->cur_rx;
-
-       while (sonic_rda_get(dev, entry, SONIC_RD_IN_USE) == 0) {
-               struct sk_buff *used_skb;
-               struct sk_buff *new_skb;
-               dma_addr_t new_laddr;
-               u16 bufadr_l;
-               u16 bufadr_h;
-               int pkt_len;
-
-               status = sonic_rda_get(dev, entry, SONIC_RD_STATUS);
-               if (status & SONIC_RCR_PRX) {
-                       /* Malloc up new buffer. */
-                       new_skb = dev_alloc_skb(SONIC_RBSIZE + 2);
-                       if (new_skb == NULL) {
-                               printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n", dev->name);
-                               lp->stats.rx_dropped++;
-                               break;
-                       }
-                       /* provide 16 byte IP header alignment unless DMA requires otherwise */
-                       if(SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
-                               skb_reserve(new_skb, 2);
-
-                       new_laddr = dma_map_single(lp->device, skb_put(new_skb, SONIC_RBSIZE),
-                                              SONIC_RBSIZE, DMA_FROM_DEVICE);
-                       if (!new_laddr) {
-                               dev_kfree_skb(new_skb);
-                               printk(KERN_ERR "%s: Failed to map rx buffer, dropping packet.\n", dev->name);
-                               lp->stats.rx_dropped++;
-                               break;
-                       }
-
-                       /* now we have a new skb to replace it, pass the used one up the stack */
-                       dma_unmap_single(lp->device, lp->rx_laddr[entry], SONIC_RBSIZE, DMA_FROM_DEVICE);
-                       used_skb = lp->rx_skb[entry];
-                       pkt_len = sonic_rda_get(dev, entry, SONIC_RD_PKTLEN);
-                       skb_trim(used_skb, pkt_len);
-                       used_skb->protocol = eth_type_trans(used_skb, dev);
-                       netif_rx(used_skb);
-                       lp->stats.rx_packets++;
-                       lp->stats.rx_bytes += pkt_len;
-
-                       /* and insert the new skb */
-                       lp->rx_laddr[entry] = new_laddr;
-                       lp->rx_skb[entry] = new_skb;
-
-                       bufadr_l = (unsigned long)new_laddr & 0xffff;
-                       bufadr_h = (unsigned long)new_laddr >> 16;
-                       sonic_rra_put(dev, entry, SONIC_RR_BUFADR_L, bufadr_l);
-                       sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, bufadr_h);
-               } else {
-                       /* This should only happen, if we enable accepting broken packets. */
-                       lp->stats.rx_errors++;
-                       if (status & SONIC_RCR_FAER)
-                               lp->stats.rx_frame_errors++;
-                       if (status & SONIC_RCR_CRCR)
-                               lp->stats.rx_crc_errors++;
-               }
-               if (status & SONIC_RCR_LPKT) {
-                       /*
-                        * this was the last packet out of the current receive buffer
-                        * give the buffer back to the SONIC
-                        */
-                       lp->cur_rwp += SIZEOF_SONIC_RR * SONIC_BUS_SCALE(lp->dma_bitmode);
-                       if (lp->cur_rwp >= lp->rra_end) lp->cur_rwp = lp->rra_laddr & 0xffff;
-                       SONIC_WRITE(SONIC_RWP, lp->cur_rwp);
-                       if (SONIC_READ(SONIC_ISR) & SONIC_INT_RBE) {
-                               if (sonic_debug > 2)
-                                       printk("%s: rx buffer exhausted\n", dev->name);
-                               SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE); /* clear the flag */
-                       }
-               } else
-                       printk(KERN_ERR "%s: rx desc without RCR_LPKT. Shouldn't happen !?\n",
-                            dev->name);
-               /*
-                * give back the descriptor
-                */
-               sonic_rda_put(dev, entry, SONIC_RD_LINK,
-                       sonic_rda_get(dev, entry, SONIC_RD_LINK) | SONIC_EOL);
-               sonic_rda_put(dev, entry, SONIC_RD_IN_USE, 1);
-               sonic_rda_put(dev, lp->eol_rx, SONIC_RD_LINK,
-                       sonic_rda_get(dev, lp->eol_rx, SONIC_RD_LINK) & ~SONIC_EOL);
-               lp->eol_rx = entry;
-               lp->cur_rx = entry = (entry + 1) & SONIC_RDS_MASK;
-       }
-       /*
-        * If any worth-while packets have been received, netif_rx()
-        * has done a mark_bh(NET_BH) for us and will work on them
-        * when we get to the bottom-half routine.
-        */
-}
-
-
-/*
- * Get the current statistics.
- * This may be called with the device open or closed.
- */
-static struct net_device_stats *sonic_get_stats(struct net_device *dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-
-       /* read the tally counter from the SONIC and reset them */
-       lp->stats.rx_crc_errors += SONIC_READ(SONIC_CRCT);
-       SONIC_WRITE(SONIC_CRCT, 0xffff);
-       lp->stats.rx_frame_errors += SONIC_READ(SONIC_FAET);
-       SONIC_WRITE(SONIC_FAET, 0xffff);
-       lp->stats.rx_missed_errors += SONIC_READ(SONIC_MPT);
-       SONIC_WRITE(SONIC_MPT, 0xffff);
-
-       return &lp->stats;
-}
-
-
-/*
- * Set or clear the multicast filter for this adaptor.
- */
-static void sonic_multicast_list(struct net_device *dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       unsigned int rcr;
-       struct netdev_hw_addr *ha;
-       unsigned char *addr;
-       int i;
-
-       rcr = SONIC_READ(SONIC_RCR) & ~(SONIC_RCR_PRO | SONIC_RCR_AMC);
-       rcr |= SONIC_RCR_BRD;   /* accept broadcast packets */
-
-       if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
-               rcr |= SONIC_RCR_PRO;
-       } else {
-               if ((dev->flags & IFF_ALLMULTI) ||
-                   (netdev_mc_count(dev) > 15)) {
-                       rcr |= SONIC_RCR_AMC;
-               } else {
-                       if (sonic_debug > 2)
-                               printk("sonic_multicast_list: mc_count %d\n",
-                                      netdev_mc_count(dev));
-                       sonic_set_cam_enable(dev, 1);  /* always enable our own address */
-                       i = 1;
-                       netdev_for_each_mc_addr(ha, dev) {
-                               addr = ha->addr;
-                               sonic_cda_put(dev, i, SONIC_CD_CAP0, addr[1] << 8 | addr[0]);
-                               sonic_cda_put(dev, i, SONIC_CD_CAP1, addr[3] << 8 | addr[2]);
-                               sonic_cda_put(dev, i, SONIC_CD_CAP2, addr[5] << 8 | addr[4]);
-                               sonic_set_cam_enable(dev, sonic_get_cam_enable(dev) | (1 << i));
-                               i++;
-                       }
-                       SONIC_WRITE(SONIC_CDC, 16);
-                       /* issue Load CAM command */
-                       SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff);
-                       SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
-               }
-       }
-
-       if (sonic_debug > 2)
-               printk("sonic_multicast_list: setting RCR=%x\n", rcr);
-
-       SONIC_WRITE(SONIC_RCR, rcr);
-}
-
-
-/*
- * Initialize the SONIC ethernet controller.
- */
-static int sonic_init(struct net_device *dev)
-{
-       unsigned int cmd;
-       struct sonic_local *lp = netdev_priv(dev);
-       int i;
-
-       /*
-        * put the Sonic into software-reset mode and
-        * disable all interrupts
-        */
-       SONIC_WRITE(SONIC_IMR, 0);
-       SONIC_WRITE(SONIC_ISR, 0x7fff);
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
-
-       /*
-        * clear software reset flag, disable receiver, clear and
-        * enable interrupts, then completely initialize the SONIC
-        */
-       SONIC_WRITE(SONIC_CMD, 0);
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS);
-
-       /*
-        * initialize the receive resource area
-        */
-       if (sonic_debug > 2)
-               printk("sonic_init: initialize receive resource area\n");
-
-       for (i = 0; i < SONIC_NUM_RRS; i++) {
-               u16 bufadr_l = (unsigned long)lp->rx_laddr[i] & 0xffff;
-               u16 bufadr_h = (unsigned long)lp->rx_laddr[i] >> 16;
-               sonic_rra_put(dev, i, SONIC_RR_BUFADR_L, bufadr_l);
-               sonic_rra_put(dev, i, SONIC_RR_BUFADR_H, bufadr_h);
-               sonic_rra_put(dev, i, SONIC_RR_BUFSIZE_L, SONIC_RBSIZE >> 1);
-               sonic_rra_put(dev, i, SONIC_RR_BUFSIZE_H, 0);
-       }
-
-       /* initialize all RRA registers */
-       lp->rra_end = (lp->rra_laddr + SONIC_NUM_RRS * SIZEOF_SONIC_RR *
-                                       SONIC_BUS_SCALE(lp->dma_bitmode)) & 0xffff;
-       lp->cur_rwp = (lp->rra_laddr + (SONIC_NUM_RRS - 1) * SIZEOF_SONIC_RR *
-                                       SONIC_BUS_SCALE(lp->dma_bitmode)) & 0xffff;
-
-       SONIC_WRITE(SONIC_RSA, lp->rra_laddr & 0xffff);
-       SONIC_WRITE(SONIC_REA, lp->rra_end);
-       SONIC_WRITE(SONIC_RRP, lp->rra_laddr & 0xffff);
-       SONIC_WRITE(SONIC_RWP, lp->cur_rwp);
-       SONIC_WRITE(SONIC_URRA, lp->rra_laddr >> 16);
-       SONIC_WRITE(SONIC_EOBC, (SONIC_RBSIZE >> 1) - (lp->dma_bitmode ? 2 : 1));
-
-       /* load the resource pointers */
-       if (sonic_debug > 3)
-               printk("sonic_init: issuing RRRA command\n");
-
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RRRA);
-       i = 0;
-       while (i++ < 100) {
-               if (SONIC_READ(SONIC_CMD) & SONIC_CR_RRRA)
-                       break;
-       }
-
-       if (sonic_debug > 2)
-               printk("sonic_init: status=%x i=%d\n", SONIC_READ(SONIC_CMD), i);
-
-       /*
-        * Initialize the receive descriptors so that they
-        * become a circular linked list, ie. let the last
-        * descriptor point to the first again.
-        */
-       if (sonic_debug > 2)
-               printk("sonic_init: initialize receive descriptors\n");
-       for (i=0; i<SONIC_NUM_RDS; i++) {
-               sonic_rda_put(dev, i, SONIC_RD_STATUS, 0);
-               sonic_rda_put(dev, i, SONIC_RD_PKTLEN, 0);
-               sonic_rda_put(dev, i, SONIC_RD_PKTPTR_L, 0);
-               sonic_rda_put(dev, i, SONIC_RD_PKTPTR_H, 0);
-               sonic_rda_put(dev, i, SONIC_RD_SEQNO, 0);
-               sonic_rda_put(dev, i, SONIC_RD_IN_USE, 1);
-               sonic_rda_put(dev, i, SONIC_RD_LINK,
-                       lp->rda_laddr +
-                       ((i+1) * SIZEOF_SONIC_RD * SONIC_BUS_SCALE(lp->dma_bitmode)));
-       }
-       /* fix last descriptor */
-       sonic_rda_put(dev, SONIC_NUM_RDS - 1, SONIC_RD_LINK,
-               (lp->rda_laddr & 0xffff) | SONIC_EOL);
-       lp->eol_rx = SONIC_NUM_RDS - 1;
-       lp->cur_rx = 0;
-       SONIC_WRITE(SONIC_URDA, lp->rda_laddr >> 16);
-       SONIC_WRITE(SONIC_CRDA, lp->rda_laddr & 0xffff);
-
-       /*
-        * initialize transmit descriptors
-        */
-       if (sonic_debug > 2)
-               printk("sonic_init: initialize transmit descriptors\n");
-       for (i = 0; i < SONIC_NUM_TDS; i++) {
-               sonic_tda_put(dev, i, SONIC_TD_STATUS, 0);
-               sonic_tda_put(dev, i, SONIC_TD_CONFIG, 0);
-               sonic_tda_put(dev, i, SONIC_TD_PKTSIZE, 0);
-               sonic_tda_put(dev, i, SONIC_TD_FRAG_COUNT, 0);
-               sonic_tda_put(dev, i, SONIC_TD_LINK,
-                       (lp->tda_laddr & 0xffff) +
-                       (i + 1) * SIZEOF_SONIC_TD * SONIC_BUS_SCALE(lp->dma_bitmode));
-               lp->tx_skb[i] = NULL;
-       }
-       /* fix last descriptor */
-       sonic_tda_put(dev, SONIC_NUM_TDS - 1, SONIC_TD_LINK,
-               (lp->tda_laddr & 0xffff));
-
-       SONIC_WRITE(SONIC_UTDA, lp->tda_laddr >> 16);
-       SONIC_WRITE(SONIC_CTDA, lp->tda_laddr & 0xffff);
-       lp->cur_tx = lp->next_tx = 0;
-       lp->eol_tx = SONIC_NUM_TDS - 1;
-
-       /*
-        * put our own address to CAM desc[0]
-        */
-       sonic_cda_put(dev, 0, SONIC_CD_CAP0, dev->dev_addr[1] << 8 | dev->dev_addr[0]);
-       sonic_cda_put(dev, 0, SONIC_CD_CAP1, dev->dev_addr[3] << 8 | dev->dev_addr[2]);
-       sonic_cda_put(dev, 0, SONIC_CD_CAP2, dev->dev_addr[5] << 8 | dev->dev_addr[4]);
-       sonic_set_cam_enable(dev, 1);
-
-       for (i = 0; i < 16; i++)
-               sonic_cda_put(dev, i, SONIC_CD_ENTRY_POINTER, i);
-
-       /*
-        * initialize CAM registers
-        */
-       SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff);
-       SONIC_WRITE(SONIC_CDC, 16);
-
-       /*
-        * load the CAM
-        */
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
-
-       i = 0;
-       while (i++ < 100) {
-               if (SONIC_READ(SONIC_ISR) & SONIC_INT_LCD)
-                       break;
-       }
-       if (sonic_debug > 2) {
-               printk("sonic_init: CMD=%x, ISR=%x\n, i=%d",
-                      SONIC_READ(SONIC_CMD), SONIC_READ(SONIC_ISR), i);
-       }
-
-       /*
-        * enable receiver, disable loopback
-        * and enable all interrupts
-        */
-       SONIC_WRITE(SONIC_CMD, SONIC_CR_RXEN | SONIC_CR_STP);
-       SONIC_WRITE(SONIC_RCR, SONIC_RCR_DEFAULT);
-       SONIC_WRITE(SONIC_TCR, SONIC_TCR_DEFAULT);
-       SONIC_WRITE(SONIC_ISR, 0x7fff);
-       SONIC_WRITE(SONIC_IMR, SONIC_IMR_DEFAULT);
-
-       cmd = SONIC_READ(SONIC_CMD);
-       if ((cmd & SONIC_CR_RXEN) == 0 || (cmd & SONIC_CR_STP) == 0)
-               printk(KERN_ERR "sonic_init: failed, status=%x\n", cmd);
-
-       if (sonic_debug > 2)
-               printk("sonic_init: new status=%x\n",
-                      SONIC_READ(SONIC_CMD));
-
-       return 0;
-}
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/sonic.h b/drivers/net/sonic.h
deleted file mode 100644 (file)
index 07091dd..0000000
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * Header file for sonic.c
- *
- * (C) Waldorf Electronics, Germany
- * Written by Andreas Busse
- *
- * NOTE: most of the structure definitions here are endian dependent.
- * If you want to use this driver on big endian machines, the data
- * and pad structure members must be exchanged. Also, the structures
- * need to be changed accordingly to the bus size.
- *
- * 981229 MSch:        did just that for the 68k Mac port (32 bit, big endian)
- *
- * 990611 David Huggins-Daines <dhd@debian.org>: This machine abstraction
- * does not cope with 16-bit bus sizes very well.  Therefore I have
- * rewritten it with ugly macros and evil inlines.
- *
- * 050625 Finn Thain: introduced more 32-bit cards and dhd's support
- *        for 16-bit cards (from the mac68k project).
- */
-
-#ifndef SONIC_H
-#define SONIC_H
-
-
-/*
- * SONIC register offsets
- */
-
-#define SONIC_CMD              0x00
-#define SONIC_DCR              0x01
-#define SONIC_RCR              0x02
-#define SONIC_TCR              0x03
-#define SONIC_IMR              0x04
-#define SONIC_ISR              0x05
-
-#define SONIC_UTDA             0x06
-#define SONIC_CTDA             0x07
-
-#define SONIC_URDA             0x0d
-#define SONIC_CRDA             0x0e
-#define SONIC_EOBC             0x13
-#define SONIC_URRA             0x14
-#define SONIC_RSA              0x15
-#define SONIC_REA              0x16
-#define SONIC_RRP              0x17
-#define SONIC_RWP              0x18
-#define SONIC_RSC              0x2b
-
-#define SONIC_CEP              0x21
-#define SONIC_CAP2             0x22
-#define SONIC_CAP1             0x23
-#define SONIC_CAP0             0x24
-#define SONIC_CE               0x25
-#define SONIC_CDP              0x26
-#define SONIC_CDC              0x27
-
-#define SONIC_WT0              0x29
-#define SONIC_WT1              0x2a
-
-#define SONIC_SR               0x28
-
-
-/* test-only registers */
-
-#define SONIC_TPS              0x08
-#define SONIC_TFC              0x09
-#define SONIC_TSA0             0x0a
-#define SONIC_TSA1             0x0b
-#define SONIC_TFS              0x0c
-
-#define SONIC_CRBA0            0x0f
-#define SONIC_CRBA1            0x10
-#define SONIC_RBWC0            0x11
-#define SONIC_RBWC1            0x12
-#define SONIC_TTDA             0x20
-#define SONIC_MDT              0x2f
-
-#define SONIC_TRBA0            0x19
-#define SONIC_TRBA1            0x1a
-#define SONIC_TBWC0            0x1b
-#define SONIC_TBWC1            0x1c
-#define SONIC_LLFA             0x1f
-
-#define SONIC_ADDR0            0x1d
-#define SONIC_ADDR1            0x1e
-
-/*
- * Error counters
- */
-
-#define SONIC_CRCT              0x2c
-#define SONIC_FAET              0x2d
-#define SONIC_MPT               0x2e
-
-#define SONIC_DCR2              0x3f
-
-/*
- * SONIC command bits
- */
-
-#define SONIC_CR_LCAM           0x0200
-#define SONIC_CR_RRRA           0x0100
-#define SONIC_CR_RST            0x0080
-#define SONIC_CR_ST             0x0020
-#define SONIC_CR_STP            0x0010
-#define SONIC_CR_RXEN           0x0008
-#define SONIC_CR_RXDIS          0x0004
-#define SONIC_CR_TXP            0x0002
-#define SONIC_CR_HTX            0x0001
-
-/*
- * SONIC data configuration bits
- */
-
-#define SONIC_DCR_EXBUS         0x8000
-#define SONIC_DCR_LBR           0x2000
-#define SONIC_DCR_PO1           0x1000
-#define SONIC_DCR_PO0           0x0800
-#define SONIC_DCR_SBUS          0x0400
-#define SONIC_DCR_USR1          0x0200
-#define SONIC_DCR_USR0          0x0100
-#define SONIC_DCR_WC1           0x0080
-#define SONIC_DCR_WC0           0x0040
-#define SONIC_DCR_DW            0x0020
-#define SONIC_DCR_BMS           0x0010
-#define SONIC_DCR_RFT1          0x0008
-#define SONIC_DCR_RFT0          0x0004
-#define SONIC_DCR_TFT1          0x0002
-#define SONIC_DCR_TFT0          0x0001
-
-/*
- * Constants for the SONIC receive control register.
- */
-
-#define SONIC_RCR_ERR           0x8000
-#define SONIC_RCR_RNT           0x4000
-#define SONIC_RCR_BRD           0x2000
-#define SONIC_RCR_PRO           0x1000
-#define SONIC_RCR_AMC           0x0800
-#define SONIC_RCR_LB1           0x0400
-#define SONIC_RCR_LB0           0x0200
-
-#define SONIC_RCR_MC            0x0100
-#define SONIC_RCR_BC            0x0080
-#define SONIC_RCR_LPKT          0x0040
-#define SONIC_RCR_CRS           0x0020
-#define SONIC_RCR_COL           0x0010
-#define SONIC_RCR_CRCR          0x0008
-#define SONIC_RCR_FAER          0x0004
-#define SONIC_RCR_LBK           0x0002
-#define SONIC_RCR_PRX           0x0001
-
-#define SONIC_RCR_LB_OFF        0
-#define SONIC_RCR_LB_MAC        SONIC_RCR_LB0
-#define SONIC_RCR_LB_ENDEC      SONIC_RCR_LB1
-#define SONIC_RCR_LB_TRANS      (SONIC_RCR_LB0 | SONIC_RCR_LB1)
-
-/* default RCR setup */
-
-#define SONIC_RCR_DEFAULT       (SONIC_RCR_BRD)
-
-
-/*
- * SONIC Transmit Control register bits
- */
-
-#define SONIC_TCR_PINTR         0x8000
-#define SONIC_TCR_POWC          0x4000
-#define SONIC_TCR_CRCI          0x2000
-#define SONIC_TCR_EXDIS         0x1000
-#define SONIC_TCR_EXD           0x0400
-#define SONIC_TCR_DEF           0x0200
-#define SONIC_TCR_NCRS          0x0100
-#define SONIC_TCR_CRLS          0x0080
-#define SONIC_TCR_EXC           0x0040
-#define SONIC_TCR_PMB           0x0008
-#define SONIC_TCR_FU            0x0004
-#define SONIC_TCR_BCM           0x0002
-#define SONIC_TCR_PTX           0x0001
-
-#define SONIC_TCR_DEFAULT       0x0000
-
-/*
- * Constants for the SONIC_INTERRUPT_MASK and
- * SONIC_INTERRUPT_STATUS registers.
- */
-
-#define SONIC_INT_BR           0x4000
-#define SONIC_INT_HBL          0x2000
-#define SONIC_INT_LCD          0x1000
-#define SONIC_INT_PINT         0x0800
-#define SONIC_INT_PKTRX                0x0400
-#define SONIC_INT_TXDN         0x0200
-#define SONIC_INT_TXER         0x0100
-#define SONIC_INT_TC           0x0080
-#define SONIC_INT_RDE          0x0040
-#define SONIC_INT_RBE          0x0020
-#define SONIC_INT_RBAE         0x0010
-#define SONIC_INT_CRC          0x0008
-#define SONIC_INT_FAE          0x0004
-#define SONIC_INT_MP           0x0002
-#define SONIC_INT_RFO          0x0001
-
-
-/*
- * The interrupts we allow.
- */
-
-#define SONIC_IMR_DEFAULT     ( SONIC_INT_BR | \
-                                SONIC_INT_LCD | \
-                                SONIC_INT_RFO | \
-                                SONIC_INT_PKTRX | \
-                                SONIC_INT_TXDN | \
-                                SONIC_INT_TXER | \
-                                SONIC_INT_RDE | \
-                                SONIC_INT_RBAE | \
-                                SONIC_INT_CRC | \
-                                SONIC_INT_FAE | \
-                                SONIC_INT_MP)
-
-
-#define SONIC_EOL       0x0001
-#define CAM_DESCRIPTORS 16
-
-/* Offsets in the various DMA buffers accessed by the SONIC */
-
-#define SONIC_BITMODE16 0
-#define SONIC_BITMODE32 1
-#define SONIC_BUS_SCALE(bitmode) ((bitmode) ? 4 : 2)
-/* Note!  These are all measured in bus-size units, so use SONIC_BUS_SCALE */
-#define SIZEOF_SONIC_RR 4
-#define SONIC_RR_BUFADR_L  0
-#define SONIC_RR_BUFADR_H  1
-#define SONIC_RR_BUFSIZE_L 2
-#define SONIC_RR_BUFSIZE_H 3
-
-#define SIZEOF_SONIC_RD 7
-#define SONIC_RD_STATUS   0
-#define SONIC_RD_PKTLEN   1
-#define SONIC_RD_PKTPTR_L 2
-#define SONIC_RD_PKTPTR_H 3
-#define SONIC_RD_SEQNO    4
-#define SONIC_RD_LINK     5
-#define SONIC_RD_IN_USE   6
-
-#define SIZEOF_SONIC_TD 8
-#define SONIC_TD_STATUS       0
-#define SONIC_TD_CONFIG       1
-#define SONIC_TD_PKTSIZE      2
-#define SONIC_TD_FRAG_COUNT   3
-#define SONIC_TD_FRAG_PTR_L   4
-#define SONIC_TD_FRAG_PTR_H   5
-#define SONIC_TD_FRAG_SIZE    6
-#define SONIC_TD_LINK         7
-
-#define SIZEOF_SONIC_CD 4
-#define SONIC_CD_ENTRY_POINTER 0
-#define SONIC_CD_CAP0          1
-#define SONIC_CD_CAP1          2
-#define SONIC_CD_CAP2          3
-
-#define SIZEOF_SONIC_CDA ((CAM_DESCRIPTORS * SIZEOF_SONIC_CD) + 1)
-#define SONIC_CDA_CAM_ENABLE   (CAM_DESCRIPTORS * SIZEOF_SONIC_CD)
-
-/*
- * Some tunables for the buffer areas. Power of 2 is required
- * the current driver uses one receive buffer for each descriptor.
- *
- * MSch: use more buffer space for the slow m68k Macs!
- */
-#define SONIC_NUM_RRS   16            /* number of receive resources */
-#define SONIC_NUM_RDS   SONIC_NUM_RRS /* number of receive descriptors */
-#define SONIC_NUM_TDS   16            /* number of transmit descriptors */
-
-#define SONIC_RDS_MASK  (SONIC_NUM_RDS-1)
-#define SONIC_TDS_MASK  (SONIC_NUM_TDS-1)
-
-#define SONIC_RBSIZE   1520          /* size of one resource buffer */
-
-/* Again, measured in bus size units! */
-#define SIZEOF_SONIC_DESC (SIZEOF_SONIC_CDA    \
-       + (SIZEOF_SONIC_TD * SONIC_NUM_TDS)     \
-       + (SIZEOF_SONIC_RD * SONIC_NUM_RDS)     \
-       + (SIZEOF_SONIC_RR * SONIC_NUM_RRS))
-
-/* Information that need to be kept for each board. */
-struct sonic_local {
-       /* Bus size.  0 == 16 bits, 1 == 32 bits. */
-       int dma_bitmode;
-       /* Register offset within the longword (independent of endianness,
-          and varies from one type of Macintosh SONIC to another
-          (Aarrgh)) */
-       int reg_offset;
-       void *descriptors;
-       /* Crud.  These areas have to be within the same 64K.  Therefore
-       we allocate a desriptors page, and point these to places within it. */
-       void *cda;  /* CAM descriptor area */
-       void *tda;  /* Transmit descriptor area */
-       void *rra;  /* Receive resource area */
-       void *rda;  /* Receive descriptor area */
-       struct sk_buff* volatile rx_skb[SONIC_NUM_RRS]; /* packets to be received */
-       struct sk_buff* volatile tx_skb[SONIC_NUM_TDS]; /* packets to be transmitted */
-       unsigned int tx_len[SONIC_NUM_TDS]; /* lengths of tx DMA mappings */
-       /* Logical DMA addresses on MIPS, bus addresses on m68k
-        * (so "laddr" is a bit misleading) */
-       dma_addr_t descriptors_laddr;
-       u32 cda_laddr;              /* logical DMA address of CDA */
-       u32 tda_laddr;              /* logical DMA address of TDA */
-       u32 rra_laddr;              /* logical DMA address of RRA */
-       u32 rda_laddr;              /* logical DMA address of RDA */
-       dma_addr_t rx_laddr[SONIC_NUM_RRS]; /* logical DMA addresses of rx skbuffs */
-       dma_addr_t tx_laddr[SONIC_NUM_TDS]; /* logical DMA addresses of tx skbuffs */
-       unsigned int rra_end;
-       unsigned int cur_rwp;
-       unsigned int cur_rx;
-       unsigned int cur_tx;           /* first unacked transmit packet */
-       unsigned int eol_rx;
-       unsigned int eol_tx;           /* last unacked transmit packet */
-       unsigned int next_tx;          /* next free TD */
-       struct device *device;         /* generic device */
-       struct net_device_stats stats;
-};
-
-#define TX_TIMEOUT (3 * HZ)
-
-/* Index to functions, as function prototypes. */
-
-static int sonic_open(struct net_device *dev);
-static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev);
-static irqreturn_t sonic_interrupt(int irq, void *dev_id);
-static void sonic_rx(struct net_device *dev);
-static int sonic_close(struct net_device *dev);
-static struct net_device_stats *sonic_get_stats(struct net_device *dev);
-static void sonic_multicast_list(struct net_device *dev);
-static int sonic_init(struct net_device *dev);
-static void sonic_tx_timeout(struct net_device *dev);
-
-/* Internal inlines for reading/writing DMA buffers.  Note that bus
-   size and endianness matter here, whereas they don't for registers,
-   as far as we can tell. */
-/* OpenBSD calls this "SWO".  I'd like to think that sonic_buf_put()
-   is a much better name. */
-static inline void sonic_buf_put(void* base, int bitmode,
-                                int offset, __u16 val)
-{
-       if (bitmode)
-#ifdef __BIG_ENDIAN
-               ((__u16 *) base + (offset*2))[1] = val;
-#else
-               ((__u16 *) base + (offset*2))[0] = val;
-#endif
-       else
-               ((__u16 *) base)[offset] = val;
-}
-
-static inline __u16 sonic_buf_get(void* base, int bitmode,
-                                 int offset)
-{
-       if (bitmode)
-#ifdef __BIG_ENDIAN
-               return ((volatile __u16 *) base + (offset*2))[1];
-#else
-               return ((volatile __u16 *) base + (offset*2))[0];
-#endif
-       else
-               return ((volatile __u16 *) base)[offset];
-}
-
-/* Inlines that you should actually use for reading/writing DMA buffers */
-static inline void sonic_cda_put(struct net_device* dev, int entry,
-                                int offset, __u16 val)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       sonic_buf_put(lp->cda, lp->dma_bitmode,
-                     (entry * SIZEOF_SONIC_CD) + offset, val);
-}
-
-static inline __u16 sonic_cda_get(struct net_device* dev, int entry,
-                                 int offset)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       return sonic_buf_get(lp->cda, lp->dma_bitmode,
-                            (entry * SIZEOF_SONIC_CD) + offset);
-}
-
-static inline void sonic_set_cam_enable(struct net_device* dev, __u16 val)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       sonic_buf_put(lp->cda, lp->dma_bitmode, SONIC_CDA_CAM_ENABLE, val);
-}
-
-static inline __u16 sonic_get_cam_enable(struct net_device* dev)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       return sonic_buf_get(lp->cda, lp->dma_bitmode, SONIC_CDA_CAM_ENABLE);
-}
-
-static inline void sonic_tda_put(struct net_device* dev, int entry,
-                                int offset, __u16 val)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       sonic_buf_put(lp->tda, lp->dma_bitmode,
-                     (entry * SIZEOF_SONIC_TD) + offset, val);
-}
-
-static inline __u16 sonic_tda_get(struct net_device* dev, int entry,
-                                 int offset)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       return sonic_buf_get(lp->tda, lp->dma_bitmode,
-                            (entry * SIZEOF_SONIC_TD) + offset);
-}
-
-static inline void sonic_rda_put(struct net_device* dev, int entry,
-                                int offset, __u16 val)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       sonic_buf_put(lp->rda, lp->dma_bitmode,
-                     (entry * SIZEOF_SONIC_RD) + offset, val);
-}
-
-static inline __u16 sonic_rda_get(struct net_device* dev, int entry,
-                                 int offset)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       return sonic_buf_get(lp->rda, lp->dma_bitmode,
-                            (entry * SIZEOF_SONIC_RD) + offset);
-}
-
-static inline void sonic_rra_put(struct net_device* dev, int entry,
-                                int offset, __u16 val)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       sonic_buf_put(lp->rra, lp->dma_bitmode,
-                     (entry * SIZEOF_SONIC_RR) + offset, val);
-}
-
-static inline __u16 sonic_rra_get(struct net_device* dev, int entry,
-                                 int offset)
-{
-       struct sonic_local *lp = netdev_priv(dev);
-       return sonic_buf_get(lp->rra, lp->dma_bitmode,
-                            (entry * SIZEOF_SONIC_RR) + offset);
-}
-
-static const char *version =
-    "sonic.c:v0.92 20.9.98 tsbogend@alpha.franken.de\n";
-
-#endif /* SONIC_H */
diff --git a/drivers/net/xtsonic.c b/drivers/net/xtsonic.c
deleted file mode 100644 (file)
index 9f12026..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * xtsonic.c
- *
- * (C) 2001 - 2007 Tensilica Inc.
- *     Kevin Chea <kchea@yahoo.com>
- *     Marc Gauthier <marc@linux-xtensa.org>
- *     Chris Zankel <chris@zankel.net>
- *
- * (C) 1996,1998 by Thomas Bogendoerfer (tsbogend@alpha.franken.de)
- *
- * This driver is based on work from Andreas Busse, but most of
- * the code is rewritten.
- *
- * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
- *
- * A driver for the onboard Sonic ethernet controller on the XT2000.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/gfp.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/dma.h>
-
-static char xtsonic_string[] = "xtsonic";
-
-extern unsigned xtboard_nvram_valid(void);
-extern void xtboard_get_ether_addr(unsigned char *buf);
-
-#include "sonic.h"
-
-/*
- * According to the documentation for the Sonic ethernet controller,
- * EOBC should be 760 words (1520 bytes) for 32-bit applications, and,
- * as such, 2 words less than the buffer size. The value for RBSIZE
- * defined in sonic.h, however is only 1520.
- *
- * (Note that in 16-bit configurations, EOBC is 759 words (1518 bytes) and
- * RBSIZE 1520 bytes)
- */
-#undef SONIC_RBSIZE
-#define SONIC_RBSIZE   1524
-
-/*
- * The chip provides 256 byte register space.
- */
-#define SONIC_MEM_SIZE 0x100
-
-/*
- * Macros to access SONIC registers
- */
-#define SONIC_READ(reg) \
-       (0xffff & *((volatile unsigned int *)dev->base_addr+reg))
-
-#define SONIC_WRITE(reg,val) \
-       *((volatile unsigned int *)dev->base_addr+reg) = val
-
-
-/* Use 0 for production, 1 for verification, and >2 for debug */
-#ifdef SONIC_DEBUG
-static unsigned int sonic_debug = SONIC_DEBUG;
-#else
-static unsigned int sonic_debug = 1;
-#endif
-
-/*
- * We cannot use station (ethernet) address prefixes to detect the
- * sonic controller since these are board manufacturer depended.
- * So we check for known Silicon Revision IDs instead.
- */
-static unsigned short known_revisions[] =
-{
-       0x101,                  /* SONIC 83934 */
-       0xffff                  /* end of list */
-};
-
-static int xtsonic_open(struct net_device *dev)
-{
-       int retval;
-
-       retval = request_irq(dev->irq, sonic_interrupt, IRQF_DISABLED,
-                               "sonic", dev);
-       if (retval) {
-               printk(KERN_ERR "%s: unable to get IRQ %d.\n",
-                      dev->name, dev->irq);
-               return -EAGAIN;
-       }
-
-       retval = sonic_open(dev);
-       if (retval)
-               free_irq(dev->irq, dev);
-       return retval;
-}
-
-static int xtsonic_close(struct net_device *dev)
-{
-       int err;
-       err = sonic_close(dev);
-       free_irq(dev->irq, dev);
-       return err;
-}
-
-static const struct net_device_ops xtsonic_netdev_ops = {
-       .ndo_open               = xtsonic_open,
-       .ndo_stop               = xtsonic_close,
-       .ndo_start_xmit         = sonic_send_packet,
-       .ndo_get_stats          = sonic_get_stats,
-       .ndo_set_multicast_list = sonic_multicast_list,
-       .ndo_tx_timeout         = sonic_tx_timeout,
-       .ndo_validate_addr      = eth_validate_addr,
-       .ndo_change_mtu         = eth_change_mtu,
-       .ndo_set_mac_address    = eth_mac_addr,
-};
-
-static int __init sonic_probe1(struct net_device *dev)
-{
-       static unsigned version_printed = 0;
-       unsigned int silicon_revision;
-       struct sonic_local *lp = netdev_priv(dev);
-       unsigned int base_addr = dev->base_addr;
-       int i;
-       int err = 0;
-
-       if (!request_mem_region(base_addr, 0x100, xtsonic_string))
-               return -EBUSY;
-
-       /*
-        * get the Silicon Revision ID. If this is one of the known
-        * one assume that we found a SONIC ethernet controller at
-        * the expected location.
-        */
-       silicon_revision = SONIC_READ(SONIC_SR);
-       if (sonic_debug > 1)
-               printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);
-
-       i = 0;
-       while ((known_revisions[i] != 0xffff) &&
-                       (known_revisions[i] != silicon_revision))
-               i++;
-
-       if (known_revisions[i] == 0xffff) {
-               printk("SONIC ethernet controller not found (0x%4x)\n",
-                               silicon_revision);
-               return -ENODEV;
-       }
-
-       if (sonic_debug  &&  version_printed++ == 0)
-               printk(version);
-
-       /*
-        * Put the sonic into software reset, then retrieve ethernet address.
-        * Note: we are assuming that the boot-loader has initialized the cam.
-        */
-       SONIC_WRITE(SONIC_CMD,SONIC_CR_RST);
-       SONIC_WRITE(SONIC_DCR,
-                   SONIC_DCR_WC0|SONIC_DCR_DW|SONIC_DCR_LBR|SONIC_DCR_SBUS);
-       SONIC_WRITE(SONIC_CEP,0);
-       SONIC_WRITE(SONIC_IMR,0);
-
-       SONIC_WRITE(SONIC_CMD,SONIC_CR_RST);
-       SONIC_WRITE(SONIC_CEP,0);
-
-       for (i=0; i<3; i++) {
-               unsigned int val = SONIC_READ(SONIC_CAP0-i);
-               dev->dev_addr[i*2] = val;
-               dev->dev_addr[i*2+1] = val >> 8;
-       }
-
-       /* Initialize the device structure. */
-
-       lp->dma_bitmode = SONIC_BITMODE32;
-
-       /*
-        *  Allocate local private descriptor areas in uncached space.
-        *  The entire structure must be located within the same 64kb segment.
-        *  A simple way to ensure this is to allocate twice the
-        *  size of the structure -- given that the structure is
-        *  much less than 64 kB, at least one of the halves of
-        *  the allocated area will be contained entirely in 64 kB.
-        *  We also allocate extra space for a pointer to allow freeing
-        *  this structure later on (in xtsonic_cleanup_module()).
-        */
-       lp->descriptors =
-               dma_alloc_coherent(lp->device,
-                       SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                       &lp->descriptors_laddr, GFP_KERNEL);
-
-       if (lp->descriptors == NULL) {
-               printk(KERN_ERR "%s: couldn't alloc DMA memory for "
-                               " descriptors.\n", dev_name(lp->device));
-               goto out;
-       }
-
-       lp->cda = lp->descriptors;
-       lp->tda = lp->cda + (SIZEOF_SONIC_CDA
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
-                            * SONIC_BUS_SCALE(lp->dma_bitmode));
-
-       /* get the virtual dma address */
-
-       lp->cda_laddr = lp->descriptors_laddr;
-       lp->tda_laddr = lp->cda_laddr + (SIZEOF_SONIC_CDA
-                                        * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rda_laddr = lp->tda_laddr + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
-                                        * SONIC_BUS_SCALE(lp->dma_bitmode));
-       lp->rra_laddr = lp->rda_laddr + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
-                                        * SONIC_BUS_SCALE(lp->dma_bitmode));
-
-       dev->netdev_ops         = &xtsonic_netdev_ops;
-       dev->watchdog_timeo     = TX_TIMEOUT;
-
-       /*
-        * clear tally counter
-        */
-       SONIC_WRITE(SONIC_CRCT,0xffff);
-       SONIC_WRITE(SONIC_FAET,0xffff);
-       SONIC_WRITE(SONIC_MPT,0xffff);
-
-       return 0;
-out:
-       release_region(dev->base_addr, SONIC_MEM_SIZE);
-       return err;
-}
-
-
-/*
- * Probe for a SONIC ethernet controller on an XT2000 board.
- * Actually probing is superfluous but we're paranoid.
- */
-
-int __devinit xtsonic_probe(struct platform_device *pdev)
-{
-       struct net_device *dev;
-       struct sonic_local *lp;
-       struct resource *resmem, *resirq;
-       int err = 0;
-
-       if ((resmem = platform_get_resource(pdev, IORESOURCE_MEM, 0)) == NULL)
-               return -ENODEV;
-
-       if ((resirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0)) == NULL)
-               return -ENODEV;
-
-       if ((dev = alloc_etherdev(sizeof(struct sonic_local))) == NULL)
-               return -ENOMEM;
-
-       lp = netdev_priv(dev);
-       lp->device = &pdev->dev;
-       SET_NETDEV_DEV(dev, &pdev->dev);
-       netdev_boot_setup_check(dev);
-
-       dev->base_addr = resmem->start;
-       dev->irq = resirq->start;
-
-       if ((err = sonic_probe1(dev)))
-               goto out;
-       if ((err = register_netdev(dev)))
-               goto out1;
-
-       printk("%s: SONIC ethernet @%08lx, MAC %pM, IRQ %d\n", dev->name,
-              dev->base_addr, dev->dev_addr, dev->irq);
-
-       return 0;
-
-out1:
-       release_region(dev->base_addr, SONIC_MEM_SIZE);
-out:
-       free_netdev(dev);
-
-       return err;
-}
-
-MODULE_DESCRIPTION("Xtensa XT2000 SONIC ethernet driver");
-module_param(sonic_debug, int, 0);
-MODULE_PARM_DESC(sonic_debug, "xtsonic debug level (1-4)");
-
-#include "sonic.c"
-
-static int __devexit xtsonic_device_remove (struct platform_device *pdev)
-{
-       struct net_device *dev = platform_get_drvdata(pdev);
-       struct sonic_local *lp = netdev_priv(dev);
-
-       unregister_netdev(dev);
-       dma_free_coherent(lp->device,
-                         SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
-                         lp->descriptors, lp->descriptors_laddr);
-       release_region (dev->base_addr, SONIC_MEM_SIZE);
-       free_netdev(dev);
-
-       return 0;
-}
-
-static struct platform_driver xtsonic_driver = {
-       .probe = xtsonic_probe,
-       .remove = __devexit_p(xtsonic_device_remove),
-       .driver = {
-               .name = xtsonic_string,
-       },
-};
-
-static int __init xtsonic_init(void)
-{
-       return platform_driver_register(&xtsonic_driver);
-}
-
-static void __exit xtsonic_cleanup(void)
-{
-       platform_driver_unregister(&xtsonic_driver);
-}
-
-module_init(xtsonic_init);
-module_exit(xtsonic_cleanup);