]> git.neil.brown.name Git - history.git/commitdiff
Import 2.0.34pre7 2.0.34pre7
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:41 +0000 (15:11 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:41 +0000 (15:11 -0500)
23 files changed:
CREDITS
Documentation/Configure.help
Documentation/networking/tlan.FAQ [new file with mode: 0644]
Documentation/networking/tlan.README [new file with mode: 0644]
MAINTAINERS
drivers/net/3c515.c
drivers/net/Changelog.tlan [new file with mode: 0644]
drivers/net/Config.in
drivers/net/Makefile
drivers/net/Space.c
drivers/net/at1700.c
drivers/net/tlan.c [new file with mode: 0644]
drivers/net/tlan.h [new file with mode: 0644]
drivers/scsi/ChangeLog.ncr53c8xx
drivers/scsi/README.ncr53c8xx
drivers/scsi/eata.c
drivers/scsi/eata.h
drivers/scsi/ncr53c8xx.c
drivers/scsi/ncr53c8xx.h
drivers/scsi/u14-34f.c
drivers/scsi/u14-34f.h
mm/vmalloc.c
net/core/dev.c

diff --git a/CREDITS b/CREDITS
index 46311514036449353d91fcb19e2febfe2ba62c3f..810669b95a7b81f8a3785bf829f0a37b300ffcd8 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -35,6 +35,15 @@ S: 4390 Albany Dr. #46
 S: San Jose, California 95129
 S: USA
 
+N: Andrea Arcangeli
+E: arcangeli@mbox.queen.it
+W: http://www.cs.unibo.it/~arcangel/
+P: 1024/CB4660B9 CC A0 71 81 F4 A0 63 AC  C0 4B 81 1D 8C 15 C8 E5
+D: Fixed a 2.0.33 mm bug that corrupts memory in linux/mm/vmalloc.c
+S: Via Ciaclini 26
+S: Imola 40026
+S: Italy
+
 N: Derek Atkins
 E: warlord@MIT.EDU
 D: Linux-AFS Port, random kernel hacker,
@@ -81,6 +90,14 @@ S: University of Notre Dame
 S: Notre Dame, Indiana
 S: USA
 
+N: James Banks
+E: james.banks@caldera.com
+D: TLAN network driver
+S: Caldera, Inc.
+S: 633 South 550 East
+S: Provo, UT 84606
+S: USA
+
 N: Peter Bauer
 E: 100136.3530@compuserve.com
 D: Driver for depca-ethernet-board
index a005d50647186f4beff39a5d0a4e4296220af7f8..0dce3e96675280aaf6b509bbb3625b9f0f2ae4b6 100644 (file)
@@ -2751,6 +2751,14 @@ CONFIG_ETH16I
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
+TI ThunderLAN support (EXPERIMENTAL)
+CONFIG_TLAN
+  If you have a TLAN based network card which is supported by this
+  driver, say Y and read the Ethernet-HOWTO.  Devices currently supported
+  are the Compaq Netelligent 10, Netelligent 10/100, and Internal
+  NetFlex 3.  This driver is also available as a module.  Please email
+  feedback to james.banks@caldera.com.
+
 Zenith Z-Note support
 CONFIG_ZNET
   The Zenith Z-Note notebook computer has a built-in network
diff --git a/Documentation/networking/tlan.FAQ b/Documentation/networking/tlan.FAQ
new file mode 100644 (file)
index 0000000..a529e64
--- /dev/null
@@ -0,0 +1,28 @@
+1.     Q. Ifconfig reports "10Mbs Ethernet" for my 10/100Mbs card.  How do
+          I get my card to run in 100Mbs mode?
+
+       A. The "10Mbs Ethernet" is poorly named.  What this is really saying
+          is that this device is some kind of Ethernet device (ie, uses
+          ethernet style frames at the link layer).  Recent versions of
+          ifconfig report this as simply "Ethernet".
+
+          The TLAN driver is designed to autodetect 10Mbs vs. 100Mbs
+          connections, and choose the higher speed.  The most sure way
+          to determine what speed you are running at is to look at the
+          100Mbs LED on the card, if your device has one.
+
+
+2.     Q. My network card is using the same interrupt as my SCSI card.
+
+       A. Some Compaqs set all PCI devices to the same interrupt by default.
+          You can either change the interrupt used by one of the device
+          with your Compaq configuration utility, or you can have the TLAN
+          driver use the same type of interrupt handler:
+
+          a. For module based use, add 'sa_int=1' to the command line, eg.:
+               insmod tlan.o sa_int=1
+
+          b. For compiled in kernel, set the 0x2 bit in the third part
+             for the boot parameter for that device, eg.:
+               ether=0,0,0x2,0,eth0
+
diff --git a/Documentation/networking/tlan.README b/Documentation/networking/tlan.README
new file mode 100644 (file)
index 0000000..a4818a3
--- /dev/null
@@ -0,0 +1,130 @@
+Caldera TLAN driver for Linux, version 0.42
+README
+
+
+I.  Supported Devices.
+
+    Only PCI devices will work with this driver.
+
+    Supported:
+    Vendor ID  Device ID       Name
+    0e11       ae32            Compaq Netelligent 10/100 TX
+    0e11       ae34            Compaq Netelligent 10 T
+    0e11       ae35            Compaq Integrated NetFlex 3/P
+    0e11       ae43            Compaq ProLiant Integrated Netelligent 10/100 TX
+    0e11       ae40            Compaq Dual Port Netelligent 10/100 TX
+    0e11       b011            Compaq Deskpro 4000 5233MMX
+    0e11       f130            Compaq NetFlex 3/P
+    0e11       f150            Compaq NetFlex 3/P
+    108d       0014            Olicom OC-2326  
+
+    Caveats:
+    
+    I don't believe 100BaseTX daughterboards will work.  I am interested
+    in any reports.
+    
+
+II.  Building the Driver.
+
+    The TLAN driver may be compiled into the kernel, or it may be compiled
+    as a module separately, or in the kernel.  A patch is included for
+    2.0.29 (which also works for 2.0.30, 2.0.31, and 2.0.32).
+
+    To compile it as part of the kernel:
+        1. Download and untar the TLAN driver package.
+       2. If your kernel is 2.1.45 or later, you do not need to patch the
+           kernel sources.  Copy the tlan.c and tlan.h to drivers/net in
+           the kernel source tree.
+        3. Otherwise, apply the appropriate patch for your kernel.  For
+          example:
+
+              cd /usr/src/linux
+               patch -p1 < kernel.2.0.29
+
+        4. Copy the files tlan.c and tlan.h from the TLAN package to the
+           directory drivers/net in the Linux kernel source tree.
+        5. Configure your kernel for the TLAN driver.  Answer 'Y' when
+           prompted to ask about experimental code (the first question).
+           Then answer 'Y' when prompted if to include TI ThunderLAN
+           support.  If you want the driver compiled as a module, answer 'M'
+           instead of 'Y'.
+        6. Make the kernel and, if necessary, the modules.
+    
+    To compile the TLAN driver independently:
+        1. Download and untar the TLAN driver package.
+        2. Change to the tlan directory.
+        3. If you are NOT using a versioned kernel (ie, want an non-
+           versioned module), edit the Makefile, and comment out the
+           line:
+                 MODVERSIONS = -DMODVERSIONS
+        4. Run 'make'.
+
+
+III.  Driver Options
+       1. You can append debug=x to the end of the insmod line to get
+           debug messages, where x is a bit field where the bits mean
+          the following:
+          
+          0x01         Turn on general debugging messages.
+          0x02         Turn on receive debugging messages.
+          0x04         Turn on transmit debugging messages.
+          0x08         Turn on list debugging messsages.
+
+       2. You can append aui=1 to the end of the insmod line to cause
+           the adapter to use the AUI interface instead of the 10 Base T
+           interface.  This is also what to do if you want to use the BNC
+          connector on a TLAN based device.  (Setting this option on a
+          device that does not have an AUI/BNC connector will probably
+          cause it to not function correctly.)
+
+       3. If the driver is built into the kernel, you can use the 3rd
+          and 4th parameters to set aui and debug respectively.  For
+          example:
+
+          ether=0,0,0x1,0x7,eth0
+
+          This sets aui to 0x1 and debug to 0x7, assuming eth0 is a
+          supported TLAN device.
+
+          The bits in the third byte are assigned as follows:
+
+               0x01 = aui
+               0x02 = use SA_INTERRUPT flag when reserving the irq.
+
+
+IV.  Things to try if you have problems.
+       1. Make sure your card's PCI id is among those listed in
+          section I, above.
+       1. Make sure routing is correct.
+       2. If you are using a 2.1.x kernel, try to duplicate the
+          problem on a 2.0.x (preferably 2.0.29 or 2.0.30) kernel.
+       3. Set debug to 7, either in tlan.c or through insmod as in
+          section III.1 above.
+       4. Make sure klog is running so the kernel messages are
+          being recorded somewhere.
+       5. Run the following sequence of programs in order (you
+          may want to do this within an xterm, as background
+          traffic may cause a lot of TLAN RECEIVED: messages
+          on the console):
+
+          ifconfig eth0 your.ip.address netmask your.net.mask up
+          route add -net local.net.address eth0
+          ifconfig
+          ping some.computer.on.local.net
+          ifconfig eth0 down
+
+       6. Mail the log of what occurred to me.  Also include the
+          kernel version and what media/connector type (eg,
+          10 BaseT/RJ45, 100 BaseTX/RJ45, Thinnet/BNC, etc).
+
+
+
+Please e-mail me with any comments, successes, or failures.  Thanks.
+
+There is also a tlan mailing list which you can join by sending "subscribe tlan"
+in the body of an email to majordomo@vuser.vu.union.edu.  I will announce new
+releases of the TLAN driver there.
+
+James
+james.banks@caldera.com
+
index fc576e8217506e381b194a57adbb2965b75a73a1..449858a39419560914f68155b865960dc8f7593f 100644 (file)
@@ -98,6 +98,12 @@ M:   phil@tazenda.demon.co.uk
 L:     linux-net@vger.rutgers.edu
 S:     Maintained
 
+TLAN NETWORK DRIVER
+P:     James Banks
+M:     james.banks@caldera.com
+L:     linux-net@vger.rutgers.edu
+S:     Supported
+
 DIGI RIGHTSWITCH NETWORK DRIVER
 P:     Rick Richardson
 M:     rick@dgii.com
index 03e327ec02f66d413acc3cb9b2309913722b9ad6..83dd13f7351b20ff24f2cd372d5ed5073b8d8f20 100644 (file)
@@ -408,7 +408,7 @@ init_module(void)
 }
 
 #else
-int tc59x_probe(struct device *dev)
+int isa515_probe(struct device *dev)
 {
        int cards_found = 0;
 
diff --git a/drivers/net/Changelog.tlan b/drivers/net/Changelog.tlan
new file mode 100644 (file)
index 0000000..4ab47bc
--- /dev/null
@@ -0,0 +1,133 @@
+TLan Device Driver change log.
+
+0.42   - Coverted tranceiver from misused per-tranceiver functions
+         to a timer-oriented path that covers four possible classes
+         of tranceivers:
+               1. Unmanaged
+               2. Manual configuration
+               3. Autonegotiation w/ manual configuration
+               4. Autonegotiation w/ auto configuration
+       - Added ability to force speed and duplex settings.
+       - Made speed, duplex, sa_int, etc, to be set per adapter with
+         ether= command.
+       - Added support for Olicom OC-2326
+
+0.41   - Added non-bounce buffer paths.  Added TLan_FreeLists to
+         dispose of unused sk_buff's at device close time.
+       - Discovered inlined functions aren't being inlined, or at
+         least take up more space than macros would.
+
+0.40   - Refined polarity checking to handle case when polarity
+         changes to normal from abnormal.
+       - Cleaned up TLan_Probe routine.
+       - Added an option for the SA_INTERRUPT flag to be set.
+       - Created FAQ.
+       - Removed all C++ style comments.
+       - Added error message if devices busmastering is inactive.
+         Also will now skip device.
+       - Put cli and sti back into TLan_HandleInterrupt.  It makes
+         me feel better.
+       - Moved the code that checks for boot parameter options to 
+         tlan_probe.
+
+0.39   - Minor cosmetic cleanups (especially variable declarations).
+       - Changes low level TLAN functions to use dev structures instead
+         individual data elements.
+        - Changed low level TLAN functions not to play with sti and cli
+         if in an interrupt routine.
+       - Removed cli and sti from TLan_HandleInterrupt.
+
+0.38   - Added code to isolate the external PHY if the internal PHY is
+         being used for AUI/BNC connectivity.  Also set the aui and
+         debug variables from mem_start and mem_end if the driver is
+         built into the kernel.
+
+0.37   - Added TLAN_PHY_ACTIVITY flag for Unmanaged PHY.
+        - If aui is selected and the card is not a 0xF130 (unmanaged phy),
+         select the builtin PHY and put it in AUI mode.  I don't know if
+         this will work, but it is my best guess as to how AUI/BNC
+         functionality is being provided for the external managed PHYs,
+         which don't support AUI/BNC.
+       - If aui is set and the card is a 0xF130, set the MTXD1 bit in
+         the ACOMMIT register, to select AUI mode on the unmanaged PHY.
+         I don't know if this is necessary.
+
+0.36   - Changed AN_ADV register to not advertise full duplex modes.
+         100Mbs should work now on 0xAE32.
+       - Fixed a small bug where heartbeat and PHY interrupts were
+         always being enabled.
+       - Force the the driver into Unmanaged PHY mode for 0xF130 devices,
+         even if a managed (ie, the built-in one) PHY is detected.
+       - Moved the PHY initialization to after the onboard PHY is enabled,
+         if selected.
+
+0.35   - Added entry for Level One LXT970 PHY.
+       - Commented out instruction to set phyOnline to 1 in DP Check.
+
+0.34   - Revised Reset routine.
+       - Added support for 0xF130 device.
+       - Added entry for NS DP83840A Rev 0 PHY.
+
+0.33   - Major Reformatting of comments to fit in 80 columns.
+       - Changed tabs to 8 spaces, reformatted accordingly.
+       - Added code to check and change polarity.
+       - Added caveats to README.
+       - Small fix to Makefile to make unversioned modules correctly.
+       - Redid PhySelect to list all phy ids then choose one.
+
+0.32   - Put in another change for BNC stuff that I mistakenly
+         omitted in 0.31.
+
+0.31   - Completed BNC changes (at least one person had it working).
+       - Added another device id.
+       - Changed debugging messages to hopefully be more useful.
+
+0.30   - Added PCI device IDs provided by Don Dupuis of Compaq.
+       - Turned of MINTEN when using 10/100 PHY.
+       - Added ability to select AUI at insmod time.  What will this do
+         for BNC, if anything?
+       - Tweaked with names.  
+
+0.29   Fixed problem with older TLAN chips (pre-PG3.0 didn't have
+       EOC bit in lists nor INTDIS register).  Added support for NetFlex
+       card.
+
+0.28   Changed a lot of commented out printfs to debugging statements.
+       Added detail to product names.  Added 2nd reset to open command.
+       Don't know why, just have to, at least for Netelligent 10 card.
+
+0.27   Created a tlan_probe function for compiled-in-kernel use.
+       Also added debugging macro, which could be reviewed.  Is there
+       a kernel variable which stores desired verbosity/debug level?
+
+0.26   Change Invalid interrupt handler to return 0, not print out debug
+       message.  This was causing lots of messages to be printed out with
+       shared interrupts.
+
+0.25
+       Added IDs for integrated NetFlex-3 controller.  Not sure if it is even
+       TLAN yet, but we'll see.
+
+0.24
+       Increased second delay in internal PHY check routine, as driver wasn't
+       starting on the first insmod.
+
+0.23
+       Added documentation to function preambles.
+       Removed some commented out printk's.
+       Tested Rx and Tx busy frequency with current queue sizes (no busies).
+
+0.22
+       Cleared tbusy flag at adapter check.
+       Removed some commented out printk's.
+       Added TLan_SetMulticastList routine.
+
+0.21
+       Got all virt_to_bus calls in place for 2.1 kernels.
+
+0.20
+       Rewrote buffer/list subsystem to use bounce buffers as DMA
+         doesn't work over 16 Meg.  Why?  This is PCI not ISA.
+       Fixed a problem with buffers stalling due to a race condition
+         in the kernel.
+
index 8dfcd68ef7061540bef17a61044d049bca859f7d..19c38314424701ad847b84d423642470741587de 100644 (file)
@@ -103,6 +103,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
     tristate 'RTL 8139 support' CONFIG_RTL8139
     tristate 'SMC EtherPower II support' CONFIG_SMC_EPIC
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN
       bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
     fi
   fi
index d071b32522b865af263319a7a07a184332010801..ef5981144a8f197e16f9ebb9776055f2aa441727 100644 (file)
@@ -355,6 +355,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_TLAN),y)
+L_OBJS += tlan.o
+else
+  ifeq ($(CONFIG_TLAN),m)
+  M_OBJS += tlan.o
+  endif
+endif
+
 ifeq ($(CONFIG_ZNET),y)
 L_OBJS += znet.o
 endif
index c897e06a6718df2e069dc8a71790c2f9418b797f..d256092074db0867320ca79cbddff3766961c225 100644 (file)
@@ -83,6 +83,10 @@ extern int hydra_probe(struct device *);
 extern int yellowfin_probe(struct device *);
 extern int epic100_probe(struct device *);
 extern int rtl8139_probe(struct device *);
+extern int tlan_probe(struct device *);
+extern int isa515_probe(struct device *);
+extern int pcnet32_probe(struct device *);
+extern int lance_probe(struct device *);
 /* Detachable devices ("pocket adaptors") */
 extern int atp_init(struct device *);
 extern int de600_probe(struct device *);
@@ -231,6 +235,18 @@ ethif_probe(struct device *dev)
 #endif
 #ifdef CONFIG_SUNLANCE
        && sparc_lance_probe(dev)
+#endif
+#ifdef CONFIG_TLAN
+       && tlan_probe(dev)
+#endif
+#ifdef CONFIG_LANCE32
+       && pcnet32_probe(dev)
+#endif
+#ifdef CONFIG_CORKSCREW
+       && isa515_probe(dev)
+#endif
+#ifdef CONFIG_LANCE
+       && lance_probe(dev)
 #endif
        && 1 ) {
        return 1;       /* -ENODEV or -EAGAIN would be more accurate. */
index d6bf1f0c3a20281b814716fb94a0eed7ac323a7f..41d437e0f22d1215267fe1a04dd6023a5728b14b 100644 (file)
-/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
-/*
-   NOTICE: this version tested with kernels 1.3.72 and later only!
-       Written 1996-1998 by Donald Becker.
+/* at1700.c: A network device driver for  the Allied Telesis AT1700.
+
+       Written 1993-98 by Donald Becker.
+
+       Copyright 1993 United States Government as represented by the
+       Director, National Security Agency.
 
        This software may be used and distributed according to the terms
        of the GNU Public License, incorporated herein by reference.
 
-       This driver is for the Intel EtherExpress Pro 100B boards.
-       It should work with other i82557 boards (if any others exist).
-       To use a built-in driver, install as drivers/net/eepro100.c.
-       To use as a module, use the compile-command at the end of the file.
-
-       The author may be reached as becker@CESDIS.usra.edu, or C/O
+       The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
        Center of Excellence in Space Data and Information Sciences
-          Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
-       For updates see
-       <base href="http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html">
-*/
+          Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-static const char *version =
-"eepro100.c:v0.99A 2/7/98 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
-
-/* A few user-configurable values that apply to all boards.
-   First set are undocumented and spelled per Intel recommendations. */
+       This is a device driver for the Allied Telesis AT1700, which is a
+       straight-forward Fujitsu MB86965 implementation.
 
-static int congenb = 0;                /* Enable congestion control in the DP83840. */
-static int txfifo = 8;         /* Tx FIFO threshold in 4 byte units, 0-15 */
-static int rxfifo = 8;         /* Rx FIFO threshold, default 32 bytes. */
-/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
-static int txdmacount = 128;
-static int rxdmacount = 0;
+  Sources:
+    The Fujitsu MB86965 datasheet.
 
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
-   Lower values use more memory, but are faster. */
-static int rx_copybreak = 200;
+       After the initial version of this driver was written Gerry Sawkins of
+       ATI provided their EEPROM configuration code header file.
+    Thanks to NIIBE Yutaka <gniibe@mri.co.jp> for bug fixes.
 
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
+  Bugs:
+       The MB86965 has a design flaw that makes all probes unreliable.  Not
+       only is it difficult to detect, it also moves around in I/O space in
+       response to inb()s from other device probes!
+*/
 
-/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
-static int multicast_filter_limit = 64;
+static const char *version =
+       "at1700.c:v1.13 1/31/98  Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
 
-#include <linux/config.h>
-#ifdef MODULE
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
 #include <linux/module.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
 
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
 #include <linux/ptrace.h>
-#include <linux/errno.h>
 #include <linux/ioport.h>
+#include <linux/in.h>
 #include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/bios32.h>
-#include <asm/processor.h>             /* Processor type for cache alignment. */
+#include <linux/string.h>
+#include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
+#include <linux/errno.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/delay.h>
-
-/* Unused in the 2.0.* version, but retained for documentation. */
-#if LINUX_VERSION_CODE > 0x20118
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(congenb, "i");
-MODULE_PARM(txfifo, "i");
-MODULE_PARM(rxfifo, "i");
-MODULE_PARM(txdmacount, "i");
-MODULE_PARM(rxdmacount, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(multicast_filter_limit, "i");
-#endif
-
-#define RUN_AT(x) (jiffies + (x))
 
-#if (LINUX_VERSION_CODE < 0x20123)
-#define test_and_set_bit(val, addr) set_bit(val, addr)
-#endif
-
-/* The total I/O port extent of the board.
-   The registers beyond 0x18 only exist on the i82558. */
-#define SPEEDO3_TOTAL_SIZE 0x20
+/* Tunable parameters. */
 
-int speedo_debug = 1;
-
-/*
-                               Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
-single-chip fast ethernet controller for PCI, as used on the Intel
-EtherExpress Pro 100 adapter.
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board.  The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line.  While it's
-possible to share PCI interrupt lines, it negatively impacts performance and
-only recent kernels support it.
-
-III. Driver operation
-
-IIIA. General
-The Speedo3 is very similar to other Intel network chips, that is to say
-"apparently designed on a different planet".  This chips retains the complex
-Rx and Tx descriptors and multiple buffers pointers as previous chips, but
-also has simplified Tx and Rx buffer modes.  This driver uses the "flexible"
-Tx mode, but in a simplified lower-overhead manner: it associates only a
-single buffer descriptor with each frame descriptor.
-
-Despite the extra space overhead in each receive skbuff, the driver must use
-the simplified Rx buffer mode to assure that only a single data buffer is
-associated with each RxFD. The driver implements this by reserving space
-for the Rx descriptor at the head of each Rx skbuff
-
-The Speedo-3 has receive and command unit base addresses that are added to
-almost all descriptor pointers.  The driver sets these to zero, so that all
-pointer fields are absolute addresses.
-
-The System Control Block (SCB) of some previous Intel chips exists on the
-chip in both PCI I/O and memory space.  This driver uses the I/O space
-registers, but might switch to memory mapped mode to better support non-x86
-processors.
-
-IIIB. Transmit structure
-
-The driver must use the complex Tx command+descriptor mode in order to
-have a indirect pointer to the skbuff data section.  Each Tx command block
-(TxCB) is associated with a single, immediately appended Tx buffer descriptor
-(TxBD).  A fixed ring of these TxCB+TxBD pairs are kept as part of the
-speedo_private data structure for each adapter instance.
-
-This ring structure is used for all normal transmit packets, but the
-transmit packet descriptors aren't long enough for most non-Tx commands such
-as CmdConfigure.  This is complicated by the possibility that the chip has
-already loaded the link address in the previous descriptor.  So for these
-commands we convert the next free descriptor on the ring to a NoOp, and point
-that descriptor's link to the complex command.
-
-An additional complexity of these non-transmit commands are that they may be
-added asynchronous to the normal transmit queue, so we disable interrupts
-whenever the Tx descriptor ring is manipulated.
-
-A notable aspect of the these special configure commands is that they do
-work with the normal Tx ring entry scavenge method.  The Tx ring scavenge
-is done at interrupt time using the 'dirty_tx' index, and checking for the
-command-complete bit.  While the setup frames may have the NoOp command on the
-Tx ring marked as complete, but not have completed the setup command, this
-is not a problem.  The tx_ring entry can be still safely reused, as the
-tx_skbuff[] entry is always empty for config_cmd and mc_setup frames.
-
-Commands may have bits set e.g. CmdSuspend in the command word to either
-suspend or stop the transmit/command unit.  This driver always flags the last
-command with CmdSuspend, erases the CmdSuspend in the previous command, and
-then issues a CU_RESUME.
-Note: Watch out for the potential race condition here: imagine
-       erasing the previous suspend
-               the chip processes the previous command
-               the chip processes the final command, and suspends
-       doing the CU_RESUME
-               the chip processes the next-yet-valid post-final-command.
-So blindly sending a CU_RESUME is only safe if we do it immediately after
-after erasing the previous CmdSuspend, without the possibility of an
-intervening delay.  Thus the resume command is always within the
-interrupts-disabled region.  This is a timing dependence, but handling this
-condition in a timing-independent way would considerably complicate the code.
-
-Note: In previous generation Intel chips, restarting the command unit was a
-notoriously slow process.  This is presumably no longer true.
-
-IIIC. Receive structure
-
-Because of the bus-master support on the Speedo3 this driver uses the new
-SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer.
-This scheme allocates full-sized skbuffs as receive buffers.  The value
-SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to
-trade-off the memory wasted by passing the full-sized skbuff to the queue
-layer for all frames vs. the copying cost of copying a frame to a
-correctly-sized skbuff.
-
-For small frames the copying cost is negligible (esp. considering that we
-are pre-loading the cache with immediately useful header information), so we
-allocate a new, minimally-sized skbuff.  For large frames the copying cost
-is non-trivial, and the larger copy might flush the cache of useful data, so
-we pass up the skbuff the packet was received into.
-
-IIID. Synchronization
-The driver runs as two independent, single-threaded flows of control.  One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag.  The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'sp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.)         After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero.     Iff the 'sp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-IV. Notes
-
-Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
-that stated that I could disclose the information.  But I still resent
-having to sign an Intel NDA when I'm helping Intel sell their own product!
+/* When to switch from the 64-entry multicast filter to Rx-all-multicast. */
+#define MC_FILTERBREAK 64
 
-*/
+/* This unusual address order is used to verify the CONFIG register. */
+static int at1700_probe_list[] =
+{0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0};
 
-/* A few values that may be tweaked. */
-/* The ring sizes should be a power of two for efficiency. */
-#define TX_RING_SIZE   16              /* Effectively 2 entries fewer. */
-#define RX_RING_SIZE   16
-/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
-#define PKT_BUF_SZ             1536
 
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  ((800*HZ)/1000)
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
 
-/* How to wait for the command unit to accept a command.
-   Typically this takes 0 ticks. */
-static inline void wait_for_cmd_done(int cmd_ioaddr)
-{
-  short wait = 100;
-  do   ;
-  while(inb(cmd_ioaddr) && --wait >= 0);
-}
+typedef unsigned char uchar;
 
-/* Operational parameter that usually are not changed. */
+/* Information that need to be kept for each board. */
+struct net_local {
+       struct enet_statistics stats;
+       unsigned char mc_filter[8];
+       uint tx_started:1;                      /* Packets are on the Tx queue. */
+       uchar tx_queue;                         /* Number of packet on the Tx queue. */
+       ushort tx_queue_len;            /* Current length of the Tx queue. */
+};
 
-/* The rest of these values should never change. */
 
-/* Offsets to the various registers.
-   All accesses need not be longword aligned. */
-enum speedo_offsets {
-       SCBStatus = 0, SCBCmd = 2,      /* Rx/Command Unit command and status. */
-       SCBPointer = 4,                         /* General purpose pointer. */
-       SCBPort = 8,                            /* Misc. commands and operands.  */
-       SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
-       SCBCtrlMDI = 16,                        /* MDI interface control. */
-       SCBEarlyRx = 20,                        /* Early receive byte count. */
-};
-/* Commands that can be put in a command list entry. */
-enum commands {
-       CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
-       CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7,
-       CmdSuspend = 0x4000,            /* Suspend after completion. */
-       CmdIntr = 0x2000,                       /* Interrupt after completion. */
-       CmdTxFlex = 0x0008,                     /* Use "Flexible mode" for CmdTx command. */
-};
+/* Offsets from the base address. */
+#define STATUS                 0
+#define TX_STATUS              0
+#define RX_STATUS              1
+#define TX_INTR                        2               /* Bit-mapped interrupt enable registers. */
+#define RX_INTR                        3
+#define TX_MODE                        4
+#define RX_MODE                        5
+#define CONFIG_0               6               /* Misc. configuration settings. */
+#define CONFIG_1               7
+/* Run-time register bank 2 definitions. */
+#define DATAPORT               8               /* Word-wide DMA or programmed-I/O dataport. */
+#define TX_START               10
+#define MODE13                 13
+#define EEPROM_Ctrl    16
+#define EEPROM_Data    17
+#define IOCONFIG               19
+#define RESET                  31              /* Write to reset some parts of the chip. */
+#define AT1700_IO_EXTENT       32
 
-/* The SCB accepts the following controls for the Tx and Rx units: */
-#define         CU_START               0x0010
-#define         CU_RESUME              0x0020
-#define         CU_STATSADDR   0x0040
-#define         CU_SHOWSTATS   0x0050  /* Dump statistics counters. */
-#define         CU_CMD_BASE    0x0060  /* Base address to add to add CU commands. */
-#define         CU_DUMPSTATS   0x0070  /* Dump then reset stats counters. */
-
-#define         RX_START       0x0001
-#define         RX_RESUME      0x0002
-#define         RX_ABORT       0x0004
-#define         RX_ADDR_LOAD   0x0006
-#define         RX_RESUMENR    0x0007
-#define INT_MASK       0x0100
-#define DRVR_INT       0x0200          /* Driver generated interrupt. */
-
-/* The Speedo3 Rx and Tx frame/buffer descriptors. */
-struct descriptor {                    /* A generic descriptor. */
-       s16 status;             /* Offset 0. */
-       s16 command;            /* Offset 2. */
-       u32 link;                                       /* struct descriptor *  */
-       unsigned char params[0];
-};
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK   0x40    /* EEPROM shift clock, in reg. 16. */
+#define EE_CS                  0x20    /* EEPROM chip select, in reg. 16. */
+#define EE_DATA_WRITE  0x80    /* EEPROM chip data in, in reg. 17. */
+#define EE_DATA_READ   0x80    /* EEPROM chip data out, in reg. 17. */
 
-/* The Speedo3 Rx and Tx buffer descriptors. */
-struct RxFD {                                  /* Receive frame descriptor. */
-       s32 status;
-       u32 link;                                       /* struct RxFD * */
-       u32 rx_buf_addr;                        /* void * */
-       u16 count;
-       u16 size;
-};
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay() do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
 
-/* Elements of the RxFD.status word. */
-#define RX_COMPLETE 0x8000
-
-struct TxFD {                                  /* Transmit frame descriptor set. */
-       s32 status;
-       u32 link;                                       /* void * */
-       u32 tx_desc_addr;                       /* Always points to the tx_buf_addr element. */
-       s32 count;                                      /* # of TBD (=1), Tx start thresh., etc. */
-       /* This constitutes a single "TBD" entry -- we only use one. */
-       u32 tx_buf_addr;                        /* void *, frame to be transmitted.  */
-       s32 tx_buf_size;                        /* Length of Tx frame. */
-};
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD   (5 << 6)
+#define EE_READ_CMD            (6 << 6)
+#define EE_ERASE_CMD   (7 << 6)
 
-/* Elements of the dump_statistics block. This block must be lword aligned. */
-struct speedo_stats {
-       u32 tx_good_frames;
-       u32 tx_coll16_errs;
-       u32 tx_late_colls;
-       u32 tx_underruns;
-       u32 tx_lost_carrier;
-       u32 tx_deferred;
-       u32 tx_one_colls;
-       u32 tx_multi_colls;
-       u32 tx_total_colls;
-       u32 rx_good_frames;
-       u32 rx_crc_errs;
-       u32 rx_align_errs;
-       u32 rx_resource_errs;
-       u32 rx_overrun_errs;
-       u32 rx_colls_errs;
-       u32 rx_runt_errs;
-       u32 done_marker;
-};
 
-struct speedo_private {
-       char devname[8];                        /* Used only for kernel debugging. */
-       const char *product_name;
-       struct device *next_module;
-       struct TxFD     tx_ring[TX_RING_SIZE];  /* Commands (usually CmdTxPacket). */
-       /* The saved address of a sent-in-place packet/buffer, for skfree(). */
-       struct sk_buff* tx_skbuff[TX_RING_SIZE];
-       struct descriptor  *last_cmd;   /* Last command sent. */
-       /* Rx descriptor ring & addresses of receive-in-place skbuffs. */
-       struct RxFD *rx_ringp[RX_RING_SIZE];
-       struct sk_buff* rx_skbuff[RX_RING_SIZE];
-       struct RxFD *last_rxf;  /* Last command sent. */
-       struct enet_statistics stats;
-       struct speedo_stats lstats;
-       struct timer_list timer;        /* Media selection timer. */
-       long last_rx_time;                      /* Last Rx, in jiffies, to handle Rx hang. */
-       unsigned int cur_rx, cur_tx;            /* The next free ring entry */
-       unsigned int dirty_rx, dirty_tx;        /* The ring entries to be free()ed. */
-       struct descriptor config_cmd;   /* A configure command, with header... */
-       u8 config_cmd_data[22];                 /* .. and setup parameters. */
-       int mc_setup_frm_len;                           /* The length of an allocated.. */
-       struct descriptor *mc_setup_frm;        /* ..multicast setup frame. */
-       int in_interrupt;                                       /* Word-aligned dev->interrupt */
-       char rx_mode;                                           /* Current PROMISC/ALLMULTI setting. */
-       unsigned int tx_full:1;                         /* The Tx queue is full. */
-       unsigned int full_duplex:1;                     /* Full-duplex operation requested. */
-       unsigned int default_port:1;            /* Last dev->if_port value. */
-       unsigned int rx_bug:1;                          /* Work around receiver hang errata. */
-       unsigned int rx_bug10:1;                        /* Receiver might hang at 10mbps. */
-       unsigned int rx_bug100:1;                       /* Receiver might hang at 100mbps. */
-       unsigned short phy[2];                          /* PHY media interfaces available. */
-};
+/* Index to functions, as function prototypes. */
 
-/* The parameters for a CmdConfigure operation.
-   There are so many options that it would be difficult to document each bit.
-   We mostly use the default or recommended settings. */
-const char basic_config_cmd[22] = {
-       22, 0x08, 0, 0,  0, 0x80, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
-       0, 0x2E, 0,  0x60, 0,
-       0xf2, 0x48,   0, 0x40, 0xf2, 0x80,              /* 0x40=Force full-duplex */
-       0x3f, 0x05, };
-
-/* PHY media interface chips. */
-static const char *phys[] = {
-       "None", "i82553-A/B", "i82553-C", "i82503",
-       "DP83840", "80c240", "80c24", "i82555",
-       "unknown-8", "unknown-9", "DP83840A", "unknown-11",
-       "unknown-12", "unknown-13", "unknown-14", "unknown-15", };
-enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
-                                        S80C24, I82555, DP83840A=10, };
-static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
-
-static void speedo_found1(struct device *dev, int ioaddr, int irq,
-                                                 int card_idx);
+extern int at1700_probe(struct device *dev);
 
+static int at1700_probe1(struct device *dev, int ioaddr);
 static int read_eeprom(int ioaddr, int location);
-static int mdio_read(int ioaddr, int phy_id, int location);
-static int mdio_write(int ioaddr, int phy_id, int location, int value);
-static int speedo_open(struct device *dev);
-static void speedo_timer(unsigned long data);
-static void speedo_init_rx_ring(struct device *dev);
-static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
-static int speedo_rx(struct device *dev);
-static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int speedo_close(struct device *dev);
-static struct enet_statistics *speedo_get_stats(struct device *dev);
-static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static int net_open(struct device *dev);
+static int     net_send_packet(struct sk_buff *skb, struct device *dev);
+static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void net_rx(struct device *dev);
+static int net_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
 static void set_rx_mode(struct device *dev);
 
 \f
-
-/* The parameters that may be passed in... */
-/* 'options' is used to pass a transceiver override or full-duplex flag
-   e.g. "options=16" for FD, "options=32" for 100mbps-only. */
-static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-#ifdef MODULE
-static int debug = -1;                 /* The debug level */
-#endif
-
-/* A list of all installed Speedo devices, for removing the driver module. */
-static struct device *root_speedo_dev = NULL;
-
-int eepro100_init(struct device *dev)
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+   If dev->base_addr == 0, probe all likely locations.
+   If dev->base_addr == 1, always return failure.
+   If dev->base_addr == 2, allocate space for the device and return success
+   (detachable devices only).
+   */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+   boilerplate below. */
+struct netdev_entry at1700_drv =
+{"at1700", at1700_probe1, AT1700_IO_EXTENT, at1700_probe_list};
+#else
+int
+at1700_probe(struct device *dev)
 {
-       int cards_found = 0;
-
-       if (pcibios_present()) {
-               static int pci_index = 0;
-               for (; pci_index < 8; pci_index++) {
-                       unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
-                       int pci_ioaddr;
-
-                       unsigned short pci_command, new_command;
-
-                       if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
-                                                                       PCI_DEVICE_ID_INTEL_82557,
-                                                                       pci_index, &pci_bus,
-                                                                       &pci_device_fn))
-                         break;
-                       pcibios_read_config_byte(pci_bus, pci_device_fn,
-                                                                        PCI_INTERRUPT_LINE, &pci_irq_line);
-                       /* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */
-                       pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                                                         PCI_BASE_ADDRESS_1, &pci_ioaddr);
-                       /* Remove I/O space marker in bit 0. */
-                       pci_ioaddr &= ~3;
-                       if (speedo_debug > 2)
-                               printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n",
-                                          (int)pci_ioaddr, pci_irq_line);
-
-                       /* Get and check the bus-master and latency values. */
-                       pcibios_read_config_word(pci_bus, pci_device_fn,
-                                                                        PCI_COMMAND, &pci_command);
-                       new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
-                       if (pci_command != new_command) {
-                               printk(KERN_INFO "  The PCI BIOS has not enabled this"
-                                          " device!  Updating PCI command %4.4x->%4.4x.\n",
-                                          pci_command, new_command);
-                               pcibios_write_config_word(pci_bus, pci_device_fn,
-                                                                                 PCI_COMMAND, new_command);
-                       }
-                       pcibios_read_config_byte(pci_bus, pci_device_fn,
-                                                                                PCI_LATENCY_TIMER, &pci_latency);
-                       if (pci_latency < 32) {
-                               printk("  PCI latency timer (CFLT) is unreasonably low at %d."
-                                          "  Setting to 32 clocks.\n", pci_latency);
-                               pcibios_write_config_byte(pci_bus, pci_device_fn,
-                                                                                 PCI_LATENCY_TIMER, 32);
-                       } else if (speedo_debug > 1)
-                               printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
-
-                       speedo_found1(dev, pci_ioaddr, pci_irq_line, cards_found);
-                       dev = NULL;
-                       cards_found++;
-               }
+       int i;
+       int base_addr = dev ? dev->base_addr : 0;
+
+       if (base_addr > 0x1ff)          /* Check a single specified location. */
+               return at1700_probe1(dev, base_addr);
+       else if (base_addr != 0)        /* Don't probe at all. */
+               return ENXIO;
+
+       for (i = 0; at1700_probe_list[i]; i++) {
+               int ioaddr = at1700_probe_list[i];
+               if (check_region(ioaddr, AT1700_IO_EXTENT))
+                       continue;
+               if (at1700_probe1(dev, ioaddr) == 0)
+                       return 0;
        }
 
-       return cards_found;
+       return ENODEV;
 }
+#endif
 
-static void speedo_found1(struct device *dev, int ioaddr, int irq,
-                                                 int card_idx)
-{
-       static int did_version = 0;                     /* Already printed version info. */
-       struct speedo_private *sp;
-       char *product;
-       int i, option;
-       u16 eeprom[0x40];
-
-       if (speedo_debug > 0  &&  did_version++ == 0)
-               printk(version);
+/* The Fujitsu datasheet suggests that the NIC be probed for by checking its
+   "signature", the default bit pattern after a reset.  This *doesn't* work --
+   there is no way to reset the bus interface without a complete power-cycle!
 
-       dev = init_etherdev(dev, sizeof(struct speedo_private));
+   It turns out that ATI came to the same conclusion I did: the only thing
+   that can be done is checking a few bits and then diving right into an
+   EEPROM read. */
 
-       if (dev->mem_start > 0) 
-               option = dev->mem_start;
-       else if (card_idx >= 0  &&  options[card_idx] >= 0)
-               option = options[card_idx];
-       else
-               option = 0;
+int at1700_probe1(struct device *dev, int ioaddr)
+{
+       char irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15};
+       unsigned int i, irq;
 
-       /* Read the station address EEPROM before doing the reset.
-          Perhaps this should even be done before accepting the device,
-          then we wouldn't have a device name with which to report the error. */
-       {
-               u16 sum = 0;
-               int j;
-               for (j = 0, i = 0; i < 0x40; i++) {
-                       u16 value = read_eeprom(ioaddr, i);
-                       eeprom[i] = value;
-                       sum += value;
-                       if (i < 3) {
-                               dev->dev_addr[j++] = value;
-                               dev->dev_addr[j++] = value >> 8;
-                       }
-               }
-               if (sum != 0xBABA)
-                       printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, "
-                                  "check settings before activating this device!\n",
-                                  dev->name, sum);
-               /* Don't  unregister_netdev(dev);  as the EEPro may actually be
-                  usable, especially if the MAC address is set later. */
+       /* Resetting the chip doesn't reset the ISA interface, so don't bother.
+          That means we have to be careful with the register values we probe for.
+          */
+#ifdef notdef
+       printk("at1700 probe at %#x, eeprom is %4.4x %4.4x %4.4x ctrl %4.4x.\n",
+                  ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5),
+                  read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl));
+#endif
+       if (at1700_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr
+               || read_eeprom(ioaddr, 4) != 0x0000
+               || (read_eeprom(ioaddr, 5) & 0xff00) != 0xF400)
+               return -ENODEV;
+
+       /* Reset the internal state machines. */
+       outb(0, ioaddr + RESET);
+
+       irq = irqmap[(read_eeprom(ioaddr, 12)&0x04)
+                                | (read_eeprom(ioaddr, 0)>>14)];
+
+       /* Snarf the interrupt vector now. */
+       if (request_irq(irq, &net_interrupt, 0, "at1700", NULL)) {
+               printk ("AT1700 found at %#3x, but it's unusable due to a conflict on"
+                               "IRQ %d.\n", ioaddr, irq);
+               return EAGAIN;
        }
 
-       /* Reset the chip: stop Tx and Rx processes and clear counters.
-          This takes less than 10usec and will easily finish before the next
-          action. */
-       outl(0, ioaddr + SCBPort);
+       /* Allocate a new 'dev' if needed. */
+       if (dev == NULL)
+               dev = init_etherdev(0, sizeof(struct net_local));
+
+       /* Grab the region so that we can find another board if the IRQ request
+          fails. */
+       request_region(ioaddr, AT1700_IO_EXTENT, "at1700");
 
-       if (eeprom[3] & 0x0100)
-               product = "OEM i82557/i82558 10/100 Ethernet";
-       else
-               product = "Intel EtherExpress Pro 10/100";
+       printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name,
+                  ioaddr, irq);
 
-       printk(KERN_INFO "%s: %s at %#3x, ", dev->name, product, ioaddr);
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+       irq2dev_map[irq] = dev;
 
-       for (i = 0; i < 5; i++)
-               printk("%2.2X:", dev->dev_addr[i]);
-       printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
+       for(i = 0; i < 3; i++) {
+               unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
+               printk("%04x", eeprom_val);
+               ((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val);
+       }
 
-#ifndef kernel_bloat
-       /* OK, this is pure kernel bloat.  I don't like it when other drivers
-          waste non-pageable kernel space to emit similar messages, but I need
-          them for bug reports. */
+       /* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
+          rather than 150 ohm shielded twisted pair compensation.
+          0x0000 == auto-sense the interface
+          0x0800 == use TP interface
+          0x1800 == use coax interface
+          */
        {
-               const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
-               /* The self-test results must be paragraph aligned. */
-               s32 str[6], *volatile self_test_results;
-               int boguscnt = 16000;   /* Timeout for set-test. */
-               if (eeprom[3] & 0x03)
-                       printk(KERN_INFO "  Receiver lock-up bug exists -- enabling"
-                                  " work-around.\n");
-               printk(KERN_INFO "  Board assembly %4.4x%2.2x-%3.3d, Physical"
-                          " connectors present:",
-                          eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff);
-               for (i = 0; i < 4; i++)
-                       if (eeprom[5] & (1<<i))
-                               printk(connectors[i]);
-               printk("\n"KERN_INFO"  Primary interface chip %s PHY #%d.\n",
-                          phys[(eeprom[6]>>8)&15], eeprom[6] & 0x1f);
-               if (eeprom[7] & 0x0700)
-                       printk(KERN_INFO "    Secondary interface chip %s.\n",
-                                  phys[(eeprom[7]>>8)&7]);
-               if (((eeprom[6]>>8) & 0x3f) == DP83840
-                       ||  ((eeprom[6]>>8) & 0x3f) == DP83840A) {
-                       int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
-                       if (congenb)
-                         mdi_reg23 |= 0x0100;
-                       printk(KERN_INFO"  DP83840 specific setup, setting register 23 to %4.4x.\n",
-                                  mdi_reg23);
-                       mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
-               }
-               if ((option >= 0) && (option & 0x70)) {
-                       printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n",
-                                  (option & 0x20 ? 100 : 10),
-                                  (option & 0x10 ? "full" : "half"));
-                       mdio_write(ioaddr, eeprom[6] & 0x1f, 0,
-                                          ((option & 0x20) ? 0x2000 : 0) |     /* 100mbps? */
-                                          ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
-               }
+               const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"};
+               ushort setup_value = read_eeprom(ioaddr, 12);
 
-               /* Perform a system self-test. */
-               self_test_results = (s32*) ((((long) str) + 15) & ~0xf);
-               self_test_results[0] = 0;
-               self_test_results[1] = -1;
-               outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
-               do {
-                       udelay(10);
-               } while (self_test_results[1] == -1  &&  --boguscnt >= 0);
-
-               if (boguscnt < 0) {             /* Test optimized out. */
-                       printk(KERN_ERR "Self test failed, status %8.8x:\n"
-                                  KERN_ERR " Failure to initialize the i82557.\n"
-                                  KERN_ERR " Verify that the card is a bus-master"
-                                  " capable slot.\n",
-                                  self_test_results[1]);
-               } else
-                       printk(KERN_INFO "  General self-test: %s.\n"
-                                  KERN_INFO "  Serial sub-system self-test: %s.\n"
-                                  KERN_INFO "  Internal registers self-test: %s.\n"
-                                  KERN_INFO "  ROM checksum self-test: %s (%#8.8x).\n",
-                                  self_test_results[1] & 0x1000 ? "failed" : "passed",
-                                  self_test_results[1] & 0x0020 ? "failed" : "passed",
-                                  self_test_results[1] & 0x0008 ? "failed" : "passed",
-                                  self_test_results[1] & 0x0004 ? "failed" : "passed",
-                                  self_test_results[0]);
+               dev->if_port = setup_value >> 8;
+               printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]);
        }
-#endif  /* kernel_bloat */
 
-       outl(0, ioaddr + SCBPort);
+       /* Set the station address in bank zero. */
+       outb(0xe0, ioaddr + 7);
+       for (i = 0; i < 6; i++)
+               outb(dev->dev_addr[i], ioaddr + 8 + i);
 
-       /* We do a request_region() only to register /proc/ioports info. */
-       request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
+       /* Switch to bank 1 and set the multicast table to accept none. */
+       outb(0xe4, ioaddr + 7);
+       for (i = 0; i < 8; i++)
+               outb(0x00, ioaddr + 8 + i);
 
-       dev->base_addr = ioaddr;
-       dev->irq = irq;
+       /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+          bus access, two 4K Tx queues, and disabled Tx and Rx. */
+       outb(0xda, ioaddr + CONFIG_0);
 
-       if (dev->priv == NULL)
-               dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
-       sp = dev->priv;
-       memset(sp, 0, sizeof(*sp));
-       sp->next_module = root_speedo_dev;
-       root_speedo_dev = dev;
-
-       sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
-       if (card_idx >= 0) {
-               if (full_duplex[card_idx] >= 0)
-                       sp->full_duplex = full_duplex[card_idx];
-       }
-       sp->default_port = option >= 0 ? (option & 0x0f) : 0;
+       /* Switch to bank 2 and lock our I/O address. */
+       outb(0xe8, ioaddr + 7);
+       outb(dev->if_port, MODE13);
 
-       sp->phy[0] = eeprom[6];
-       sp->phy[1] = eeprom[7];
-       sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
+       /* Power-down the chip.  Aren't we green! */
+       outb(0x00, ioaddr + CONFIG_1);
 
-       if (sp->rx_bug)
-               printk(KERN_INFO "  Receiver lock-up workaround activated.\n");
+       if (net_debug)
+               printk(version);
+
+       /* Initialize the device structure. */
+       dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+       if (dev->priv == NULL)
+               return -ENOMEM;
+       memset(dev->priv, 0, sizeof(struct net_local));
 
-       /* The Speedo-specific entries in the device structure. */
-       dev->open = &speedo_open;
-       dev->hard_start_xmit = &speedo_start_xmit;
-       dev->stop = &speedo_close;
-       dev->get_stats = &speedo_get_stats;
+       dev->open               = net_open;
+       dev->stop               = net_close;
+       dev->hard_start_xmit = net_send_packet;
+       dev->get_stats  = net_get_stats;
        dev->set_multicast_list = &set_rx_mode;
-       dev->do_ioctl = &speedo_ioctl;
 
-       return;
+       /* Fill in the fields of 'dev' with ethernet-generic values. */
+          
+       ether_setup(dev);
+       return 0;
 }
-\f
-/* Serial EEPROM section.
-   A "bit" grungy, but we work our way through bit-by-bit :->. */
-/*  EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK   0x01    /* EEPROM shift clock. */
-#define EE_CS                  0x02    /* EEPROM chip select. */
-#define EE_DATA_WRITE  0x04    /* EEPROM chip data in. */
-#define EE_WRITE_0             0x01
-#define EE_WRITE_1             0x05
-#define EE_DATA_READ   0x08    /* EEPROM chip data out. */
-#define EE_ENB                 (0x4800 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
-   This will actually work with no delay on 33Mhz PCI.  */
-#define eeprom_delay(nanosec)          udelay(1);
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD   (5 << 6)
-#define EE_READ_CMD            (6 << 6)
-#define EE_ERASE_CMD   (7 << 6)
 
 static int read_eeprom(int ioaddr, int location)
 {
        int i;
        unsigned short retval = 0;
-       int ee_addr = ioaddr + SCBeeprom;
+       int ee_addr = ioaddr + EEPROM_Ctrl;
+       int ee_daddr = ioaddr + EEPROM_Data;
        int read_cmd = location | EE_READ_CMD;
-
-       outw(EE_ENB & ~EE_CS, ee_addr);
-       outw(EE_ENB, ee_addr);
-
+       short ctrl_val = EE_CS;
+       
+       outb(ctrl_val, ee_addr);
+       
        /* Shift the read command bits out. */
-       for (i = 10; i >= 0; i--) {
+       for (i = 9; i >= 0; i--) {
                short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
-               outw(EE_ENB | dataval, ee_addr);
-               eeprom_delay(100);
-               outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
-               eeprom_delay(150);
+               outb(dataval, ee_daddr);
+               outb(EE_CS | EE_SHIFT_CLK, ee_addr);    /* EEPROM clock tick. */
+               eeprom_delay();
+               outb(EE_CS, ee_addr);   /* Finish EEPROM a clock tick. */
+               eeprom_delay();
        }
-       outw(EE_ENB, ee_addr);
-
-       for (i = 15; i >= 0; i--) {
-               outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
-               eeprom_delay(100);
-               retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
-               outw(EE_ENB, ee_addr);
-               eeprom_delay(100);
+       outb(EE_CS, ee_addr);
+       
+       for (i = 16; i > 0; i--) {
+               outb(EE_CS | EE_SHIFT_CLK, ee_addr);
+               eeprom_delay();
+               retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
+               outb(EE_CS, ee_addr);
+               eeprom_delay();
        }
 
        /* Terminate the EEPROM access. */
-       outw(EE_ENB & ~EE_CS, ee_addr);
+       ctrl_val &= ~EE_CS;
+       outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+       eeprom_delay();
+       outb(ctrl_val, ee_addr);
+       eeprom_delay();
        return retval;
 }
 
-static int mdio_read(int ioaddr, int phy_id, int location)
-{
-       int val, boguscnt = 64*10;              /* <64 usec. to complete, typ 27 ticks */
-       outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
-       do {
-               val = inl(ioaddr + SCBCtrlMDI);
-               if (--boguscnt < 0) {
-                       printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
-               }
-       } while (! (val & 0x10000000));
-       return val & 0xffff;
-}
-
-static int mdio_write(int ioaddr, int phy_id, int location, int value)
-{
-       int val, boguscnt = 64*10;              /* <64 usec. to complete, typ 27 ticks */
-       outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
-                ioaddr + SCBCtrlMDI);
-       do {
-               val = inl(ioaddr + SCBCtrlMDI);
-               if (--boguscnt < 0) {
-                       printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
-               }
-       } while (! (val & 0x10000000));
-       return val & 0xffff;
-}
-
 \f
-static int
-speedo_open(struct device *dev)
+
+static int net_open(struct device *dev)
 {
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       struct net_local *lp = (struct net_local *)dev->priv;
        int ioaddr = dev->base_addr;
+       int i;
 
-#ifdef notdef
-       /* We could reset the chip, but should not need to. */
-       outl(0, ioaddr + SCBPort);
-       udelay(10);
-#endif
-
-       if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
-                                       "Intel EtherExpress Pro 10/100 Ethernet", dev)) {
-               return -EAGAIN;
-       }
-       if (speedo_debug > 1)
-               printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
-
-       MOD_INC_USE_COUNT;
+       /* Powerup the chip, initialize config register 1, and select bank 0. */
+       outb(0xe0, ioaddr + CONFIG_1);
 
-       /* Load the statistics block address. */
-       wait_for_cmd_done(ioaddr + SCBCmd);
-       outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
-       outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
-       sp->lstats.done_marker = 0;
+       /* Set the station address in bank zero. */
+       for (i = 0; i < 6; i++)
+               outb(dev->dev_addr[i], ioaddr + 8 + i);
 
-       speedo_init_rx_ring(dev);
-       wait_for_cmd_done(ioaddr + SCBCmd);
-       outl(0, ioaddr + SCBPointer);
-       outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+       /* Switch to bank 1 and set the multicast table to accept none. */
+       outb(0xe4, ioaddr + CONFIG_1);
+       for (i = 0; i < 8; i++)
+               outb(0x00, ioaddr + 8 + i);
 
-       /* Todo: verify that we must wait for previous command completion. */
-       wait_for_cmd_done(ioaddr + SCBCmd);
-       outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer);
-       outw(INT_MASK | RX_START, ioaddr + SCBCmd);
+       /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+          bus access, and two 4K Tx queues. */
+       outb(0xda, ioaddr + CONFIG_0);
 
-       /* Fill the first command with our physical address. */
-       {
-               u16 *eaddrs = (u16 *)dev->dev_addr;
-               u16 *setup_frm = (u16 *)&(sp->tx_ring[0].tx_desc_addr);
-
-               /* Avoid a bug(?!) here by marking the command already completed. */
-               sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
-               sp->tx_ring[0].link = virt_to_bus(&(sp->tx_ring[1]));
-               *setup_frm++ = eaddrs[0];
-               *setup_frm++ = eaddrs[1];
-               *setup_frm++ = eaddrs[2];
-       }
-       sp->last_cmd = (struct descriptor *)&sp->tx_ring[0];
-       sp->cur_tx = 1;
-       sp->dirty_tx = 0;
-       sp->tx_full = 0;
+       /* Same config 0, except enable the Rx and Tx. */
+       outb(0x5a, ioaddr + CONFIG_0);
+       /* Switch to register bank 2 for the run-time registers. */
+       outb(0xe8, ioaddr + CONFIG_1);
 
-       wait_for_cmd_done(ioaddr + SCBCmd);
-       outl(0, ioaddr + SCBPointer);
-       outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
+       lp->tx_started = 0;
+       lp->tx_queue = 0;
+       lp->tx_queue_len = 0;
 
-       dev->if_port = sp->default_port;
+       /* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */
+       outb(0x00, ioaddr + TX_INTR);
+       outb(0x81, ioaddr + RX_INTR);
 
-       sp->in_interrupt = 0;
        dev->tbusy = 0;
        dev->interrupt = 0;
        dev->start = 1;
 
-       /* Start the chip's Tx process and unmask interrupts. */
-       /* Todo: verify that we must wait for previous command completion. */
-       wait_for_cmd_done(ioaddr + SCBCmd);
-       outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer);
-       outw(CU_START, ioaddr + SCBCmd);
-
-       /* Setup the chip and configure the multicast list. */
-       sp->mc_setup_frm = NULL;
-       sp->mc_setup_frm_len = 0;
-       sp->rx_mode = -1;                       /* Invalid -> always reset the mode. */
-       set_rx_mode(dev);
-
-       if (speedo_debug > 2) {
-               printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
-                          dev->name, inw(ioaddr + SCBStatus));
-       }
-       /* Set the timer.  The timer serves a dual purpose:
-          1) to monitor the media interface (e.g. link beat) and perhaps switch
-          to an alternate media type
-          2) to monitor Rx activity, and restart the Rx process if the receiver
-          hangs. */
-       init_timer(&sp->timer);
-       sp->timer.expires = RUN_AT((24*HZ)/10);                         /* 2.4 sec. */
-       sp->timer.data = (unsigned long)dev;
-       sp->timer.function = &speedo_timer;                                     /* timer handler */
-       add_timer(&sp->timer);
-
-       wait_for_cmd_done(ioaddr + SCBCmd);
-       outw(CU_DUMPSTATS, ioaddr + SCBCmd);
-       return 0;
-}
-
-/* Media monitoring and control. */
-static void speedo_timer(unsigned long data)
-{
-       struct device *dev = (struct device *)data;
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
-       int tickssofar = jiffies - sp->last_rx_time;
-
-       if (speedo_debug > 3) {
-               int ioaddr = dev->base_addr;
-               printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n",
-                          dev->name, inw(ioaddr + SCBStatus));
-       }
-       if (sp->rx_bug) {
-               if (tickssofar > 2*HZ  || sp->rx_mode < 0) {
-                       /* We haven't received a packet in a Long Time.  We might have been
-                          bitten by the receiver hang bug.  This can be cleared by sending
-                          a set multicast list command. */
-                       set_rx_mode(dev);
-               }
-               /* We must continue to monitor the media. */
-               sp->timer.expires = RUN_AT(2*HZ);                       /* 2.0 sec. */
-               add_timer(&sp->timer);
-       }
-}
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-speedo_init_rx_ring(struct device *dev)
-{
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
-       struct RxFD *rxf, *last_rxf = NULL;
-       int i;
-
-       sp->cur_rx = 0;
-       sp->dirty_rx = RX_RING_SIZE - 1;
-
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               struct sk_buff *skb;
-               skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
-               sp->rx_skbuff[i] = skb;
-               if (skb == NULL)
-                       break;                  /* Bad news!  */
-               skb->dev = dev;                 /* Mark as being used by this device. */
-
-               rxf = (struct RxFD *)skb->tail;
-               skb_reserve(skb, sizeof(struct RxFD));
-               sp->rx_ringp[i] = rxf;
-               if (last_rxf)
-                       last_rxf->link = virt_to_bus(rxf);
-               last_rxf = rxf;
-               rxf->status = 0x00000001;                       /* '1' is flag value only. */
-               rxf->link = 0;                                          /* None yet. */
-               /* This field unused by i82557, we use it as a consistency check. */
-               rxf->rx_buf_addr = virt_to_bus(skb->tail);
-
-               rxf->count = 0;
-               rxf->size = PKT_BUF_SZ;
-       }
-       /* Mark the last entry as end-of-list. */
-       last_rxf->status = 0xC0000002;                  /* '2' is flag value only. */
-       sp->last_rxf = last_rxf;
-}
-
-static void speedo_tx_timeout(struct device *dev)
-{
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
-       int ioaddr = dev->base_addr;
-
-       printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
-                  "command %4.4x.\n",
-                  dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
+       MOD_INC_USE_COUNT;
 
-       if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
-         printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
-                        dev->name);
-         outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
-                  ioaddr + SCBPointer);
-         outw(CU_START, ioaddr + SCBCmd);
-       } else {
-         outw(DRVR_INT, ioaddr + SCBCmd);
-       }
-       /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
-       if ((sp->phy[0] & 0x8000) == 0) {
-               int phy_addr = sp->phy[0] & 0x1f;
-               mdio_write(ioaddr, phy_addr, 0, 0x0400);
-               mdio_write(ioaddr, phy_addr, 1, 0x0000);
-               mdio_write(ioaddr, phy_addr, 4, 0x0000);
-               mdio_write(ioaddr, phy_addr, 0, 0x8000);
-       }
-       sp->stats.tx_errors++;
-       dev->trans_start = jiffies;
-       return;
+       return 0;
 }
 
 static int
-speedo_start_xmit(struct sk_buff *skb, struct device *dev)
+net_send_packet(struct sk_buff *skb, struct device *dev)
 {
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       struct net_local *lp = (struct net_local *)dev->priv;
        int ioaddr = dev->base_addr;
-       int entry;
 
-       /* Block a timer-based transmit from overlapping.  This could better be
-          done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
-          If this ever occurs the queue layer is doing something evil! */
-       if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+       if (dev->tbusy) {
+               /* If we get here, some higher level has decided we are broken.
+                  There should really be a "kick me" function call instead. */
                int tickssofar = jiffies - dev->trans_start;
-               if (tickssofar < TX_TIMEOUT - 2)
+               if (tickssofar < 10)
                        return 1;
-               if (tickssofar < TX_TIMEOUT) {
-                       /* Reap sent packets from the full Tx queue. */
-                       outw(DRVR_INT, ioaddr + SCBCmd);
-                       return 1;
-               }
-               speedo_tx_timeout(dev);
-               return 0;
+               printk("%s: transmit timed out with status %04x, %s?\n", dev->name,
+                          inw(ioaddr + STATUS), inb(ioaddr + TX_STATUS) & 0x80
+                          ? "IRQ conflict" : "network cable problem");
+               printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
+                          dev->name, inw(ioaddr + 0), inw(ioaddr + 2), inw(ioaddr + 4),
+                          inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10),
+                          inw(ioaddr + 12), inw(ioaddr + 14));
+               lp->stats.tx_errors++;
+               /* ToDo: We should try to restart the adaptor... */
+               outw(0xffff, ioaddr + 24);
+               outw(0xffff, ioaddr + TX_STATUS);
+               outw(0xe85a, ioaddr + CONFIG_0);
+               outw(0x8100, ioaddr + TX_INTR);
+               dev->tbusy=0;
+               dev->trans_start = jiffies;
+               lp->tx_started = 0;
+               lp->tx_queue = 0;
+               lp->tx_queue_len = 0;
        }
 
-       /* Caution: the write order is important here, set the base address
-          with the "ownership" bits last. */
-
-       {       /* Prevent interrupts from changing the Tx ring from underneath us. */
-               unsigned long flags;
-
-               save_flags(flags);
-               cli();
-               /* Calculate the Tx descriptor entry. */
-               entry = sp->cur_tx++ % TX_RING_SIZE;
-
-               sp->tx_skbuff[entry] = skb;
-               /* Todo: be a little more clever about setting the interrupt bit. */
-               sp->tx_ring[entry].status =
-                       (CmdSuspend | CmdTx | CmdTxFlex) << 16;
-               sp->tx_ring[entry].link =
-                 virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
-               sp->tx_ring[entry].tx_desc_addr =
-                 virt_to_bus(&sp->tx_ring[entry].tx_buf_addr);
-               /* The data region is always in one buffer descriptor, Tx FIFO
-                  threshold of 256. */
-               sp->tx_ring[entry].count = 0x01208000;
-               sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data);
-               sp->tx_ring[entry].tx_buf_size = skb->len;
-               /* Todo: perhaps leave the interrupt bit set if the Tx queue is more
-                  than half full.  Argument against: we should be receiving packets
-                  and scavenging the queue.  Argument for: if so, it shouldn't
-                  matter. */
-               sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
-               sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-               /* Trigger the command unit resume. */
-               wait_for_cmd_done(ioaddr + SCBCmd);
-               outw(CU_RESUME, ioaddr + SCBCmd);
-               restore_flags(flags);
+       /* If some higher layer thinks we've missed an tx-done interrupt
+          we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+          itself. */
+       if (skb == NULL) {
+               dev_tint(dev);
+               return 0;
        }
 
-       /* Leave room for set_rx_mode() to fill two entries. */
-       if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3)
-               sp->tx_full = 1;
-       else
-               dev->tbusy = 0;
-
-       dev->trans_start = jiffies;
+       /* Block a timer-based transmit from overlapping.  This could better be
+          done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+       if (set_bit(0, (void*)&dev->tbusy) != 0)
+               printk("%s: Transmitter access conflict.\n", dev->name);
+       else {
+               short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+               unsigned char *buf = skb->data;
+
+               /* Turn off the possible Tx interrupts. */
+               outb(0x00, ioaddr + TX_INTR);
+               
+               outw(length, ioaddr + DATAPORT);
+               outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+               lp->tx_queue++;
+               lp->tx_queue_len += length + 2;
+
+               if (lp->tx_started == 0) {
+                       /* If the Tx is idle, always trigger a transmit. */
+                       outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+                       lp->tx_queue = 0;
+                       lp->tx_queue_len = 0;
+                       dev->trans_start = jiffies;
+                       lp->tx_started = 1;
+                       dev->tbusy = 0;
+               } else if (lp->tx_queue_len < 4096 - 1502)
+                       /* Yes, there is room for one more packet. */
+                       dev->tbusy = 0;
+
+               /* Turn on Tx interrupts back on. */
+               outb(0x82, ioaddr + TX_INTR);
+       }
+       dev_kfree_skb (skb, FREE_WRITE);
 
        return 0;
 }
-
-/* The interrupt handler does all of the Rx thread work and cleans up
-   after the Tx thread. */
-static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+\f
+/* The typical workload of the driver:
+   Handle the network interface interrupts. */
+static void
+net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-       struct device *dev = (struct device *)dev_instance;
-       struct speedo_private *sp;
-       int ioaddr, boguscnt = max_interrupt_work;
-       unsigned short status;
+       struct device *dev = (struct device *)(irq2dev_map[irq]);
+       struct net_local *lp;
+       int ioaddr, status;
 
-#ifndef final_version
        if (dev == NULL) {
-               printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq);
+               printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);
                return;
        }
-#endif
+       dev->interrupt = 1;
 
        ioaddr = dev->base_addr;
-       sp = (struct speedo_private *)dev->priv;
-#ifndef final_version
-       /* A lock to prevent simultaneous entry on SMP machines. */
-       if (test_and_set_bit(0, (void*)&sp->in_interrupt)) {
-               printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
-                          dev->name);
-               return;
+       lp = (struct net_local *)dev->priv;
+       status = inw(ioaddr + TX_STATUS);
+       outw(status, ioaddr + TX_STATUS);
+
+       if (net_debug > 4)
+               printk("%s: Interrupt with status %04x.\n", dev->name, status);
+       if (status & 0xff00
+               ||  (inb(ioaddr + RX_MODE) & 0x40) == 0) {                      /* Got a packet(s). */
+               net_rx(dev);
        }
-       dev->interrupt = 1;
-#endif
-
-       do {
-               status = inw(ioaddr + SCBStatus);
-               /* Acknowledge all of the current interrupt sources ASAP. */
-               outw(status & 0xfc00, ioaddr + SCBStatus);
-
-               if (speedo_debug > 4)
-                       printk(KERN_DEBUG "%s: interrupt  status=%#4.4x.\n",
-                                  dev->name, status);
-
-               if ((status & 0xfc00) == 0)
-                       break;
-
-               if (status & 0x4000)     /* Packet received. */
-                       speedo_rx(dev);
-
-               if (status & 0x1000) {
-                 if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
-                       outw(RX_RESUMENR, ioaddr + SCBCmd);
-                 else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
-                       /* No idea of what went wrong.  Restart the receiver. */
-                       outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
-                                ioaddr + SCBPointer);
-                       outw(RX_START, ioaddr + SCBCmd);
-                 }
-                 sp->stats.rx_errors++;
-               }
-
-               /* User interrupt, Command/Tx unit interrupt or CU not active. */
-               if (status & 0xA400) {
-                       unsigned int dirty_tx = sp->dirty_tx;
-
-                       while (sp->cur_tx - dirty_tx > 0) {
-                               int entry = dirty_tx % TX_RING_SIZE;
-                               int status = sp->tx_ring[entry].status;
-
-                               if (speedo_debug > 5)
-                                       printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
-                                                  entry, status);
-                               if ((status & 0x8000) == 0)
-                                       break;                  /* It still hasn't been processed. */
-                               /* Free the original skb. */
-                               if (sp->tx_skbuff[entry]) {
-                                       sp->stats.tx_packets++; /* Count only user packets. */
-                                       dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE);
-                                       sp->tx_skbuff[entry] = 0;
-                               }
-                               dirty_tx++;
-                       }
-
-#ifndef final_version
-                       if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
-                               printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
-                                          " full=%d.\n",
-                                          dirty_tx, sp->cur_tx, sp->tx_full);
-                               dirty_tx += TX_RING_SIZE;
-                       }
-#endif
-
-                       if (sp->tx_full && dev->tbusy
-                               && dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) {
-                               /* The ring is no longer full, clear tbusy. */
-                               sp->tx_full = 0;
+       if (status & 0x00ff) {
+               if (status & 0x80) {
+                       lp->stats.tx_packets++;
+                       if (lp->tx_queue) {
+                               outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+                               lp->tx_queue = 0;
+                               lp->tx_queue_len = 0;
+                               dev->trans_start = jiffies;
                                dev->tbusy = 0;
-                               mark_bh(NET_BH);
+                               mark_bh(NET_BH);        /* Inform upper layers. */
+                       } else {
+                               lp->tx_started = 0;
+                               /* Turn on Tx interrupts off. */
+                               outb(0x00, ioaddr + TX_INTR);
+                               dev->tbusy = 0;
+                               mark_bh(NET_BH);        /* Inform upper layers. */
                        }
-
-                       sp->dirty_tx = dirty_tx;
-               }
-
-               if (--boguscnt < 0) {
-                       printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
-                                  dev->name, status);
-                       /* Clear all interrupt sources. */
-                       outl(0xfc00, ioaddr + SCBStatus);
-                       break;
                }
-       } while (1);
-
-       if (speedo_debug > 3)
-               printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
-                          dev->name, inw(ioaddr + SCBStatus));
+       }
 
        dev->interrupt = 0;
-       sp->in_interrupt = 0;
        return;
 }
 
-static int
-speedo_rx(struct device *dev)
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+net_rx(struct device *dev)
 {
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
-       int entry = sp->cur_rx % RX_RING_SIZE;
-       int status;
-
-       if (speedo_debug > 4)
-               printk(KERN_DEBUG " In speedo_rx().\n");
-       /* If we own the next entry, it's a new packet. Send it up. */
-       while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) {
-
-               if (speedo_debug > 4)
-                       printk(KERN_DEBUG "  speedo_rx() status %8.8x len %d.\n", status,
-                                  sp->rx_ringp[entry]->count & 0x3fff);
-               if (status & 0x0200) {
-                       printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
-                                  "status %8.8x!\n", dev->name, status);
-               } else if ( ! (status & 0x2000)) {
-                       /* There was a fatal error.  This *should* be impossible. */
-                       sp->stats.rx_errors++;
-                       printk(KERN_ERR "%s: Anomalous event in speedo_rx(), status %8.8x.\n",
-                                  dev->name, status);
+       struct net_local *lp = (struct net_local *)dev->priv;
+       int ioaddr = dev->base_addr;
+       int boguscount = 5;
+
+       while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
+               ushort status = inw(ioaddr + DATAPORT);
+               ushort pkt_len = inw(ioaddr + DATAPORT);
+
+               if (net_debug > 4)
+                       printk("%s: Rxing packet mode %02x status %04x.\n",
+                                  dev->name, inb(ioaddr + RX_MODE), status);
+#ifndef final_version
+               if (status == 0) {
+                       outb(0x05, ioaddr + 14);
+                       break;
+               }
+#endif
+
+               if ((status & 0xF0) != 0x20) {  /* There was an error. */
+                       lp->stats.rx_errors++;
+                       if (status & 0x08) lp->stats.rx_length_errors++;
+                       if (status & 0x04) lp->stats.rx_frame_errors++;
+                       if (status & 0x02) lp->stats.rx_crc_errors++;
+                       if (status & 0x01) lp->stats.rx_over_errors++;
                } else {
-                       /* Malloc up new buffer, compatible with net-2e. */
-                       int pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
+                       /* Malloc up new buffer. */
                        struct sk_buff *skb;
-                       int rx_in_place = 0;
-
-                       /* Check if the packet is long enough to just accept without
-                          copying to a properly sized skbuff. */
-                       if (pkt_len > rx_copybreak) {
-                               struct sk_buff *newskb;
-                               char *temp;
-
-                               /* Pass up the skb already on the Rx ring. */
-                               skb = sp->rx_skbuff[entry];
-                               temp = skb_put(skb, pkt_len);
-                               if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
-                                       printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
-                                                  " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name,
-                                                  sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
-                               /* Get a fresh skbuff to replace the filled one. */
-                               newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
-
-                               if (newskb) {
-                                       struct RxFD *rxf;
-                                       rx_in_place = 1;
-                                       sp->rx_skbuff[entry] = newskb;
-                                       newskb->dev = dev;
-                                       rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail;
-                                       skb_reserve(newskb, sizeof(struct RxFD));
-                                       /* Unused by i82557, consistency check only. */
-                                       rxf->rx_buf_addr = virt_to_bus(newskb->tail);
-                                       rxf->status = 0x00000001;
-                               } else                  /* No memory, drop the packet. */
-                                 skb = 0;
-                       } else
-                               skb = dev_alloc_skb(pkt_len + 2);
+
+                       if (pkt_len > 1550) {
+                               printk("%s: The AT1700 claimed a very large packet, size %d.\n",
+                                          dev->name, pkt_len);
+                               /* Prime the FIFO and then flush the packet. */
+                               inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
+                               outb(0x05, ioaddr + 14);
+                               lp->stats.rx_errors++;
+                               break;
+                       }
+                       skb = dev_alloc_skb(pkt_len+3);
                        if (skb == NULL) {
-                               int i;
-                               printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name);
-                               /* Check that at least two ring entries are free.
-                                  If not, free one and mark stats->rx_dropped++. */
-                               /* ToDo: This is not correct!!!!  We should count the number
-                                  of linked-in Rx buffer to very that we have at least two
-                                  remaining. */
-                               for (i = 0; i < RX_RING_SIZE; i++)
-                                       if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status)
-                                                  & RX_COMPLETE))
-                                               break;
-
-                               if (i > RX_RING_SIZE -2) {
-                                       sp->stats.rx_dropped++;
-                                       sp->rx_ringp[entry]->status = 0;
-                                       sp->cur_rx++;
-                               }
+                               printk("%s: Memory squeeze, dropping packet (len %d).\n",
+                                          dev->name, pkt_len);
+                               /* Prime the FIFO and then flush the packet. */
+                               inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
+                               outb(0x05, ioaddr + 14);
+                               lp->stats.rx_dropped++;
                                break;
                        }
                        skb->dev = dev;
-                       if (! rx_in_place) {
-                               skb_reserve(skb, 2);    /* 16 byte align the data fields */
-#if defined(__i386)   &&  notyet
-                               /* Packet is in one chunk -- we can copy + cksum. */
-                               eth_io_copy_and_sum(skb, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
-                                                                       pkt_len, 0);
-#else
-                               memcpy(skb_put(skb, pkt_len),
-                                          bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
-#endif
-                       }
-                       skb->protocol = eth_type_trans(skb, dev);
+                       skb_reserve(skb,2);
+
+                       insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
+                       skb->protocol=eth_type_trans(skb, dev);
                        netif_rx(skb);
-                       sp->stats.rx_packets++;
+                       lp->stats.rx_packets++;
                }
+               if (--boguscount <= 0)
+                       break;
+       }
 
-               /*      ToDo: This is better than before, but should be checked. */
-               {
-                       struct RxFD *rxf = sp->rx_ringp[entry];
-                       rxf->status = 0xC0000003;               /* '3' for verification only */
-                       rxf->link = 0;                  /* None yet. */
-                       rxf->count = 0;
-                       rxf->size = PKT_BUF_SZ;
-                       sp->last_rxf->link = virt_to_bus(rxf);
-                       sp->last_rxf->status &= ~0xC0000000;
-                       sp->last_rxf = rxf;
-                       entry = (++sp->cur_rx) % RX_RING_SIZE;
+       /* If any worth-while packets have been received, dev_rint()
+          has done a mark_bh(NET_BH) for us and will work on them
+          when we get to the bottom-half routine. */
+       {
+               int i;
+               for (i = 0; i < 20; i++) {
+                       if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
+                               break;
+                       inw(ioaddr + DATAPORT);                         /* dummy status read */
+                       outb(0x05, ioaddr + 14);
                }
-       }
 
-       sp->last_rx_time = jiffies;
-       return 0;
+               if (net_debug > 5)
+                       printk("%s: Exint Rx packet with mode %02x after %d ticks.\n", 
+                                  dev->name, inb(ioaddr + RX_MODE), i);
+       }
+       return;
 }
 
-static int
-speedo_close(struct device *dev)
+/* The inverse routine to net_open(). */
+static int net_close(struct device *dev)
 {
        int ioaddr = dev->base_addr;
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
-       int i;
 
-       dev->start = 0;
        dev->tbusy = 1;
+       dev->start = 0;
 
-       if (speedo_debug > 1)
-               printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
-                          dev->name, inw(ioaddr + SCBStatus));
-
-       /* Shut off the media monitoring timer. */
-       del_timer(&sp->timer);
-
-       /* Disable interrupts, and stop the chip's Rx process. */
-       outw(INT_MASK, ioaddr + SCBCmd);
-       outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
+       /* Set configuration register 0 to disable Tx and Rx. */
+       outb(0xda, ioaddr + CONFIG_0);
 
-       free_irq(dev->irq, dev);
+       /* Update the statistics -- ToDo. */
 
-       /* Free all the skbuffs in the Rx and Tx queues. */
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               struct sk_buff *skb = sp->rx_skbuff[i];
-               sp->rx_skbuff[i] = 0;
-               /* Clear the Rx descriptors. */
-               if (skb)
-                       dev_kfree_skb(skb, FREE_WRITE);
-       }
+       /* Power-down the chip.  Green, green, green! */
+       outb(0x00, ioaddr + CONFIG_1);
 
-       for (i = 0; i < TX_RING_SIZE; i++) {
-               struct sk_buff *skb = sp->tx_skbuff[i];
-               sp->tx_skbuff[i] = 0;
-               /* Clear the Tx descriptors. */
-               if (skb)
-                       dev_kfree_skb(skb, FREE_WRITE);
-       }
-       if (sp->mc_setup_frm) {
-               kfree(sp->mc_setup_frm);
-               sp->mc_setup_frm_len = 0;
-       }
-
-       /* Print a few items for debugging. */
-       if (speedo_debug > 3) {
-               int phy_num = sp->phy[0] & 0x1f;
-               printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n",
-                          dev->name, sp->cur_rx);
-
-               for (i = 0; i < RX_RING_SIZE; i++)
-                       printk(KERN_DEBUG "  Rx ring entry %d  %8.8x.\n",
-                                  i, (int)sp->rx_ringp[i]->status);
-
-               for (i = 0; i < 5; i++)
-                       printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n",
-                                  phy_num, i, mdio_read(ioaddr, phy_num, i));
-               for (i = 21; i < 26; i++)
-                       printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n",
-                                  phy_num, i, mdio_read(ioaddr, phy_num, i));
-       }
        MOD_DEC_USE_COUNT;
 
        return 0;
 }
 
-/* The Speedo-3 has an especially awkward and unusable method of getting
-   statistics out of the chip.  It takes an unpredictable length of time
-   for the dump-stats command to complete.  To avoid a busy-wait loop we
-   update the stats with the previous dump results, and then trigger a
-   new dump.
-
-   These problems are mitigated by the current /proc implementation, which
-   calls this routine first to judge the output length, and then to emit the
-   output.
-
-   Oh, and incoming frames are dropped while executing dump-stats!
-   */
+/* Get the current statistics.
+   This may be called with the card open or closed.
+   There are no on-chip counters, so this function is trivial.
+*/
 static struct enet_statistics *
-speedo_get_stats(struct device *dev)
+net_get_stats(struct device *dev)
 {
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
-       int ioaddr = dev->base_addr;
-
-       if (sp->lstats.done_marker == 0xA007) { /* Previous dump finished */
-               sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs;
-               sp->stats.tx_window_errors += sp->lstats.tx_late_colls;
-               sp->stats.tx_fifo_errors += sp->lstats.tx_underruns;
-               sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier;
-               /*sp->stats.tx_deferred += sp->lstats.tx_deferred;*/
-               sp->stats.collisions += sp->lstats.tx_total_colls;
-               sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs;
-               sp->stats.rx_frame_errors += sp->lstats.rx_align_errs;
-               sp->stats.rx_over_errors += sp->lstats.rx_resource_errs;
-               sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
-               sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
-               sp->lstats.done_marker = 0x0000;
-               if (dev->start) {
-                       wait_for_cmd_done(ioaddr + SCBCmd);
-                       outw(CU_DUMPSTATS, ioaddr + SCBCmd);
-               }
-       }
-       return &sp->stats;
+       struct net_local *lp = (struct net_local *)dev->priv;
+       return &lp->stats;
 }
 
-static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+/*
+  Set the multicast/promiscuous mode for this adaptor.
+*/
+
+/* The little-endian AUTODIN II ethernet CRC calculation.
+   N.B. Do not use for bulk data, use a table-based routine instead.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
 {
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
-       int ioaddr = dev->base_addr;
-       u16 *data = (u16 *)&rq->ifr_data;
-       int phy = sp->phy[0] & 0x1f;
-
-    switch(cmd) {
-       case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
-               data[0] = phy;
-       case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
-               data[3] = mdio_read(ioaddr, data[0], data[1]);
-               return 0;
-       case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
-               if (!suser())
-                       return -EPERM;
-               mdio_write(ioaddr, data[0], data[1], data[2]);
-               return 0;
-       default:
-               return -EOPNOTSUPP;
+       unsigned int crc = 0xffffffff;  /* Initial value. */
+       while(--length >= 0) {
+               unsigned char current_octet = *data++;
+               int bit;
+               for (bit = 8; --bit >= 0; current_octet >>= 1) {
+                       if ((crc ^ current_octet) & 1) {
+                               crc >>= 1;
+                               crc ^= ethernet_polynomial_le;
+                       } else
+                               crc >>= 1;
+               }
        }
+       return crc;
 }
 
-/* Set or clear the multicast filter for this adaptor.
-   This is very ugly with Intel chips -- we usually have to execute an
-   entire configuration command, plus process a multicast command.
-   This is complicated.  We must put a large configuration command and
-   an arbitrarily-sized multicast command in the transmit list.
-   To minimize the disruption -- the previous command might have already
-   loaded the link -- we convert the current command block, normally a Tx
-   command, into a no-op and link it to the new command.
-*/
 static void
 set_rx_mode(struct device *dev)
 {
-       struct speedo_private *sp = (struct speedo_private *)dev->priv;
        int ioaddr = dev->base_addr;
-       char new_rx_mode;
-       unsigned long flags;
-       int entry, i;
-
-       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
-               new_rx_mode = 3;
-       } else if ((dev->flags & IFF_ALLMULTI)  ||
-                          dev->mc_count > multicast_filter_limit) {
-               new_rx_mode = 1;
-       } else
-               new_rx_mode = 0;
-
-       if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
-         /* The Tx ring is full -- don't add anything!  Presumably the new mode
-                is in config_cmd_data and will be added anyway. */
-               sp->rx_mode = -1;
-               return;
-       }
-
-       if (new_rx_mode != sp->rx_mode) {
-               /* We must change the configuration. Construct a CmdConfig frame. */
-               memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd));
-               sp->config_cmd_data[1] = (txfifo << 4) | rxfifo;
-               sp->config_cmd_data[4] = rxdmacount;
-               sp->config_cmd_data[5] = txdmacount + 0x80;
-               sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48;
-               sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80;
-               sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
-               if (sp->phy[0] & 0x8000) {                      /* Use the AUI port instead. */
-                 sp->config_cmd_data[15] |= 0x80;
-                 sp->config_cmd_data[8] = 0;
-               }
-               save_flags(flags);
-               cli();
-               /* Fill the "real" tx_ring frame with a no-op and point it to us. */
-               entry = sp->cur_tx++ % TX_RING_SIZE;
-               sp->tx_skbuff[entry] = 0;       /* Nothing to free. */
-               sp->tx_ring[entry].status = CmdNOp << 16;
-               sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd);
-               sp->config_cmd.status = 0;
-               sp->config_cmd.command = CmdSuspend | CmdConfigure;
-               sp->config_cmd.link =
-                 virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
-               sp->last_cmd->command &= ~CmdSuspend;
-               /* Immediately trigger the command unit resume. */
-               wait_for_cmd_done(ioaddr + SCBCmd);
-               outw(CU_RESUME, ioaddr + SCBCmd);
-               sp->last_cmd = &sp->config_cmd;
-               restore_flags(flags);
-               if (speedo_debug > 5) {
-                       int i;
-                       printk(KERN_DEBUG " CmdConfig frame in entry %d.\n", entry);
-                       for(i = 0; i < 32; i++)
-                               printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]);
-                       printk(".\n");
-               }
-       }
-
-       if (new_rx_mode == 0  &&  dev->mc_count < 3) {
-               /* The simple case of 0-2 multicast list entries occurs often, and
-                  fits within one tx_ring[] entry. */
-               u16 *setup_params, *eaddrs;
-               struct dev_mc_list *mclist;
-
-               save_flags(flags);
-               cli();
-               entry = sp->cur_tx++ % TX_RING_SIZE;
-               sp->tx_skbuff[entry] = 0;
-               sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16;
-               sp->tx_ring[entry].link =
-                 virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
-               sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
-               setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr;
-               *setup_params++ = dev->mc_count*6;
-               /* Fill in the multicast addresses. */
-               for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
-                        i++, mclist = mclist->next) {
-                       eaddrs = (u16 *)mclist->dmi_addr;
-                       *setup_params++ = *eaddrs++;
-                       *setup_params++ = *eaddrs++;
-                       *setup_params++ = *eaddrs++;
-               }
+       struct net_local *lp = (struct net_local *)dev->priv;
+       unsigned char mc_filter[8];              /* Multicast hash filter */
+       long flags;
+       int i;
 
-               sp->last_cmd->command &= ~CmdSuspend;
-               /* Immediately trigger the command unit resume. */
-               wait_for_cmd_done(ioaddr + SCBCmd);
-               outw(CU_RESUME, ioaddr + SCBCmd);
-               sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-               restore_flags(flags);
-       } else if (new_rx_mode == 0) {
-               /* This does not work correctly, but why not? */
+       if (dev->flags & IFF_PROMISC) {
+               /* Unconditionally log net taps. */
+               printk("%s: Promiscuous mode enabled.\n", dev->name);
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               outb(3, ioaddr + RX_MODE);      /* Enable promiscuous mode */
+       } else if (dev->mc_count > MC_FILTERBREAK
+                          ||  (dev->flags & IFF_ALLMULTI)) {
+               /* Too many to filter perfectly -- accept all multicasts. */
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               outb(2, ioaddr + RX_MODE);      /* Use normal mode. */
+       } else if (dev->mc_count == 0) {
+               memset(mc_filter, 0x00, sizeof(mc_filter));
+               outb(1, ioaddr + RX_MODE);      /* Ignore almost all multicasts. */
+       } else {
                struct dev_mc_list *mclist;
-               u16 *eaddrs;
-               struct descriptor *mc_setup_frm = sp->mc_setup_frm;
-               u16 *setup_params;
                int i;
 
-               if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
-                       || sp->mc_setup_frm == NULL) {
-                       /* Allocate a new frame, 10bytes + addrs, with a few
-                          extra entries for growth. */
-                       if (sp->mc_setup_frm)
-                               kfree(sp->mc_setup_frm);
-                       sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
-                       sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC);
-                       if (sp->mc_setup_frm == NULL) {
-                         printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", dev->name);
-                               sp->rx_mode = -1; /* We failed, try again. */
-                               return;
-                       }
-               }
-               mc_setup_frm = sp->mc_setup_frm;
-               /* Construct the new setup frame. */
-               if (speedo_debug > 1)
-                       printk(KERN_DEBUG "%s: Constructing a setup frame at %p, "
-                                  "%d bytes.\n",
-                                  dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
-               mc_setup_frm->status = 0;
-               mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
-               /* Link set below. */
-               setup_params = (u16 *)mc_setup_frm->params;
-               *setup_params++ = dev->mc_count*6;
-               /* Fill in the multicast addresses. */
-               for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
-                        i++, mclist = mclist->next) {
-                       eaddrs = (u16 *)mclist->dmi_addr;
-                       *setup_params++ = *eaddrs++;
-                       *setup_params++ = *eaddrs++;
-                       *setup_params++ = *eaddrs++;
-               }
-
-               /* Disable interrupts while playing with the Tx Cmd list. */
-               save_flags(flags);
-               cli();
-               entry = sp->cur_tx++ % TX_RING_SIZE;
-
-               if (speedo_debug > 5)
-                       printk(" CmdMCSetup frame length %d in entry %d.\n",
-                                  dev->mc_count, entry);
-
-               /* Change the command to a NoOp, pointing to the CmdMulti command. */
-               sp->tx_skbuff[entry] = 0;
-               sp->tx_ring[entry].status = CmdNOp << 16;
-               sp->tx_ring[entry].link = virt_to_bus(mc_setup_frm);
-
-               /* Set the link in the setup frame. */
-               mc_setup_frm->link =
-                 virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
-
-               sp->last_cmd->command &= ~CmdSuspend;
-               /* Immediately trigger the command unit resume. */
-               wait_for_cmd_done(ioaddr + SCBCmd);
-               outw(CU_RESUME, ioaddr + SCBCmd);
-               sp->last_cmd = mc_setup_frm;
-               restore_flags(flags);
-               if (speedo_debug > 1)
-                       printk(KERN_DEBUG "%s: Last command at %p is %4.4x.\n",
-                                  dev->name, sp->last_cmd, sp->last_cmd->command);
+               memset(mc_filter, 0, sizeof(mc_filter));
+               for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                        i++, mclist = mclist->next)
+                       set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+                                       mc_filter);
        }
 
-       sp->rx_mode = new_rx_mode;
+       save_flags(flags);
+       cli();
+       if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {
+               int saved_bank = inb(ioaddr + CONFIG_1);
+               /* Switch to bank 1 and set the multicast table. */
+               outb(0xe4, ioaddr + CONFIG_1);
+               for (i = 0; i < 8; i++)
+                       outb(mc_filter[i], ioaddr + 8 + i);
+               memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
+               outb(saved_bank, ioaddr + CONFIG_1);
+       }
+       restore_flags(flags);
+       return;
 }
-\f
-#ifdef MODULE
 
-int
-init_module(void)
-{
-       int cards_found;
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_at1700 = {
+       devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+       0, 0, 0, 0,
+       0, 0,
+       0, 0, 0, NULL, at1700_probe };
 
-       if (debug >= 0)
-               speedo_debug = debug;
-       if (speedo_debug)
-               printk(KERN_INFO "%s", version);
+static int io = 0x260;
+static int irq = 0;
 
-       root_speedo_dev = NULL;
-       cards_found = eepro100_init(NULL);
-       return cards_found ? 0 : -ENODEV;
+int init_module(void)
+{
+       if (io == 0)
+               printk("at1700: You should not use auto-probing with insmod!\n");
+       dev_at1700.base_addr = io;
+       dev_at1700.irq       = irq;
+       if (register_netdev(&dev_at1700) != 0) {
+               printk("at1700: register_netdev() returned non-zero.\n");
+               return -EIO;
+       }
+       return 0;
 }
 
 void
 cleanup_module(void)
 {
-       struct device *next_dev;
-
-       /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-       while (root_speedo_dev) {
-               next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module;
-               unregister_netdev(root_speedo_dev);
-               release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE);
-               kfree(root_speedo_dev);
-               root_speedo_dev = next_dev;
-       }
-}
-#else   /* not MODULE */
-int eepro100_probe(struct device *dev)
-{
-       int cards_found = 0;
-
-       cards_found = eepro100_init(dev);
-
-       if (speedo_debug > 0  &&  cards_found)
-               printk(version);
-
-       return cards_found ? 0 : -ENODEV;
+       unregister_netdev(&dev_at1700);
+       kfree(dev_at1700.priv);
+       dev_at1700.priv = NULL;
+
+       /* If we don't do this, we can't re-insmod it later. */
+       free_irq(dev_at1700.irq, NULL);
+       irq2dev_map[dev_at1700.irq] = NULL;
+       release_region(dev_at1700.base_addr, AT1700_IO_EXTENT);
 }
-#endif  /* MODULE */
+#endif /* MODULE */
 \f
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  c-indent-level: 4
- *  c-basic-offset: 4
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c"
+ *  alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c"
  *  tab-width: 4
+ *  c-basic-offset: 4
+ *  c-indent-level: 4
  * End:
  */
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
new file mode 100644 (file)
index 0000000..7f19029
--- /dev/null
@@ -0,0 +1,2817 @@
+/********************************************************************
+ *
+ *  Linux ThunderLAN Driver
+ *
+ *  tlan.c
+ *  by James Banks, james.banks@caldera.com
+ *
+ *  (C) 1997-1998 Caldera, Inc.
+ *
+ *  This software may be used and distributed according to the terms
+ *  of the GNU Public License, incorporated herein by reference.
+ *
+ ** This file is best viewed/edited with columns>=132.
+ *
+ ** Useful (if not required) reading:
+ *
+ *             Texas Instruments, ThunderLAN Programmer's Guide,
+ *                     TI Literature Number SPWU013A
+ *                     available in PDF format from www.ti.com
+ *             Level One, LXT901 and LXT970 Data Sheets
+ *                     available in PDF format from www.level1.com
+ *             National Semiconductor, DP83840A Data Sheet
+ *                     available in PDF format from www.national.com
+ *             Microchip Technology, 24C01A/02A/04A Data Sheet
+ *                     available in PDF format from www.microchip.com
+ *
+ ********************************************************************/
+
+
+#include <linux/module.h>
+
+#include "tlan.h"
+
+#include <linux/bios32.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+
+
+typedef u32 (TLanIntVectorFunc)( struct device *, u16 );
+
+
+#ifdef MODULE
+
+static struct device   *TLanDevices = NULL;
+static int             TLanDevicesInstalled = 0;
+
+#endif
+
+
+static  int            debug = 0;
+static int             aui = 0;
+static int             sa_int = 0;
+static int             bbuf = 0;
+static int             duplex = 0;
+static int             speed = 0;
+static u8              *TLanPadBuffer;
+static char            TLanSignature[] = "TLAN";
+static int             TLanVersionMajor = 0;
+static int             TLanVersionMinor = 42;
+
+
+static TLanAdapterEntry TLanAdapterList[] = {
+       { PCI_VENDOR_ID_COMPAQ, 
+         PCI_DEVICE_ID_NETELLIGENT_10,
+         "Compaq Netelligent 10",
+         TLAN_ADAPTER_ACTIVITY_LED
+       },
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_NETELLIGENT_10_100,
+         "Compaq Netelligent 10/100",
+         TLAN_ADAPTER_ACTIVITY_LED
+       }, 
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED,
+         "Compaq Integrated NetFlex-3/P",
+         TLAN_ADAPTER_NONE
+       }, 
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_NETFLEX_3P,
+         "Compaq NetFlex-3/P",
+         TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY
+       },
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_NETFLEX_3P_BNC,
+         "Compaq NetFlex-3/P",
+         TLAN_ADAPTER_NONE
+       },
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT,
+         "Compaq ProLiant Netelligent 10/100",
+         TLAN_ADAPTER_NONE
+       },
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL,
+         "Compaq Dual Port Netelligent 10/100",
+         TLAN_ADAPTER_NONE
+       },
+       { PCI_VENDOR_ID_COMPAQ,
+         PCI_DEVICE_ID_DESKPRO_4000_5233MMX,
+         "Compaq Integrated Netelligent 10/100",
+         TLAN_ADAPTER_NONE
+       },
+       { PCI_VENDOR_ID_OLICOM,
+         PCI_DEVICE_ID_OC_2326,
+         "Olicom OC-2326",
+         TLAN_ADAPTER_USE_INTERN_10
+       },
+       { 0,
+         0,
+         NULL,
+         0
+       } /* End of List */
+};
+
+
+static int     TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * );
+static int     TLan_Init( struct device * );
+static int     TLan_Open(struct device *dev);
+static int     TLan_StartTx(struct sk_buff *, struct device *);
+static void    TLan_HandleInterrupt(int, void *, struct pt_regs *);
+static int     TLan_Close(struct device *);
+static struct  net_device_stats *TLan_GetStats( struct device * );
+static void    TLan_SetMulticastList( struct device * );
+
+static u32     TLan_HandleInvalid( struct device *, u16 );
+static u32     TLan_HandleTxEOF( struct device *, u16 );
+static u32     TLan_HandleStatOverflow( struct device *, u16 );
+static u32     TLan_HandleRxEOF( struct device *, u16 );
+static u32     TLan_HandleDummy( struct device *, u16 );
+static u32     TLan_HandleTxEOC( struct device *, u16 );
+static u32     TLan_HandleStatusCheck( struct device *, u16 );
+static u32     TLan_HandleRxEOC( struct device *, u16 );
+
+static void    TLan_Timer( unsigned long );
+
+static void    TLan_ResetLists( struct device * );
+static void    TLan_FreeLists( struct device * );
+static void    TLan_PrintDio( u16 );
+static void    TLan_PrintList( TLanList *, char *, int );
+static void    TLan_ReadAndClearStats( struct device *, int );
+static void    TLan_ResetAdapter( struct device * );
+static void    TLan_FinishReset( struct device * );
+static void    TLan_SetMac( struct device *, int areg, char *mac );
+
+static void    TLan_PhyPrint( struct device * );
+static void    TLan_PhyDetect( struct device * );
+static void    TLan_PhyPowerDown( struct device * );
+static void    TLan_PhyPowerUp( struct device * );
+static void    TLan_PhyReset( struct device * );
+static void    TLan_PhyStartLink( struct device * );
+static void    TLan_PhyFinishAutoNeg( struct device * );
+/*
+static int     TLan_PhyNop( struct device * );
+static int     TLan_PhyInternalCheck( struct device * );
+static int     TLan_PhyInternalService( struct device * );
+static int     TLan_PhyDp83840aCheck( struct device * );
+*/
+
+static int     TLan_MiiReadReg( struct device *, u16, u16, u16 * );
+static void    TLan_MiiSendData( u16, u32, unsigned );
+static void    TLan_MiiSync( u16 );
+static void    TLan_MiiWriteReg( struct device *, u16, u16, u16 );
+
+static void    TLan_EeSendStart( u16 );
+static int     TLan_EeSendByte( u16, u8, int );
+static void    TLan_EeReceiveByte( u16, u8 *, int );
+static int     TLan_EeReadByte( struct device *, u8, u8 * );
+
+
+static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
+       TLan_HandleInvalid,
+       TLan_HandleTxEOF,
+       TLan_HandleStatOverflow,
+       TLan_HandleRxEOF,
+       TLan_HandleDummy,
+       TLan_HandleTxEOC,
+       TLan_HandleStatusCheck,
+       TLan_HandleRxEOC
+};
+
+static inline void
+TLan_SetTimer( struct device *dev, u32 ticks, u32 type )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+       cli();
+       if ( priv->timer.function != NULL ) {
+               return;
+       }
+       priv->timer.function = &TLan_Timer;
+       sti();
+
+       priv->timer.data = (unsigned long) dev;
+       priv->timer.expires = jiffies + ticks;
+       priv->timerSetAt = jiffies;
+       priv->timerType = type;
+       add_timer( &priv->timer );
+
+} /* TLan_SetTimer */
+
+
+/*****************************************************************************
+******************************************************************************
+
+       ThunderLAN Driver Primary Functions
+
+       These functions are more or less common to all Linux network drivers.
+
+******************************************************************************
+*****************************************************************************/
+
+
+#ifdef MODULE
+
+       /***************************************************************
+        *      init_module
+        *
+        *      Returns:
+        *              0 if module installed ok, non-zero if not.
+        *      Parms:
+        *              None
+        *
+        *      This function begins the setup of the driver creating a
+        *      pad buffer, finding all TLAN devices (matching
+        *      TLanAdapterList entries), and creating and initializing a
+        *      device structure for each adapter.
+        *
+        **************************************************************/
+
+extern int init_module(void)
+{
+       TLanPrivateInfo *priv;
+       u8              bus;
+       struct device   *dev;
+       size_t          dev_size;
+       u8              dfn;
+       u32             index;
+       int             failed;
+       int             found;
+       u32             io_base;
+       u8              irq;
+       u8              rev;
+
+       printk( "TLAN driver, v%d.%d, (C) 1997-8 Caldera, Inc.\n",
+               TLanVersionMajor,
+               TLanVersionMinor
+             );
+       TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE,
+                                       ( GFP_KERNEL | GFP_DMA )
+                                     );
+       if ( TLanPadBuffer == NULL ) {
+               printk( "TLAN:  Could not allocate memory for pad buffer.\n" );
+               return -ENOMEM;
+       }
+
+       memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+
+       dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
+
+       while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ) ) ) {
+               dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
+               if ( dev == NULL ) {
+                       printk( "TLAN:  Could not allocate memory for device.\n" );
+                       continue;
+               }
+               memset( dev, 0, dev_size );
+
+               dev->priv = priv = ( (void *) dev ) + sizeof(struct device);
+               dev->name = priv->devName;
+               strcpy( priv->devName, "    " );
+               dev->base_addr = io_base;
+               dev->irq = irq;
+               dev->init = TLan_Init;
+
+               priv->adapter =    &TLanAdapterList[index];
+               priv->adapterRev = rev;
+               priv->aui =        aui;
+               priv->duplex =     duplex;
+               priv->speed =      speed;
+               priv->sa_int =     sa_int;
+               priv->debug =      debug;
+
+               ether_setup( dev );
+
+               failed = register_netdev( dev );
+
+               if ( failed ) {
+                       printk( "TLAN:  Could not register device.\n" );
+                       kfree( dev );
+               } else {
+                       priv->nextDevice = TLanDevices;
+                       TLanDevices = dev;
+                       TLanDevicesInstalled++;
+                       printk("TLAN:  %s irq=%2d io=%04x, %s, Rev. %d\n",
+                               dev->name,
+                               (int) dev->irq,
+                               (int) dev->base_addr,
+                               priv->adapter->deviceLabel,
+                               priv->adapterRev );
+               }
+       }
+       
+       /* printk( "TLAN:  Found %d device(s).\n", TLanDevicesInstalled ); */
+
+    return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
+
+} /* init_module */
+
+
+
+
+       /***************************************************************
+        *      cleanup_module
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              None
+        *
+        *      Goes through the TLanDevices list and frees the device
+        *      structs and memory associated with each device (lists
+        *      and buffers).  It also ureserves the IO port regions
+        *      associated with this device.
+        *
+        **************************************************************/
+
+extern void cleanup_module(void)
+{
+       struct device   *dev;
+       TLanPrivateInfo *priv;
+
+       while ( TLanDevicesInstalled ) {
+               dev = TLanDevices;
+               priv = (TLanPrivateInfo *) dev->priv;
+               if ( priv->dmaStorage ) {
+                       kfree( priv->dmaStorage );
+               }
+               release_region( dev->base_addr, 0x10 );
+               unregister_netdev( dev );
+               TLanDevices = priv->nextDevice;
+               kfree( dev );
+               TLanDevicesInstalled--;
+       }
+       kfree( TLanPadBuffer );
+
+} /* cleanup_module */
+
+
+#else /* MODULE */
+
+
+
+
+       /***************************************************************
+        *      tlan_probe
+        *
+        *      Returns:
+        *              0 on success, error code on error
+        *      Parms:
+        *              dev     device struct to use if adapter is
+        *                      found.
+        *
+        *      The name is lower case to fit in with all the rest of
+        *      the netcard_probe names.  This function looks for a/
+        *      another TLan based adapter, setting it up with the
+        *      provided device struct if one is found.
+        *
+        **************************************************************/
+        
+extern int tlan_probe( struct device *dev )
+{
+       TLanPrivateInfo *priv;
+       static int      pad_allocated = 0;
+       int             found;
+       u8              bus, dfn, irq, rev;
+       u32             io_base, index;
+
+       found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index );
+
+       if ( ! found ) {
+               return -ENODEV;
+       }
+
+       dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
+       
+       if ( dev->priv == NULL ) {
+               printk( "TLAN:  Could not allocate memory for device.\n" );
+               return  -ENOMEM;
+       }
+
+       memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
+
+       if ( ! pad_allocated ) {
+               TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, 
+//                                             ( GFP_KERNEL | GFP_DMA )
+                                               ( GFP_KERNEL )
+                                             );
+               if ( TLanPadBuffer == NULL ) {
+                       printk( "TLAN:  Could not allocate memory for padding.\n" );
+                       kfree( dev->priv );
+                       return -ENOMEM;
+               } else {
+                       pad_allocated = 1;
+                       memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+               }
+       }
+
+       priv = (TLanPrivateInfo *) dev->priv;
+
+       dev->name = priv->devName;
+       strcpy( priv->devName, "    " );
+
+       dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
+
+       dev->base_addr = io_base;
+       dev->irq = irq;
+
+
+       priv->adapter =    &TLanAdapterList[index];
+       priv->adapterRev = rev;
+       priv->aui =        dev->mem_start & 0x01;
+       priv->duplex =     ( ( dev->mem_start & 0x0C ) == 0x0C ) ? 0 : dev->mem_start & 0x0C >> 2;
+       priv->speed =      ( ( dev->mem_start & 0x30 ) == 0x30 ) ? 0 : dev->mem_start & 0x30 >> 4;
+       priv->sa_int =     dev->mem_start & 0x02;
+       priv->debug =      dev->mem_end;
+
+
+       printk("TLAN %d.%d:  %s irq=%2d io=%04x, %s, Rev. %d\n",
+               TLanVersionMajor,
+               TLanVersionMinor,
+               dev->name, 
+               (int) irq, 
+               io_base,
+               priv->adapter->deviceLabel,
+               priv->adapterRev );
+
+       TLan_Init( dev );
+                       
+       return 0;
+
+} /* tlan_probe */
+
+
+#endif /* MODULE */
+
+
+
+
+       /***************************************************************
+        *      TLan_PciProbe
+        *
+        *      Returns:
+        *              1 if another TLAN card was found, 0 if not.
+        *      Parms:
+        *              pci_bus         The PCI bus the card was found
+        *                              on.
+        *              pci_dfn         The PCI whatever the card was
+        *                              found at.
+        *              pci_irq         The IRQ of the found adapter.
+        *              pci_rev         The revision of the adapter.
+        *              pci_io_base     The first IO port used by the
+        *                              adapter.
+        *              dl_ix           The index in the device list
+        *                              of the adapter.
+        *
+        *      This function searches for an adapter with PCI vendor
+        *      and device IDs matching those in the TLanAdapterList.
+        *      The function 'remembers' the last device it found,
+        *      and so finds a new device (if anymore are to be found)
+        *      each time the function is called.  It then looks up
+        *      pertinent PCI info and returns it to the caller.
+        *
+        **************************************************************/
+
+int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix )
+{
+       static int dl_index = 0;
+       static int pci_index = 0;
+
+       int     not_found;
+       u8      pci_latency;
+       u16     pci_command;
+       int     reg;
+
+
+       if ( ! pcibios_present() ) {
+               printk( "TLAN:   PCI Bios not present.\n" );
+               return 0;
+       }
+
+       for (; TLanAdapterList[dl_index].vendorId != 0; dl_index++) {
+
+               not_found = pcibios_find_device(
+                       TLanAdapterList[dl_index].vendorId,
+                       TLanAdapterList[dl_index].deviceId,
+                       pci_index,
+                       pci_bus,
+                       pci_dfn
+               );
+
+               if ( ! not_found ) {
+
+                       TLAN_DBG(
+                               TLAN_DEBUG_GNRL,
+                               "TLAN:  found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
+                               TLanAdapterList[dl_index].vendorId,
+                               TLanAdapterList[dl_index].deviceId
+                       );
+
+                       pcibios_read_config_byte ( *pci_bus,  *pci_dfn, PCI_REVISION_ID, pci_rev);
+                       pcibios_read_config_byte ( *pci_bus,  *pci_dfn, PCI_INTERRUPT_LINE, pci_irq);
+                       pcibios_read_config_word ( *pci_bus,  *pci_dfn, PCI_COMMAND, &pci_command);
+                       pcibios_read_config_dword( *pci_bus,  *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base);
+                       pcibios_read_config_byte ( *pci_bus,  *pci_dfn, PCI_LATENCY_TIMER, &pci_latency);
+
+                       if (pci_latency < 0x10) {
+                               pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff);
+                               TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:    Setting latency timer to max.\n");
+                       }
+
+                       for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg +=4 ) {
+                               pcibios_read_config_dword( *pci_bus, *pci_dfn, reg, pci_io_base);
+                               if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) {
+                                       *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK;
+                                       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:    IO mapping is available at %x.\n", *pci_io_base);
+                                       break;
+                               } else {
+                                       *pci_io_base = 0;
+                               }
+                       }
+
+                       if ( *pci_io_base == 0 )
+                               printk("TLAN:    IO mapping not available, ignoring device.\n");
+
+                       if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) {
+                               pcibios_write_config_word ( *pci_bus,  *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER );
+                               printk( "TLAN:  Attempting to activate busmastering.\n" );
+                               printk( "TLAN:    You may need to set busmastering to on in the CMOS\n" );
+                               printk( "TLAN:    before this card will work.\n" );
+                               *pci_io_base = 0;
+                       }
+
+                       pci_index++;
+
+                       if ( *pci_io_base ) {
+                               *dl_ix = dl_index;
+                               return 1;
+                       }
+
+               } else {
+                       pci_index = 0;
+               }
+       }
+
+       return 0;
+
+} /* TLan_PciProbe */
+
+
+
+
+       /***************************************************************
+        *      TLan_Init
+        *
+        *      Returns:
+        *              0 on success, error code otherwise.
+        *      Parms:
+        *              dev     The structure of the device to be
+        *                      init'ed.
+        *
+        *      This function completes the initialization of the
+        *      device structure and driver.  It reserves the IO
+        *      addresses, allocates memory for the lists and bounce
+        *      buffers, retrieves the MAC address from the eeprom
+        *      and assignes the device's methods.
+        *      
+        **************************************************************/
+
+int TLan_Init( struct device *dev )
+{
+       int             dma_size;
+       int             err;
+       int             i;
+       TLanPrivateInfo *priv;
+
+       priv = (TLanPrivateInfo *) dev->priv;
+
+       err = check_region( dev->base_addr, 0x10 );
+       if ( err ) {
+               printk( "TLAN:  %s: Io port region 0x%lx size 0x%x in use.\n",
+                       dev->name,
+                       dev->base_addr,
+                       0x10 );
+               return -EIO;
+       }
+       request_region( dev->base_addr, 0x10, TLanSignature );
+
+       if ( bbuf ) {
+               dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+                  * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
+       } else {
+               dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+                  * ( sizeof(TLanList) );
+       }
+
+       priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
+       if ( priv->dmaStorage == NULL ) {
+               printk( "TLAN:  Could not allocate lists and buffers for %s.\n",
+                       dev->name );
+               return -ENOMEM;
+       }
+       memset( priv->dmaStorage, 0, dma_size );
+       priv->rxList = (TLanList *) 
+                      ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
+       priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
+
+       if ( bbuf ) {
+               priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+               priv->txBuffer = priv->rxBuffer
+                                + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+       }
+
+       err = 0;
+       for ( i = 0;  i < 6 ; i++ )
+               err |= TLan_EeReadByte( dev,
+                                       (u8) 0x83 + i,
+                                       (u8 *) &dev->dev_addr[i] );
+       if ( err ) {
+               printk( "TLAN:  %s: Error reading MAC from eeprom: %d\n",
+                       dev->name,
+                       err );
+       }
+
+       dev->addr_len = 6;
+
+       dev->open = &TLan_Open;
+       dev->hard_start_xmit = &TLan_StartTx;
+       dev->stop = &TLan_Close;
+       dev->get_stats = &TLan_GetStats;
+       dev->set_multicast_list = &TLan_SetMulticastList;
+
+
+       return 0;
+
+} /* TLan_Init */
+
+
+
+
+       /***************************************************************
+        *      TLan_Open
+        *
+        *      Returns:
+        *              0 on success, error code otherwise.
+        *      Parms:
+        *              dev     Structure of device to be opened.
+        *
+        *      This routine puts the driver and TLAN adapter in a
+        *      state where it is ready to send and receive packets.
+        *      It allocates the IRQ, resets and brings the adapter
+        *      out of reset, and allows interrupts.  It also delays
+        *      the startup for autonegotiation or sends a Rx GO
+        *      command to the adapter, as appropriate.
+        *
+        **************************************************************/
+
+int TLan_Open( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       int             err;
+
+       priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
+       if ( priv->sa_int ) {
+               TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:   Using SA_INTERRUPT\n" ); 
+               err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev );
+       } else {
+               err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+       }
+       if ( err ) {
+               printk( "TLAN:  Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
+               return -EAGAIN;
+       }
+       
+       MOD_INC_USE_COUNT;
+
+       dev->tbusy = 0;
+       dev->interrupt = 0;
+       dev->start = 1;
+
+       /* NOTE: It might not be necessary to read the stats before a
+                        reset if you don't care what the values are.
+       */
+       TLan_ResetLists( dev );
+       TLan_ReadAndClearStats( dev, TLAN_IGNORE );
+       TLan_ResetAdapter( dev );
+
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Opened.  TLAN Chip Rev: %x\n", dev->name, priv->tlanRev );
+
+       return 0;
+
+} /* TLan_Open */
+
+
+
+
+       /***************************************************************
+        *      TLan_StartTx
+        *  
+        *      Returns:
+        *              0 on success, non-zero on failure.
+        *      Parms:
+        *              skb     A pointer to the sk_buff containing the
+        *                      frame to be sent.
+        *              dev     The device to send the data on.
+        *
+        *      This function adds a frame to the Tx list to be sent
+        *      ASAP.  First it verifies that the adapter is ready and
+        *      there is room in the queue.  Then it sets up the next
+        *      available list, copies the frame to the corresponding
+        *      buffer.  If the adapter Tx channel is idle, it gives
+        *      the adapter a Tx Go command on the list, otherwise it
+        *      sets the forward address of the previous list to point
+        *      to this one.  Then it frees the sk_buff.
+        *
+        **************************************************************/
+
+int TLan_StartTx( struct sk_buff *skb, struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       TLanList        *tail_list;
+       u8              *tail_buffer;
+       int             pad;
+
+       if ( ! priv->phyOnline ) {
+               TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s PHY is not ready\n", dev->name );
+               dev_kfree_skb( skb, FREE_WRITE );
+               return 0;
+       }
+
+       tail_list = priv->txList + priv->txTail;
+
+       if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
+               TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail );
+               dev->tbusy = 1;
+               priv->txBusyCount++;
+               return 1;
+       }
+
+       tail_list->forward = 0;
+
+       if ( bbuf ) {
+               tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+               memcpy( tail_buffer, skb->data, skb->len );
+       } else {
+               tail_list->buffer[0].address = virt_to_bus( skb->data );
+               tail_list->buffer[9].address = (u32) skb;
+       }
+
+       pad = TLAN_MIN_FRAME_SIZE - skb->len;
+
+       if ( pad > 0 ) {
+               tail_list->frameSize = (u16) skb->len + pad;
+               tail_list->buffer[0].count = (u32) skb->len;
+               tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad;
+               tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer );
+       } else {
+               tail_list->frameSize = (u16) skb->len;
+               tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len;
+               tail_list->buffer[1].count = 0;
+               tail_list->buffer[1].address = 0;
+       }
+
+       cli();
+       tail_list->cStat = TLAN_CSTAT_READY;
+       if ( ! priv->txInProgress ) {
+               priv->txInProgress = 1;
+               outw( 0x4, dev->base_addr + TLAN_HOST_INT );
+               TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Starting TX on buffer %d\n", priv->txTail );
+               outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM );
+               outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
+       } else {
+               TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Adding buffer %d to TX channel\n", priv->txTail );
+               if ( priv->txTail == 0 ) {
+                       ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
+               } else {
+                       ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
+               }
+       }
+       sti();
+
+       CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS );
+
+       if ( bbuf ) {
+               dev_kfree_skb( skb, FREE_WRITE );
+       }
+               
+       dev->trans_start = jiffies;
+       return 0;
+
+} /* TLan_StartTx */
+
+
+
+
+       /***************************************************************
+        *      TLan_HandleInterrupt
+        *  
+        *      Returns:        
+        *              Nothing
+        *      Parms:
+        *              irq     The line on which the interrupt
+        *                      occurred.
+        *              dev_id  A pointer to the device assigned to
+        *                      this irq line.
+        *              regs    ???
+        *
+        *      This function handles an interrupt generated by its
+        *      assigned TLAN adapter.  The function deactivates
+        *      interrupts on its adapter, records the type of
+        *      interrupt, executes the appropriate subhandler, and
+        *      acknowdges the interrupt to the adapter (thus
+        *      re-enabling adapter interrupts.
+        *
+        **************************************************************/
+
+void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       u32             ack;
+       struct device   *dev;
+       u32             host_cmd;
+       u16             host_int;
+       int             type;
+
+       dev = (struct device *) dev_id;
+
+       cli();
+       if ( dev->interrupt ) {
+               printk( "TLAN:   Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
+       }
+       dev->interrupt++;
+
+       host_int = inw( dev->base_addr + TLAN_HOST_INT );
+       outw( host_int, dev->base_addr + TLAN_HOST_INT );
+
+       type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
+
+       ack = TLanIntVector[type]( dev, host_int );
+
+       if ( ack ) {
+               host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
+               outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
+       }
+
+       dev->interrupt--;
+       sti();
+
+} /* TLan_HandleInterrupts */
+
+
+
+
+       /***************************************************************
+        *      TLan_Close
+        *  
+        *      Returns:
+        *              An error code.
+        *      Parms:
+        *              dev     The device structure of the device to
+        *                      close.
+        *
+        *      This function shuts down the adapter.  It records any
+        *      stats, puts the adapter into reset state, deactivates
+        *      its time as needed, and frees the irq it is using.
+        *
+        **************************************************************/
+
+int TLan_Close(struct device *dev)
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+       dev->start = 0;
+       dev->tbusy = 1;
+
+       TLan_ReadAndClearStats( dev, TLAN_RECORD );
+       outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+       if ( priv->timer.function != NULL )
+               del_timer( &priv->timer );
+       free_irq( dev->irq, dev );
+       TLan_FreeLists( dev );
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  Device %s closed.\n", dev->name );
+
+       MOD_DEC_USE_COUNT;
+
+       return 0;
+
+} /* TLan_Close */
+
+
+
+
+       /***************************************************************
+        *      TLan_GetStats
+        *  
+        *      Returns:
+        *              A pointer to the device's statistics structure.
+        *      Parms:
+        *              dev     The device structure to return the
+        *                      stats for.
+        *
+        *      This function updates the devices statistics by reading
+        *      the TLAN chip's onboard registers.  Then it returns the
+        *      address of the statistics structure.
+        *
+        **************************************************************/
+
+struct net_device_stats *TLan_GetStats( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       int i;
+
+       /* Should only read stats if open ? */
+       TLan_ReadAndClearStats( dev, TLAN_RECORD );
+
+       TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  %s EOC count = %d\n", dev->name, priv->rxEocCount );
+       TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s Busy count = %d\n", dev->name, priv->txBusyCount );
+       if ( debug & TLAN_DEBUG_GNRL ) {
+               TLan_PrintDio( dev->base_addr );
+               TLan_PhyPrint( dev );
+       }
+       if ( debug & TLAN_DEBUG_LIST ) {
+               for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ )
+                       TLan_PrintList( priv->rxList + i, "RX", i );
+               for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ )
+                       TLan_PrintList( priv->txList + i, "TX", i );
+       }
+       
+       return ( &( (TLanPrivateInfo *) dev->priv )->stats );
+
+} /* TLan_GetStats */
+
+
+
+
+       /***************************************************************
+        *      TLan_SetMulticastList
+        *  
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              dev     The device structure to set the
+        *                      multicast list for.
+        *
+        *      This function sets the TLAN adaptor to various receive
+        *      modes.  If the IFF_PROMISC flag is set, promiscuous
+        *      mode is acitviated.  Otherwise, promiscuous mode is
+        *      turned off.  If the IFF_ALLMULTI flag is set, then
+        *      the hash table is set to receive all group addresses.
+        *      Otherwise, the first three multicast addresses are
+        *      stored in AREG_1-3, and the rest are selected via the
+        *      hash table, as necessary.
+        *
+        **************************************************************/
+
+void TLan_SetMulticastList( struct device *dev )
+{      
+       struct dev_mc_list      *dmi = dev->mc_list;
+       u32                     hash1 = 0;
+       u32                     hash2 = 0;
+       int                     i;
+       u32                     offset;
+       u8                      tmp;
+
+       if ( dev->flags & IFF_PROMISC ) {
+               tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+               TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF );
+       } else {
+               tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+               TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF );
+               if ( dev->flags & IFF_ALLMULTI ) {
+                       for ( i = 0; i < 3; i++ ) 
+                               TLan_SetMac( dev, i + 1, NULL );
+                       TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF );
+                       TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF );
+               } else {
+                       for ( i = 0; i < dev->mc_count; i++ ) {
+                               if ( i < 3 ) {
+                                       TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr );
+                               } else {
+                                       offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr );
+                                       if ( offset < 32 ) 
+                                               hash1 |= ( 1 << offset );
+                                       else
+                                               hash2 |= ( 1 << ( offset - 32 ) );
+                               }
+                               dmi = dmi->next;
+                       }
+                       for ( ; i < 3; i++ ) 
+                               TLan_SetMac( dev, i + 1, NULL );
+                       TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 );
+                       TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 );
+               }
+       }
+
+} /* TLan_SetMulticastList */
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+        ThunderLAN Driver Interrupt Vectors and Table
+
+       Please see Chap. 4, "Interrupt Handling" of the "ThunderLAN
+       Programmer's Guide" for more informations on handling interrupts
+       generated by TLAN based adapters.  
+
+******************************************************************************
+*****************************************************************************/
+
+
+       /***************************************************************
+        *      TLan_HandleInvalid
+        *
+        *      Returns:
+        *              0
+        *      Parms:
+        *              dev             Device assigned the IRQ that was
+        *                              raised.
+        *              host_int        The contents of the HOST_INT
+        *                              port.
+        *
+        *      This function handles invalid interrupts.  This should
+        *      never happen unless some other adapter is trying to use
+        *      the IRQ line assigned to the device.
+        *
+        **************************************************************/
+
+u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
+{
+       host_int = 0;
+       /* printk( "TLAN:  Invalid interrupt on %s.\n", dev->name ); */
+       return 0;
+
+} /* TLan_HandleInvalid */
+
+
+
+
+       /***************************************************************
+        *      TLan_HandleTxEOF
+        *
+        *      Returns:
+        *              1
+        *      Parms:
+        *              dev             Device assigned the IRQ that was
+        *                              raised.
+        *              host_int        The contents of the HOST_INT
+        *                              port.
+        *
+        *      This function handles Tx EOF interrupts which are raised
+        *      by the adapter when it has completed sending the
+        *      contents of a buffer.  If detemines which list/buffer
+        *      was completed and resets it.  If the buffer was the last
+        *      in the channel (EOC), then the function checks to see if
+        *      another buffer is ready to send, and if so, sends a Tx
+        *      Go command.  Finally, the driver activates/continues the
+        *      activity LED.
+        *
+        **************************************************************/
+
+u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       int             eoc = 0;
+       TLanList        *head_list;
+       u32             ack = 1;
+
+       TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
+       host_int = 0;
+       head_list = priv->txList + priv->txHead;
+
+       if ( ! bbuf ) {
+               dev_kfree_skb( (struct sk_buff *) head_list->buffer[9].address, FREE_WRITE );
+               head_list->buffer[9].address = 0;
+       }
+
+       if ( head_list->cStat & TLAN_CSTAT_EOC )
+               eoc = 1;
+       if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+               printk( "TLAN:  Received interrupt for uncompleted TX frame.\n" );
+       }
+
+#if LINUX_KERNEL_VERSION > 0x20100
+       priv->stats->tx_bytes += head_list->frameSize;
+#endif
+
+       head_list->cStat = TLAN_CSTAT_UNUSED;
+       dev->tbusy = 0;
+       CIRC_INC( priv->txHead, TLAN_NUM_TX_LISTS );
+       if ( eoc ) {
+               TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
+               head_list = priv->txList + priv->txHead;
+               if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+                       outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+                       ack |= TLAN_HC_GO;
+               } else {
+                       priv->txInProgress = 0;
+               }
+       }
+
+       if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
+               TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+               if ( priv->timer.function == NULL ) {
+                       TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+               } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
+                       priv->timerSetAt = jiffies;
+               }
+       }
+
+       return ack;
+
+} /* TLan_HandleTxEOF */
+
+
+
+
+       /***************************************************************
+        *      TLan_HandleStatOverflow
+        *
+        *      Returns:
+        *              1
+        *      Parms:
+        *              dev             Device assigned the IRQ that was
+        *                              raised.
+        *              host_int        The contents of the HOST_INT
+        *                              port.
+        *
+        *      This function handles the Statistics Overflow interrupt
+        *      which means that one or more of the TLAN statistics
+        *      registers has reached 1/2 capacity and needs to be read.
+        *
+        **************************************************************/
+
+u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int )
+{
+       host_int = 0;
+       TLan_ReadAndClearStats( dev, TLAN_RECORD );
+
+       return 1;
+
+} /* TLan_HandleStatOverflow */
+
+
+
+
+       /***************************************************************
+        *      TLan_HandleRxEOF
+        *
+        *      Returns:
+        *              1
+        *      Parms:
+        *              dev             Device assigned the IRQ that was
+        *                              raised.
+        *              host_int        The contents of the HOST_INT
+        *                              port.
+        *
+        *      This function handles the Rx EOF interrupt which
+        *      indicates a frame has been received by the adapter from
+        *      the net and the frame has been transferred to memory.
+        *      The function determines the bounce buffer the frame has
+        *      been loaded into, creates a new sk_buff big enough to
+        *      hold the frame, and sends it to protocol stack.  It
+        *      then resets the used buffer and appends it to the end
+        *      of the list.  If the frame was the last in the Rx
+        *      channel (EOC), the function restarts the receive channel
+        *      by sending an Rx Go command to the adapter.  Then it
+        *      activates/continues the the activity LED.
+        *
+        **************************************************************/
+
+u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u32             ack = 1;
+       int             eoc = 0;
+       u8              *head_buffer;
+       TLanList        *head_list;
+       struct sk_buff  *skb;
+       TLanList        *tail_list;
+       void            *t;
+
+       TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  Handling RX EOF (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
+       host_int = 0;
+       head_list = priv->rxList + priv->rxHead;
+       tail_list = priv->rxList + priv->rxTail;
+
+       if ( head_list->cStat & TLAN_CSTAT_EOC ) {
+               eoc = 1;
+       }
+
+       if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+               printk( "TLAN:  Received interrupt for uncompleted RX frame.\n" );
+       } else if ( bbuf ) {
+               skb = dev_alloc_skb( head_list->frameSize + 7 );
+               if ( skb == NULL ) { 
+                       printk( "TLAN:  Couldn't allocate memory for received data.\n" );
+               } else {
+                       head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE );
+                       skb->dev = dev;
+                       skb_reserve( skb, 2 );
+                       t = (void *) skb_put( skb, head_list->frameSize );
+
+#if LINUX_KERNEL_VERSION > 0x20100
+                       priv->stats->rx_bytes += head_list->frameSize;
+#endif
+
+                       memcpy( t, head_buffer, head_list->frameSize );
+                       skb->protocol = eth_type_trans( skb, dev );
+                       netif_rx( skb );
+               }
+       } else {
+               skb = (struct sk_buff *) head_list->buffer[9].address;
+               head_list->buffer[9].address = 0;
+               skb_trim( skb, head_list->frameSize );
+
+#if LINUX_KERNEL_VERSION > 0x20100
+                       priv->stats->rx_bytes += head_list->frameSize;
+#endif
+
+               skb->protocol = eth_type_trans( skb, dev );
+               netif_rx( skb );
+
+               skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
+               if ( skb == NULL ) {
+                       printk( "TLAN:  Couldn't allocate memory for received data.\n" );
+                       /* If this ever happened it would be a problem */
+               } else {
+                       skb->dev = dev;
+                       skb_reserve( skb, 2 );
+                       t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
+                       head_list->buffer[0].address = virt_to_bus( t );
+                       head_list->buffer[9].address = (u32) skb;
+               }
+       }
+
+       head_list->forward = 0;
+       head_list->frameSize = TLAN_MAX_FRAME_SIZE;
+       head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+       tail_list->forward = virt_to_bus( head_list );
+
+       CIRC_INC( priv->rxHead, TLAN_NUM_RX_LISTS );
+       CIRC_INC( priv->rxTail, TLAN_NUM_RX_LISTS );
+
+       if ( eoc ) { 
+               TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
+               head_list = priv->rxList + priv->rxHead;
+               outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+               ack |= TLAN_HC_GO | TLAN_HC_RT;
+               priv->rxEocCount++;
+       }
+
+       if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
+               TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+               if ( priv->timer.function == NULL )  {
+                       TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+               } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
+                       priv->timerSetAt = jiffies;
+               }
+       }
+
+       dev->last_rx = jiffies;
+
+       return ack;
+
+} /* TLan_HandleRxEOF */
+
+
+
+
+       /***************************************************************
+        *      TLan_HandleDummy
+        *
+        *      Returns:
+        *              1
+        *      Parms:
+        *              dev             Device assigned the IRQ that was
+        *                              raised.
+        *              host_int        The contents of the HOST_INT
+        *                              port.
+        *
+        *      This function handles the Dummy interrupt, which is
+        *      raised whenever a test interrupt is generated by setting
+        *      the Req_Int bit of HOST_CMD to 1.
+        *
+        **************************************************************/
+
+u32 TLan_HandleDummy( struct device *dev, u16 host_int )
+{
+       host_int = 0;
+       printk( "TLAN:  Test interrupt on %s.\n", dev->name );
+       return 1;
+
+} /* TLan_HandleDummy */
+
+
+
+
+       /***************************************************************
+        *      TLan_HandleTxEOC
+        *
+        *      Returns:
+        *              1
+        *      Parms:
+        *              dev             Device assigned the IRQ that was
+        *                              raised.
+        *              host_int        The contents of the HOST_INT
+        *                              port.
+        *
+        *      This driver is structured to determine EOC occurances by
+        *      reading the CSTAT member of the list structure.  Tx EOC
+        *      interrupts are disabled via the DIO INTDIS register.
+        *      However, TLAN chips before revision 3.0 didn't have this
+        *      functionality, so process EOC events if this is the
+        *      case.
+        *
+        **************************************************************/
+
+u32 TLan_HandleTxEOC( struct device *dev, u16 host_int )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       TLanList                *head_list;
+       u32                             ack = 1;
+
+       host_int = 0;
+       if ( priv->tlanRev < 0x30 ) {
+               TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOC (Head=%d Tail=%d) -- IRQ\n", priv->txHead, priv->txTail );
+               head_list = priv->txList + priv->txHead;
+               if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+                       outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+                       ack |= TLAN_HC_GO;
+               } else {
+                       priv->txInProgress = 0;
+               }
+       }
+
+       return ack;
+
+} /* TLan_HandleTxEOC */
+
+
+
+
+       /***************************************************************
+        *      TLan_HandleStatusCheck
+        *
+        *      Returns:
+        *              0 if Adapter check, 1 if Network Status check.
+        *      Parms:
+        *              dev             Device assigned the IRQ that was
+        *                              raised.
+        *              host_int        The contents of the HOST_INT
+        *                              port.
+        *
+        *      This function handles Adapter Check/Network Status
+        *      interrupts generated by the adapter.  It checks the
+        *      vector in the HOST_INT register to determine if it is
+        *      an Adapter Check interrupt.  If so, it resets the
+        *      adapter.  Otherwise it clears the status registers
+        *      and services the PHY.
+        *
+        **************************************************************/
+
+u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
+{      
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u32             ack;
+       u32             error;
+       u8              net_sts;
+       u32             phy;
+       u16             tlphy_ctl;
+       u16             tlphy_sts;
+       
+       ack = 1;
+       if ( host_int & TLAN_HI_IV_MASK ) {
+               error = inl( dev->base_addr + TLAN_CH_PARM );
+               printk( "TLAN:  %s: Adaptor Error = 0x%x\n", dev->name, error );
+               TLan_ReadAndClearStats( dev, TLAN_RECORD );
+               outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+               TLan_FreeLists( dev );
+               TLan_ResetLists( dev );
+               TLan_ResetAdapter( dev );
+               dev->tbusy = 0;
+               ack = 0;
+       } else {
+               TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Status Check\n", dev->name );
+               phy = priv->phy[priv->phyNum];
+
+               net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
+               if ( net_sts ) {
+                       TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
+                       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s:    Net_Sts = %x\n", dev->name, (unsigned) net_sts );
+               }
+               if ( ( net_sts & TLAN_NET_STS_MIRQ ) &&  ( priv->phyNum == 0 ) ) {
+                       TLan_MiiReadReg( dev, phy, TLAN_TLPHY_STS, &tlphy_sts );
+                       TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
+                       if ( ! ( tlphy_sts & TLAN_TS_POLOK ) && ! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+                               tlphy_ctl |= TLAN_TC_SWAPOL;
+                               TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+                       } else if ( ( tlphy_sts & TLAN_TS_POLOK ) && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+                               tlphy_ctl &= ~TLAN_TC_SWAPOL;
+                               TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+                       }
+
+                       if (debug) {
+                               TLan_PhyPrint( dev );
+                       }
+               }
+               udelay(10000000);
+       }
+
+       return ack;
+
+} /* TLan_HandleStatusCheck */
+
+
+
+
+       /***************************************************************
+        *      TLan_HandleRxEOC
+        *
+        *      Returns:
+        *              1
+        *      Parms:
+        *              dev             Device assigned the IRQ that was
+        *                              raised.
+        *              host_int        The contents of the HOST_INT
+        *                              port.
+        *
+        *      This driver is structured to determine EOC occurances by
+        *      reading the CSTAT member of the list structure.  Rx EOC
+        *      interrupts are disabled via the DIO INTDIS register.
+        *      However, TLAN chips before revision 3.0 didn't have this
+        *      CSTAT member or a INTDIS register, so if this chip is
+        *      pre-3.0, process EOC interrupts normally.
+        *
+        **************************************************************/
+
+u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       TLanList        *head_list;
+       u32             ack = 1;
+
+       host_int = 0;
+       if (  priv->tlanRev < 0x30 ) {
+               TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  Handling RX EOC (Head=%d Tail=%d) -- IRQ\n", priv->rxHead, priv->rxTail );
+               head_list = priv->rxList + priv->rxHead;
+               outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+               ack |= TLAN_HC_GO | TLAN_HC_RT;
+               priv->rxEocCount++;
+       }
+
+       return ack;
+
+} /* TLan_HandleRxEOC */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+       ThunderLAN Driver Timer Function
+
+******************************************************************************
+*****************************************************************************/
+
+
+       /***************************************************************
+        *      TLan_Timer
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              data    A value given to add timer when
+        *                      add_timer was called.
+        *
+        *      This function handles timed functionality for the
+        *      TLAN driver.  The two current timer uses are for
+        *      delaying for autonegotionation and driving the ACT LED.
+        *      -       Autonegotiation requires being allowed about
+        *              2 1/2 seconds before attempting to transmit a
+        *              packet.  It would be a very bad thing to hang
+        *              the kernel this long, so the driver doesn't
+        *              allow transmission 'til after this time, for
+        *              certain PHYs.  It would be much nicer if all
+        *              PHYs were interrupt-capable like the internal
+        *              PHY.
+        *      -       The ACT LED, which shows adapter activity, is
+        *              driven by the driver, and so must be left on
+        *              for a short period to power up the LED so it
+        *              can be seen.  This delay can be changed by
+        *              changing the TLAN_TIMER_ACT_DELAY in tlan.h,
+        *              if desired.  10 jiffies produces a slightly
+        *              sluggish response.
+        *
+        **************************************************************/
+
+void TLan_Timer( unsigned long data )
+{
+       struct device   *dev = (struct device *) data;
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u32             elapsed;
+
+       priv->timer.function = NULL;
+
+       switch ( priv->timerType ) {
+               case TLAN_TIMER_PHY_PDOWN:
+                       TLan_PhyPowerDown( dev );
+                       break;
+               case TLAN_TIMER_PHY_PUP:
+                       TLan_PhyPowerUp( dev );
+                       break;
+               case TLAN_TIMER_PHY_RESET:
+                       TLan_PhyReset( dev );
+                       break;
+               case TLAN_TIMER_PHY_START_LINK:
+                       TLan_PhyStartLink( dev );
+                       break;
+               case TLAN_TIMER_PHY_FINISH_AN:
+                       TLan_PhyFinishAutoNeg( dev );
+                       break;
+               case TLAN_TIMER_FINISH_RESET:
+                       TLan_FinishReset( dev );
+                       break;
+               case TLAN_TIMER_ACTIVITY:
+                       cli();
+                       if ( priv->timer.function == NULL ) {
+                               elapsed = jiffies - priv->timerSetAt;
+                               if ( elapsed >= TLAN_TIMER_ACT_DELAY ) {
+                                       TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+                               } else  {
+                                       priv->timer.function = &TLan_Timer;
+                                       priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
+                                       sti();
+                                       add_timer( &priv->timer );
+                               }
+                       }
+                       sti();
+                       break;
+               default:
+                       break;
+       }
+
+} /* TLan_Timer */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+       ThunderLAN Driver Adapter Related Routines
+
+******************************************************************************
+*****************************************************************************/
+
+
+       /***************************************************************
+        *      TLan_ResetLists
+        *  
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              dev     The device structure with the list
+        *                      stuctures to be reset.
+        *
+        *      This routine sets the variables associated with managing
+        *      the TLAN lists to their initial values.
+        *
+        **************************************************************/
+
+void TLan_ResetLists( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       int             i;
+       TLanList        *list;
+       struct sk_buff  *skb;
+       void            *t = NULL;
+
+       priv->txHead = 0;
+       priv->txTail = 0;
+       for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+               list = priv->txList + i;
+               list->cStat = TLAN_CSTAT_UNUSED;
+               if ( bbuf ) {
+                       list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+               } else {
+                       list->buffer[0].address = 0;
+               }
+               list->buffer[2].count = 0;
+               list->buffer[2].address = 0;
+       }
+
+       priv->rxHead = 0;
+       priv->rxTail = TLAN_NUM_RX_LISTS - 1;
+       for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+               list = priv->rxList + i;
+               list->cStat = TLAN_CSTAT_READY;
+               list->frameSize = TLAN_MAX_FRAME_SIZE;
+               list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+               if ( bbuf ) {
+                       list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+               } else {
+                       skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
+                       if ( skb == NULL ) {
+                               printk( "TLAN:  Couldn't allocate memory for received data.\n" );
+                               /* If this ever happened it would be a problem */
+                       } else {
+                               skb->dev = dev;
+                               skb_reserve( skb, 2 );
+                               t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
+                       }
+                       list->buffer[0].address = virt_to_bus( t );
+                       list->buffer[9].address = (u32) skb;
+               }
+               list->buffer[1].count = 0;
+               list->buffer[1].address = 0;
+               if ( i < TLAN_NUM_RX_LISTS - 1 )
+                       list->forward = virt_to_bus( list + 1 );
+               else
+                       list->forward = 0;
+       }
+
+} /* TLan_ResetLists */
+
+
+void TLan_FreeLists( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       int             i;
+       TLanList        *list;
+       struct sk_buff  *skb;
+
+       if ( ! bbuf ) {
+               for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+                       list = priv->txList + i;
+                       skb = (struct sk_buff *) list->buffer[9].address;
+                       if ( skb ) {
+                               dev_kfree_skb( skb, FREE_WRITE );
+                               list->buffer[9].address = 0;
+                       }
+               }
+
+               for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+                       list = priv->rxList + i;
+                       skb = (struct sk_buff *) list->buffer[9].address;
+                       if ( skb ) {
+                               dev_kfree_skb( skb, FREE_READ );
+                               list->buffer[9].address = 0;
+                       }
+               }
+       }
+
+} /* TLan_FreeLists */
+
+
+
+
+       /***************************************************************
+        *      TLan_PrintDio
+        *  
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              io_base         Base IO port of the device of
+        *                              which to print DIO registers.
+        *
+        *      This function prints out all the the internal (DIO)
+        *      registers of a TLAN chip.
+        *
+        **************************************************************/
+
+void TLan_PrintDio( u16 io_base )
+{
+       u32 data0, data1;
+       int     i;
+
+       printk( "TLAN:   Contents of internal registers for io base 0x%04hx.\n", io_base );
+       printk( "TLAN:      Off.  +0         +4\n" );
+       for ( i = 0; i < 0x4C; i+= 8 ) {
+               data0 = TLan_DioRead32( io_base, i );
+               data1 = TLan_DioRead32( io_base, i + 0x4 );
+               printk( "TLAN:      0x%02x  0x%08x 0x%08x\n", i, data0, data1 );
+       }
+
+} /* TLan_PrintDio */
+
+
+
+
+       /***************************************************************
+        *      TLan_PrintList
+        *  
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              list    A pointer to the TLanList structure to
+        *                      be printed.
+        *              type    A string to designate type of list,
+        *                      "Rx" or "Tx".
+        *              num     The index of the list.
+        *
+        *      This function prints out the contents of the list
+        *      pointed to by the list parameter.
+        *
+        **************************************************************/
+
+void TLan_PrintList( TLanList *list, char *type, int num)
+{
+       int i;
+
+       printk( "TLAN:   %s List %d at 0x%08x\n", type, num, (u32) list );
+       printk( "TLAN:      Forward    = 0x%08x\n",  list->forward );
+       printk( "TLAN:      CSTAT      = 0x%04hx\n", list->cStat );
+       printk( "TLAN:      Frame Size = 0x%04hx\n", list->frameSize );
+       /* for ( i = 0; i < 10; i++ ) { */
+       for ( i = 0; i < 2; i++ ) {
+               printk( "TLAN:      Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
+       }
+
+} /* TLan_PrintList */
+
+
+
+
+       /***************************************************************
+        *      TLan_ReadAndClearStats
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              dev     Pointer to device structure of adapter
+        *                      to which to read stats.
+        *              record  Flag indicating whether to add 
+        *
+        *      This functions reads all the internal status registers
+        *      of the TLAN chip, which clears them as a side effect.
+        *      It then either adds the values to the device's status
+        *      struct, or discards them, depending on whether record
+        *      is TLAN_RECORD (!=0)  or TLAN_IGNORE (==0).
+        *
+        **************************************************************/
+
+void TLan_ReadAndClearStats( struct device *dev, int record )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u32             tx_good, tx_under;
+       u32             rx_good, rx_over;
+       u32             def_tx, crc, code;
+       u32             multi_col, single_col;
+       u32             excess_col, late_col, loss;
+
+       outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+       tx_good  = inb( dev->base_addr + TLAN_DIO_DATA );
+       tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+       tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+       tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+       outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+       rx_good  = inb( dev->base_addr + TLAN_DIO_DATA );
+       rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+       rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+       rx_over  = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+               
+       outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR );
+       def_tx  = inb( dev->base_addr + TLAN_DIO_DATA );
+       def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+       crc     = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+       code    = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+       
+       outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+       multi_col   = inb( dev->base_addr + TLAN_DIO_DATA );
+       multi_col  += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+       single_col  = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+       single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8;
+
+       outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+       excess_col = inb( dev->base_addr + TLAN_DIO_DATA );
+       late_col   = inb( dev->base_addr + TLAN_DIO_DATA + 1 );
+       loss       = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+
+       if ( record ) {
+               priv->stats.rx_packets += rx_good;
+               priv->stats.rx_errors  += rx_over + crc + code;
+               priv->stats.tx_packets += tx_good;
+               priv->stats.tx_errors  += tx_under + loss;
+               priv->stats.collisions += multi_col + single_col + excess_col + late_col;
+
+               priv->stats.rx_over_errors    += rx_over;
+               priv->stats.rx_crc_errors     += crc;
+               priv->stats.rx_frame_errors   += code;
+
+               priv->stats.tx_aborted_errors += tx_under;
+               priv->stats.tx_carrier_errors += loss;
+       }
+                       
+} /* TLan_ReadAndClearStats */
+
+
+
+
+       /***************************************************************
+        *      TLan_Reset
+        *
+        *      Returns:
+        *              0
+        *      Parms:
+        *              dev     Pointer to device structure of adapter
+        *                      to be reset.
+        *
+        *      This function resets the adapter and it's physical
+        *      device.  See Chap. 3, pp. 9-10 of the "ThunderLAN
+        *      Programmer's Guide" for details.  The routine tries to
+        *      implement what is detailed there, though adjustments
+        *      have been made.
+        *
+        **************************************************************/
+
+void
+TLan_ResetAdapter( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       int             i;
+       u32             addr;
+       u32             data;
+       u8              data8;
+
+       priv->tlanFullDuplex = FALSE;
+/*  1. Assert reset bit. */
+
+       data = inl(dev->base_addr + TLAN_HOST_CMD);
+       data |= TLAN_HC_AD_RST;
+       outl(data, dev->base_addr + TLAN_HOST_CMD);
+       
+       udelay(1000);
+
+/*  2. Turn off interrupts. ( Probably isn't necessary ) */
+
+       data = inl(dev->base_addr + TLAN_HOST_CMD);
+       data |= TLAN_HC_INT_OFF;
+       outl(data, dev->base_addr + TLAN_HOST_CMD);
+
+/*  3. Clear AREGs and HASHs. */
+
+       for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) {
+               TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
+       }
+
+/*  4. Setup NetConfig register. */
+
+       data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+       TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
+
+/*  5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */
+
+       outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
+       outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
+
+/*  6. Unreset the MII by setting NMRST (in NetSio) to 1. */
+
+       outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
+       addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+       TLan_SetBit( TLAN_NET_SIO_NMRST, addr );
+
+/*  7. Setup the remaining registers. */
+
+       if ( priv->tlanRev >= 0x30 ) {
+               data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC;
+               TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 );
+       }
+       TLan_PhyDetect( dev );
+       data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN;
+       if ( priv->adapter->flags & TLAN_ADAPTER_BIT_RATE_PHY ) {
+               data |= TLAN_NET_CFG_BIT;
+               if ( priv->aui == 1 ) {
+                       TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a );
+               } else if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+                       TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x00 );
+                       priv->tlanFullDuplex = TRUE;
+               } else {
+                       TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 );
+               }
+       }
+       if ( priv->phyNum == 0 ) {
+               data |= TLAN_NET_CFG_PHY_EN;
+       }
+       TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
+
+       if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+               TLan_FinishReset( dev );
+       } else {
+               TLan_PhyPowerDown( dev );
+       }
+
+} /* TLan_ResetAdapter */
+
+
+
+
+void
+TLan_FinishReset( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u8              data;
+       u32             phy;
+       u8              sio;
+       u16             status;
+       u16             tlphy_ctl;
+
+       phy = priv->phy[priv->phyNum];
+
+       data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
+       if ( priv->tlanFullDuplex ) {
+               data |= TLAN_NET_CMD_DUPLEX;
+       }
+       TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data );
+       data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; 
+       if ( priv->phyNum == 0 ) {
+               data |= TLAN_NET_MASK_MASK7; 
+       }
+       TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data );
+       TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE );
+
+       if ( ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) || ( priv->aui ) ) {
+               status = MII_GS_LINK;
+               printk( "TLAN:  %s: Link forced.\n", dev->name );
+       } else {
+               TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+               udelay( 1000 );
+               TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+               if ( status & MII_GS_LINK ) {
+                       printk( "TLAN:  %s: Link active.\n", dev->name );
+                       TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+               }
+       }
+
+       if ( priv->phyNum == 0 ) {
+               TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
+               tlphy_ctl |= TLAN_TC_INTEN;
+               TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl );
+               sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
+               sio |= TLAN_NET_SIO_MINTEN;
+               TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
+       }
+
+       if ( status & MII_GS_LINK ) {
+               TLan_SetMac( dev, 0, dev->dev_addr );
+               priv->phyOnline = 1;
+               outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+               if ( debug >= 1 ) {
+                       outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+               }
+               outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+               outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+       } else {
+               printk( "TLAN:  %s: Link inactive, will retry in 10 secs...\n", dev->name );
+               TLan_SetTimer( dev, 1000, TLAN_TIMER_FINISH_RESET );
+               return;
+       }
+
+} /* TLan_FinishReset */
+
+
+
+
+       /***************************************************************
+        *      TLan_SetMac
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              dev     Pointer to device structure of adapter
+        *                      on which to change the AREG.
+        *              areg    The AREG to set the address in (0 - 3).
+        *              mac     A pointer to an array of chars.  Each
+        *                      element stores one byte of the address.
+        *                      IE, it isn't in ascii.
+        *
+        *      This function transfers a MAC address to one of the
+        *      TLAN AREGs (address registers).  The TLAN chip locks
+        *      the register on writing to offset 0 and unlocks the
+        *      register after writing to offset 5.  If NULL is passed
+        *      in mac, then the AREG is filled with 0's.
+        *
+        **************************************************************/
+
+void TLan_SetMac( struct device *dev, int areg, char *mac )
+{
+       int i;
+                       
+       areg *= 6;
+
+       if ( mac != NULL ) {
+               for ( i = 0; i < 6; i++ )
+                       TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] );
+       } else {
+               for ( i = 0; i < 6; i++ )
+                       TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 );
+       }
+
+} /* TLan_SetMac */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+       ThunderLAN Driver PHY Layer Routines
+
+******************************************************************************
+*****************************************************************************/
+
+
+
+       /*********************************************************************
+        *      TLan_PhyPrint
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              dev     A pointer to the device structure of the
+        *                      TLAN device having the PHYs to be detailed.
+        *                              
+        *      This function prints the registers a PHY (aka tranceiver).
+        *
+        ********************************************************************/
+
+void TLan_PhyPrint( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16 i, data0, data1, data2, data3, phy;
+
+       phy = priv->phy[priv->phyNum];
+
+       if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+               printk( "TLAN:   Device %s, Unmanaged PHY.\n", dev->name );
+       } else if ( phy <= TLAN_PHY_MAX_ADDR ) {
+               printk( "TLAN:   Device %s, PHY 0x%02x.\n", dev->name, phy );
+               printk( "TLAN:      Off.  +0     +1     +2     +3 \n" );
+                for ( i = 0; i < 0x20; i+= 4 ) {
+                       printk( "TLAN:      0x%02x", i );
+                       TLan_MiiReadReg( dev, phy, i, &data0 );
+                       printk( " 0x%04hx", data0 );
+                       TLan_MiiReadReg( dev, phy, i + 1, &data1 );
+                       printk( " 0x%04hx", data1 );
+                       TLan_MiiReadReg( dev, phy, i + 2, &data2 );
+                       printk( " 0x%04hx", data2 );
+                       TLan_MiiReadReg( dev, phy, i + 3, &data3 );
+                       printk( " 0x%04hx\n", data3 );
+               }
+       } else {
+               printk( "TLAN:   Device %s, Invalid PHY.\n", dev->name );
+       }
+
+} /* TLan_PhyPrint */
+
+
+
+
+       /*********************************************************************
+        *      TLan_PhyDetect
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              dev     A pointer to the device structure of the adapter
+        *                      for which the PHY needs determined.
+        *
+        *      So far I've found that adapters which have external PHYs
+        *      may also use the internal PHY for part of the functionality.
+        *      (eg, AUI/Thinnet).  This function finds out if this TLAN
+        *      chip has an internal PHY, and then finds the first external
+        *      PHY (starting from address 0) if it exists).
+        *
+        ********************************************************************/
+
+void TLan_PhyDetect( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16             control;
+       u16             hi;
+       u16             lo;
+       u32             phy;
+
+       if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+               priv->phyNum = 0xFFFF;
+               return;
+       }
+
+       TLan_MiiReadReg( dev, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi );
+
+       if ( hi != 0xFFFF ) {
+               priv->phy[0] = TLAN_PHY_MAX_ADDR;
+       } else {
+               priv->phy[0] = TLAN_PHY_NONE;
+       }
+
+       priv->phy[1] = TLAN_PHY_NONE;
+       for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
+               TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &control );
+               TLan_MiiReadReg( dev, phy, MII_GEN_ID_HI, &hi );
+               TLan_MiiReadReg( dev, phy, MII_GEN_ID_LO, &lo );
+               if ( ( control != 0xFFFF ) || ( hi != 0xFFFF ) || ( lo != 0xFFFF ) ) {
+                       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: PHY found at %02x %04x %04x %04x\n", phy, control, hi, lo );
+                       if ( ( priv->phy[1] == TLAN_PHY_NONE ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
+                               priv->phy[1] = phy;
+                       }
+               }
+       }
+
+       if ( priv->phy[1] != TLAN_PHY_NONE ) {
+               priv->phyNum = 1;
+       } else if ( priv->phy[0] != TLAN_PHY_NONE ) {
+               priv->phyNum = 0;
+       } else {
+               printk( "TLAN:  Cannot initialize device, no PHY was found!\n" );
+       }
+
+} /* TLan_PhyDetect */
+
+
+
+
+void TLan_PhyPowerDown( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16             value;
+
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Powering down PHY(s).\n", dev->name );
+       value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE;
+       TLan_MiiSync( dev->base_addr );
+       TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
+       if ( ( priv->phyNum == 0 ) && ( priv->phy[1] != TLAN_PHY_NONE ) && ( ! ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) ) ) {
+               TLan_MiiSync( dev->base_addr );
+               TLan_MiiWriteReg( dev, priv->phy[1], MII_GEN_CTL, value );
+       }
+
+       /* Wait for 5 jiffies (50 ms) and powerup
+        * This is abitrary.  It is intended to make sure the
+        * tranceiver settles.
+        */
+       TLan_SetTimer( dev, 5, TLAN_TIMER_PHY_PUP );
+
+} /* TLan_PhyPowerDown */
+
+
+
+
+void TLan_PhyPowerUp( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16             value;
+
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Powering up PHY.\n", dev->name );
+       TLan_MiiSync( dev->base_addr );
+       value = MII_GC_LOOPBK;
+       TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
+
+       /* Wait for 50 jiffies (500 ms) and reset the
+        * tranceiver.  The TLAN docs say both 50 ms and
+        * 500 ms, so do the longer, just in case
+        */
+       TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_RESET );
+
+} /* TLan_PhyPowerUp */
+
+
+
+
+void TLan_PhyReset( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16             phy;
+       u16             value;
+
+       phy = priv->phy[priv->phyNum];
+
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Reseting PHY.\n", dev->name );
+       TLan_MiiSync( dev->base_addr );
+       value = MII_GC_LOOPBK | MII_GC_RESET;
+       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, value );
+       TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
+       while ( value & MII_GC_RESET ) {
+               TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
+       }
+       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 );
+
+       /* Wait for 50 jiffies (500 ms) and initialize.
+        * I don't remember why I wait this long.
+        */
+       TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_START_LINK );
+
+} /* TLan_PhyReset */
+
+
+
+
+void TLan_PhyStartLink( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16             ability;
+       u16             control;
+       u16             data;
+       u16             phy;
+       u16             status;
+       u16             tctl;
+
+       phy = priv->phy[priv->phyNum];
+
+       TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Trying to activate link.\n", dev->name );
+       TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+       if ( ( status & MII_GS_AUTONEG ) && 
+            ( priv->duplex == TLAN_DUPLEX_DEFAULT ) && 
+            ( priv->speed == TLAN_SPEED_DEFAULT ) &&
+            ( ! priv->aui ) ) {
+               ability = status >> 11;
+
+               if ( priv->speed == TLAN_SPEED_10 ) {
+                       ability &= 0x0003;
+               } else if ( priv->speed == TLAN_SPEED_100 ) {
+                       ability &= 0x001C;
+               }
+
+               if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+                       ability &= 0x000A;
+               } else if ( priv->duplex == TLAN_DUPLEX_HALF ) {
+                       ability &= 0x0005;
+               }
+
+               TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 );
+                       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
+               TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
+
+               /* Wait for 400 jiffies (4 sec) for autonegotiation
+                * to complete.  The max spec time is less than this
+                * but the card need additional time to start AN.
+                * .5 sec should be plenty extra.
+                */
+               printk( "TLAN:  %s: Starting autonegotiation.\n", dev->name );
+               TLan_SetTimer( dev, 400, TLAN_TIMER_PHY_FINISH_AN );
+               return;
+       }
+
+       if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) {
+               priv->phyNum = 0;
+               data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+               TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
+               TLan_SetTimer( dev, 4, TLAN_TIMER_PHY_PDOWN );
+               return;
+       } else if ( priv->phyNum == 0 ) {
+               TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl );
+               if ( priv->aui ) {
+                       tctl |= TLAN_TC_AUISEL;
+               } else {
+                       tctl &= ~TLAN_TC_AUISEL;
+                       control = 0;
+                       if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+                               control |= MII_GC_DUPLEX;
+                               priv->tlanFullDuplex = TRUE;
+                       }
+                       if ( priv->speed == TLAN_SPEED_100 ) {
+                               control |= MII_GC_SPEEDSEL;
+                       }
+                               TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, control );
+               }
+               TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl );
+       }
+
+       /* Wait for 100 jiffies (1 sec) to give the tranceiver time
+        * to establish link.
+        */
+       TLan_SetTimer( dev, 100, TLAN_TIMER_FINISH_RESET );
+
+} /* TLan_PhyStartLink */
+
+
+
+
+void TLan_PhyFinishAutoNeg( struct device *dev )
+{
+       TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+       u16             an_adv;
+       u16             an_lpa;
+       u16             data;
+       u16             mode;
+       u16             phy;
+       u16             status;
+       
+       phy = priv->phy[priv->phyNum];
+
+       TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+       if ( ! ( status & MII_GS_AUTOCMPLT ) ) {
+               /* Wait for 800 jiffies (8 sec) to give the process
+                * more time.  Perhaps we should fail after a while.
+                */
+               printk( "TLAN:  Giving autonegotiation more time.\n" );
+               TLan_SetTimer( dev, 800, TLAN_TIMER_PHY_FINISH_AN );
+               return;
+       }
+
+       printk( "TLAN:  %s: Autonegotiation complete.\n", dev->name );
+       TLan_MiiReadReg( dev, phy, MII_AN_ADV, &an_adv );
+       TLan_MiiReadReg( dev, phy, MII_AN_LPA, &an_lpa );
+       mode = an_adv & an_lpa & 0x03E0;
+       if ( mode & 0x0100 ) {
+               priv->tlanFullDuplex = TRUE;
+       } else if ( ! ( mode & 0x0080 ) && ( mode & 0x0040 ) ) {
+               priv->tlanFullDuplex = TRUE;
+       }
+
+       if ( ( ! ( mode & 0x0180 ) ) && ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) && ( priv->phyNum != 0 ) ) {
+               priv->phyNum = 0;
+               data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+               TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
+               TLan_SetTimer( dev, 40, TLAN_TIMER_PHY_PDOWN );
+               return;
+       }
+
+       if ( priv->phyNum == 0 ) {
+               if ( ( priv->duplex == TLAN_DUPLEX_FULL ) || ( an_adv & an_lpa & 0x0040 ) ) {
+                       TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX );
+               }
+       }
+
+       /* Wait for 10 jiffies (100 ms).  No reason in partiticular.
+        */
+       TLan_SetTimer( dev, 10, TLAN_TIMER_FINISH_RESET );
+               
+} /* TLan_PhyFinishAutoNeg */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+       ThunderLAN Driver MII Routines
+
+       These routines are based on the information in Chap. 2 of the
+       "ThunderLAN Programmer's Guide", pp. 15-24.
+
+******************************************************************************
+*****************************************************************************/
+
+
+       /***************************************************************
+        *      TLan_MiiReadReg
+        *
+        *      Returns:
+        *              0       if ack received ok
+        *              1       otherwise.
+        *
+        *      Parms:
+        *              dev             The device structure containing
+        *                              The io address and interrupt count
+        *                              for this device.
+        *              phy             The address of the PHY to be queried.
+        *              reg             The register whose contents are to be
+        *                              retreived.
+        *              val             A pointer to a variable to store the
+        *                              retrieved value.
+        *
+        *      This function uses the TLAN's MII bus to retreive the contents
+        *      of a given register on a PHY.  It sends the appropriate info
+        *      and then reads the 16-bit register value from the MII bus via
+        *      the TLAN SIO register.
+        *
+        **************************************************************/
+
+int TLan_MiiReadReg( struct device *dev, u16 phy, u16 reg, u16 *val )
+{
+       u8      nack;
+       u16     sio, tmp;
+       u32     i;
+       int     err;
+       int     minten;
+
+       err = FALSE;
+       outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
+       sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+       if ( dev->interrupt == 0 )
+               cli();
+       dev->interrupt++;
+
+       TLan_MiiSync(dev->base_addr);
+
+       minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
+       if ( minten )
+               TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
+
+       TLan_MiiSendData( dev->base_addr, 0x1, 2 );     /* Start ( 01b ) */
+       TLan_MiiSendData( dev->base_addr, 0x2, 2 );     /* Read  ( 10b ) */
+       TLan_MiiSendData( dev->base_addr, phy, 5 );     /* Device #      */
+       TLan_MiiSendData( dev->base_addr, reg, 5 );     /* Register #    */
+
+
+       TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio);         /* Change direction */
+
+       TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);          /* Clock Idle bit */
+       TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+       TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);          /* Wait 300ns */
+
+       nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio);    /* Check for ACK */
+       TLan_SetBit(TLAN_NET_SIO_MCLK, sio);            /* Finish ACK */
+       if (nack) {                                     /* No ACK, so fake it */
+               for (i = 0; i < 16; i++) {
+                       TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+                       TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+               }
+               tmp = 0xffff;
+               err = TRUE;
+       } else {                                        /* ACK, so read data */
+               for (tmp = 0, i = 0x8000; i; i >>= 1) {
+                       TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+                       if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio))
+                               tmp |= i;
+                       TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+               }
+       }
+
+
+       TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);          /* Idle cycle */
+       TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+
+       if ( minten )
+               TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
+
+       *val = tmp;
+
+       dev->interrupt--;
+       if ( dev->interrupt == 0 )
+               sti();
+
+       return err;
+
+} /* TLan_MiiReadReg */
+
+
+
+
+       /***************************************************************
+        *      TLan_MiiSendData
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              base_port       The base IO port of the adapter in
+        *                              question.
+        *              dev             The address of the PHY to be queried.
+        *              data            The value to be placed on the MII bus.
+        *              num_bits        The number of bits in data that are to
+        *                              be placed on the MII bus.
+        *
+        *      This function sends on sequence of bits on the MII
+        *      configuration bus.
+        *
+        **************************************************************/
+
+void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits )
+{
+       u16 sio;
+       u32 i;
+
+       if ( num_bits == 0 )
+               return;
+
+       outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+       sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+       TLan_SetBit( TLAN_NET_SIO_MTXEN, sio );
+
+       for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) {
+               TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+               TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+               if ( data & i )
+                       TLan_SetBit( TLAN_NET_SIO_MDATA, sio );
+               else
+                       TLan_ClearBit( TLAN_NET_SIO_MDATA, sio );
+               TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+               TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+       }
+
+} /* TLan_MiiSendData */
+
+
+
+
+       /***************************************************************
+        *      TLan_MiiSync
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              base_port       The base IO port of the adapter in
+        *                              question.
+        *
+        *      This functions syncs all PHYs in terms of the MII configuration
+        *      bus.
+        *
+        **************************************************************/
+
+void TLan_MiiSync( u16 base_port )
+{
+       int i;
+       u16 sio;
+
+       outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+       sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+       TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio );
+       for ( i = 0; i < 32; i++ ) {
+               TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+               TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+       }
+
+} /* TLan_MiiSync */
+
+
+
+
+       /***************************************************************
+        *      TLan_MiiWriteReg
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              dev             The device structure for the device
+        *                              to write to.
+        *              phy             The address of the PHY to be written to.
+        *              reg             The register whose contents are to be
+        *                              written.
+        *              val             The value to be written to the register.
+        *
+        *      This function uses the TLAN's MII bus to write the contents of a
+        *      given register on a PHY.  It sends the appropriate info and then
+        *      writes the 16-bit register value from the MII configuration bus
+        *      via the TLAN SIO register.
+        *
+        **************************************************************/
+
+void TLan_MiiWriteReg( struct device *dev, u16 phy, u16 reg, u16 val )
+{
+       u16     sio;
+       int     minten;
+
+       outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
+       sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+       if ( dev->interrupt == 0 )
+               cli();
+       dev->interrupt++;
+
+       TLan_MiiSync( dev->base_addr );
+
+       minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
+       if ( minten )
+               TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
+
+       TLan_MiiSendData( dev->base_addr, 0x1, 2 );     /* Start ( 01b ) */
+       TLan_MiiSendData( dev->base_addr, 0x1, 2 );     /* Write ( 01b ) */
+       TLan_MiiSendData( dev->base_addr, phy, 5 );     /* Device #      */
+       TLan_MiiSendData( dev->base_addr, reg, 5 );     /* Register #    */
+
+       TLan_MiiSendData( dev->base_addr, 0x2, 2 );     /* Send ACK */
+       TLan_MiiSendData( dev->base_addr, val, 16 );    /* Send Data */
+
+       TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );        /* Idle cycle */
+       TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+
+       if ( minten )
+               TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
+
+       dev->interrupt--;
+       if ( dev->interrupt == 0 )
+               sti();
+
+} /* TLan_MiiWriteReg */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+       ThunderLAN Driver Eeprom routines
+
+       The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A
+       EEPROM.  These functions are based on information in Microchip's
+       data sheet.  I don't know how well this functions will work with
+       other EEPROMs.
+
+******************************************************************************
+*****************************************************************************/
+
+
+       /***************************************************************
+        *      TLan_EeSendStart
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:  
+        *              io_base         The IO port base address for the
+        *                              TLAN device with the EEPROM to
+        *                              use.
+        *
+        *      This function sends a start cycle to an EEPROM attached
+        *      to a TLAN chip.
+        *
+        **************************************************************/
+
+void TLan_EeSendStart( u16 io_base )
+{
+       u16     sio;
+
+       outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+       sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+       TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+       TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+       TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+       TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+       TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+
+} /* TLan_EeSendStart */
+
+
+
+
+       /***************************************************************
+        *      TLan_EeSendByte
+        *
+        *      Returns:
+        *              If the correct ack was received, 0, otherwise 1
+        *      Parms:  io_base         The IO port base address for the
+        *                              TLAN device with the EEPROM to
+        *                              use.
+        *              data            The 8 bits of information to
+        *                              send to the EEPROM.
+        *              stop            If TLAN_EEPROM_STOP is passed, a
+        *                              stop cycle is sent after the
+        *                              byte is sent after the ack is
+        *                              read.
+        *
+        *      This function sends a byte on the serial EEPROM line,
+        *      driving the clock to send each bit. The function then
+        *      reverses transmission direction and reads an acknowledge
+        *      bit.
+        *
+        **************************************************************/
+
+int TLan_EeSendByte( u16 io_base, u8 data, int stop )
+{
+       int     err;
+       u8      place;
+       u16     sio;
+
+       outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+       sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+       /* Assume clock is low, tx is enabled; */
+       for ( place = 0x80; place != 0; place >>= 1 ) {
+               if ( place & data )
+                       TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+               else
+                       TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+               TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+               TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+       }
+       TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+       TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+       err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio );
+       TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+       TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+
+       if ( ( ! err ) && stop ) {
+               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       /* STOP, raise data while clock is high */
+               TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+               TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+       }
+
+       return ( err );
+
+} /* TLan_EeSendByte */
+
+
+
+
+       /***************************************************************
+        *      TLan_EeReceiveByte
+        *
+        *      Returns:
+        *              Nothing
+        *      Parms:
+        *              io_base         The IO port base address for the
+        *                              TLAN device with the EEPROM to
+        *                              use.
+        *              data            An address to a char to hold the
+        *                              data sent from the EEPROM.
+        *              stop            If TLAN_EEPROM_STOP is passed, a
+        *                              stop cycle is sent after the
+        *                              byte is received, and no ack is
+        *                              sent.
+        *
+        *      This function receives 8 bits of data from the EEPROM
+        *      over the serial link.  It then sends and ack bit, or no
+        *      ack and a stop bit.  This function is used to retrieve
+        *      data after the address of a byte in the EEPROM has been
+        *      sent.
+        *
+        **************************************************************/
+
+void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
+{
+       u8  place;
+       u16 sio;
+
+       outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+       sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+       *data = 0;
+
+       /* Assume clock is low, tx is enabled; */
+       TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+       for ( place = 0x80; place; place >>= 1 ) {
+               TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+               if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) )
+                       *data |= place;
+               TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+       }
+
+       TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+       if ( ! stop ) {
+               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       /* Ack = 0 */
+               TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+               TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+       } else {
+               TLan_SetBit( TLAN_NET_SIO_EDATA, sio );         /* No ack = 1 (?) */
+               TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+               TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+               TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );       /* STOP, raise data while clock is high */
+               TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+               TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+       }
+
+} /* TLan_EeReceiveByte */
+
+
+
+
+       /***************************************************************
+        *      TLan_EeReadByte
+        *
+        *      Returns:
+        *              No error = 0, else, the stage at which the error
+        *              occured.
+        *      Parms:
+        *              io_base         The IO port base address for the
+        *                              TLAN device with the EEPROM to
+        *                              use.
+        *              ee_addr         The address of the byte in the
+        *                              EEPROM whose contents are to be
+        *                              retrieved.
+        *              data            An address to a char to hold the
+        *                              data obtained from the EEPROM.
+        *
+        *      This function reads a byte of information from an byte
+        *      cell in the EEPROM.
+        *
+        **************************************************************/
+
+int TLan_EeReadByte( struct device *dev, u8 ee_addr, u8 *data )
+{
+       int err;
+
+       if ( dev->interrupt == 0 )
+               cli();
+       dev->interrupt++;
+
+       TLan_EeSendStart( dev->base_addr );
+       err = TLan_EeSendByte( dev->base_addr, 0xA0, TLAN_EEPROM_ACK );
+       if (err)
+               return 1;
+       err = TLan_EeSendByte( dev->base_addr, ee_addr, TLAN_EEPROM_ACK );
+       if (err)
+               return 2;
+       TLan_EeSendStart( dev->base_addr );
+       err = TLan_EeSendByte( dev->base_addr, 0xA1, TLAN_EEPROM_ACK );
+       if (err)
+               return 3;
+       TLan_EeReceiveByte( dev->base_addr, data, TLAN_EEPROM_STOP );
+
+       dev->interrupt--;
+       if ( dev->interrupt == 0 )
+               sti();
+
+       return 0;
+
+} /* TLan_EeReadByte */
+
+
+
+
+
diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h
new file mode 100644 (file)
index 0000000..c8f56e3
--- /dev/null
@@ -0,0 +1,514 @@
+#ifndef TLAN_H
+#define TLAN_H
+/********************************************************************
+ *
+ *  Linux ThunderLAN Driver
+ *
+ *  tlan.h
+ *  by James Banks, james.banks@caldera.com
+ *
+ *  (C) 1997-1998 Caldera, Inc.
+ *
+ *  This software may be used and distributed according to the terms
+ *  of the GNU Public License, incorporated herein by reference.
+ *
+ ** This file is best viewed/edited with tabstop=4, colums>=132
+ *
+ ********************************************************************/
+
+
+#include <asm/io.h>
+#include <asm/types.h>
+#include <linux/netdevice.h>
+
+#if LINUX_VERSION_CODE <= 0x20100
+#define net_device_stats       enet_statistics
+#endif
+
+
+
+
+       /*****************************************************************
+        * TLan Definitions
+        *
+        ****************************************************************/
+
+#define FALSE                  0
+#define TRUE                   1
+
+#define TLAN_MIN_FRAME_SIZE    64
+#define TLAN_MAX_FRAME_SIZE    1600
+
+#define TLAN_NUM_RX_LISTS      4
+#define TLAN_NUM_TX_LISTS      8
+
+#define TLAN_IGNORE            0
+#define TLAN_RECORD            1
+
+#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printk( format, ##args );
+#define TLAN_DEBUG_GNRL                0x0001
+#define TLAN_DEBUG_TX          0x0002
+#define TLAN_DEBUG_RX          0x0004 
+#define TLAN_DEBUG_LIST                0x0008
+
+
+
+
+       /*****************************************************************
+        * Device Identification Definitions
+        *
+        ****************************************************************/
+               
+#define PCI_DEVICE_ID_NETELLIGENT_10                   0xAE34
+#define PCI_DEVICE_ID_NETELLIGENT_10_100               0xAE32
+#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED            0xAE35
+#define PCI_DEVICE_ID_NETFLEX_3P                       0xF130
+#define PCI_DEVICE_ID_NETFLEX_3P_BNC                   0xF150
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT      0xAE43
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL          0xAE40
+#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX             0xB011
+#define PCI_DEVICE_ID_OC_2326                          0x0014
+
+typedef struct tlan_adapter_entry {
+       u16     vendorId;
+       u16     deviceId;
+       char    *deviceLabel;
+       u32     flags;
+} TLanAdapterEntry;
+
+#define TLAN_ADAPTER_NONE              0x00000000
+#define TLAN_ADAPTER_UNMANAGED_PHY     0x00000001
+#define TLAN_ADAPTER_BIT_RATE_PHY      0x00000002
+#define TLAN_ADAPTER_USE_INTERN_10     0x00000004
+#define TLAN_ADAPTER_ACTIVITY_LED      0x00000008
+
+#define TLAN_SPEED_DEFAULT     0
+#define TLAN_SPEED_10          10
+#define TLAN_SPEED_100         100
+
+#define TLAN_DUPLEX_DEFAULT    0
+#define TLAN_DUPLEX_HALF       1
+#define TLAN_DUPLEX_FULL       2
+
+
+
+
+       /*****************************************************************
+        * Rx/Tx List Definitions
+        *
+        ****************************************************************/
+
+#define TLAN_BUFFERS_PER_LIST  10
+#define TLAN_LAST_BUFFER       0x80000000
+#define TLAN_CSTAT_UNUSED      0x8000
+#define TLAN_CSTAT_FRM_CMP     0x4000
+#define TLAN_CSTAT_READY       0x3000
+#define TLAN_CSTAT_EOC         0x0800
+#define TLAN_CSTAT_RX_ERROR    0x0400
+#define TLAN_CSTAT_PASS_CRC    0x0200
+#define TLAN_CSTAT_DP_PR       0x0100
+
+
+typedef struct tlan_buffer_ref_tag {
+       u32     count;
+       u32     address;
+} TLanBufferRef;
+
+
+typedef struct tlan_list_tag {
+       u32             forward;
+       u16             cStat;
+       u16             frameSize;
+       TLanBufferRef   buffer[TLAN_BUFFERS_PER_LIST];
+} TLanList;
+
+
+typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
+
+
+
+
+       /*****************************************************************
+        * PHY definitions
+        *
+        ****************************************************************/
+
+#define TLAN_PHY_MAX_ADDR      0x1F
+#define TLAN_PHY_NONE          0x20
+
+
+
+
+       /*****************************************************************
+        * TLAN Private Information Structure
+        *
+        ****************************************************************/
+
+typedef struct tlan_private_tag {
+       struct device           *nextDevice;
+       void                    *dmaStorage;
+       u8                      *padBuffer;
+       TLanList                *rxList;
+       u8                      *rxBuffer;
+       u32                     rxHead;
+       u32                     rxTail;
+       u32                     rxEocCount;
+       TLanList                *txList;
+       u8                      *txBuffer;
+       u32                     txHead;
+       u32                     txInProgress;
+       u32                     txTail;
+       u32                     txBusyCount;
+       u32                     phyOnline;
+       u32                     timerSetAt;
+       u32                     timerType;
+       struct timer_list       timer;
+       struct net_device_stats stats;
+       TLanAdapterEntry        *adapter;
+       u32                     adapterRev;
+       u32                     aui;
+       u32                     debug;
+       u32                     duplex;
+       u32                     phy[2];
+       u32                     phyNum;
+       u32                     sa_int;
+       u32                     speed;
+       u8                      tlanRev;
+       u8                      tlanFullDuplex;
+       char                    devName[8];
+} TLanPrivateInfo;
+
+
+
+
+       /*****************************************************************
+        * TLan Driver Timer Definitions
+        *
+        ****************************************************************/
+
+#define TLAN_TIMER_LINK                        1
+#define TLAN_TIMER_ACTIVITY            2
+#define TLAN_TIMER_PHY_PDOWN           3
+#define TLAN_TIMER_PHY_PUP             4
+#define TLAN_TIMER_PHY_RESET           5
+#define TLAN_TIMER_PHY_START_LINK      6
+#define TLAN_TIMER_PHY_FINISH_AN       7
+#define TLAN_TIMER_FINISH_RESET                8
+
+#define TLAN_TIMER_ACT_DELAY           10
+
+
+
+
+       /*****************************************************************
+        * TLan Driver Eeprom Definitions
+        *
+        ****************************************************************/
+
+#define TLAN_EEPROM_ACK                0
+#define TLAN_EEPROM_STOP       1
+
+
+
+
+       /*****************************************************************
+        * Host Register Offsets and Contents
+        *
+        ****************************************************************/
+
+#define TLAN_HOST_CMD                  0x00
+#define        TLAN_HC_GO              0x80000000
+#define                TLAN_HC_STOP            0x40000000
+#define                TLAN_HC_ACK             0x20000000
+#define                TLAN_HC_CS_MASK         0x1FE00000
+#define                TLAN_HC_EOC             0x00100000
+#define                TLAN_HC_RT              0x00080000
+#define                TLAN_HC_NES             0x00040000
+#define                TLAN_HC_AD_RST          0x00008000
+#define                TLAN_HC_LD_TMR          0x00004000
+#define                TLAN_HC_LD_THR          0x00002000
+#define                TLAN_HC_REQ_INT         0x00001000
+#define                TLAN_HC_INT_OFF         0x00000800
+#define                TLAN_HC_INT_ON          0x00000400
+#define                TLAN_HC_AC_MASK         0x000000FF
+#define TLAN_CH_PARM                   0x04
+#define TLAN_DIO_ADR                   0x08
+#define                TLAN_DA_ADR_INC         0x8000
+#define                TLAN_DA_RAM_ADR         0x4000
+#define TLAN_HOST_INT                  0x0A
+#define                TLAN_HI_IV_MASK         0x1FE0
+#define                TLAN_HI_IT_MASK         0x001C
+#define TLAN_DIO_DATA                  0x0C
+
+
+/* ThunderLAN Internal Register DIO Offsets */
+
+#define TLAN_NET_CMD                   0x00
+#define                TLAN_NET_CMD_NRESET     0x80
+#define                TLAN_NET_CMD_NWRAP      0x40
+#define                TLAN_NET_CMD_CSF        0x20
+#define                TLAN_NET_CMD_CAF        0x10
+#define                TLAN_NET_CMD_NOBRX      0x08
+#define                TLAN_NET_CMD_DUPLEX     0x04
+#define                TLAN_NET_CMD_TRFRAM     0x02
+#define                TLAN_NET_CMD_TXPACE     0x01
+#define TLAN_NET_SIO                   0x01
+#define        TLAN_NET_SIO_MINTEN     0x80
+#define                TLAN_NET_SIO_ECLOK      0x40
+#define                TLAN_NET_SIO_ETXEN      0x20
+#define                TLAN_NET_SIO_EDATA      0x10
+#define                TLAN_NET_SIO_NMRST      0x08
+#define                TLAN_NET_SIO_MCLK       0x04
+#define                TLAN_NET_SIO_MTXEN      0x02
+#define                TLAN_NET_SIO_MDATA      0x01
+#define TLAN_NET_STS                   0x02
+#define                TLAN_NET_STS_MIRQ       0x80
+#define                TLAN_NET_STS_HBEAT      0x40
+#define                TLAN_NET_STS_TXSTOP     0x20
+#define                TLAN_NET_STS_RXSTOP     0x10
+#define                TLAN_NET_STS_RSRVD      0x0F
+#define TLAN_NET_MASK                  0x03
+#define                TLAN_NET_MASK_MASK7     0x80
+#define                TLAN_NET_MASK_MASK6     0x40
+#define                TLAN_NET_MASK_MASK5     0x20
+#define                TLAN_NET_MASK_MASK4     0x10
+#define                TLAN_NET_MASK_RSRVD     0x0F
+#define TLAN_NET_CONFIG                        0x04
+#define        TLAN_NET_CFG_RCLK       0x8000
+#define                TLAN_NET_CFG_TCLK       0x4000
+#define                TLAN_NET_CFG_BIT        0x2000
+#define                TLAN_NET_CFG_RXCRC      0x1000
+#define                TLAN_NET_CFG_PEF        0x0800
+#define                TLAN_NET_CFG_1FRAG      0x0400
+#define                TLAN_NET_CFG_1CHAN      0x0200
+#define                TLAN_NET_CFG_MTEST      0x0100
+#define                TLAN_NET_CFG_PHY_EN     0x0080
+#define                TLAN_NET_CFG_MSMASK     0x007F
+#define TLAN_MAN_TEST                  0x06
+#define TLAN_DEF_VENDOR_ID             0x08
+#define TLAN_DEF_DEVICE_ID             0x0A
+#define TLAN_DEF_REVISION              0x0C
+#define TLAN_DEF_SUBCLASS              0x0D
+#define TLAN_DEF_MIN_LAT               0x0E
+#define TLAN_DEF_MAX_LAT               0x0F
+#define TLAN_AREG_0                    0x10
+#define TLAN_AREG_1                    0x16
+#define TLAN_AREG_2                    0x1C
+#define TLAN_AREG_3                    0x22
+#define TLAN_HASH_1                    0x28
+#define TLAN_HASH_2                    0x2C
+#define TLAN_GOOD_TX_FRMS              0x30
+#define TLAN_TX_UNDERUNS               0x33
+#define TLAN_GOOD_RX_FRMS              0x34
+#define TLAN_RX_OVERRUNS               0x37
+#define TLAN_DEFERRED_TX               0x38
+#define TLAN_CRC_ERRORS                        0x3A
+#define TLAN_CODE_ERRORS               0x3B
+#define TLAN_MULTICOL_FRMS             0x3C
+#define TLAN_SINGLECOL_FRMS            0x3E
+#define TLAN_EXCESSCOL_FRMS            0x40
+#define TLAN_LATE_COLS                 0x41
+#define TLAN_CARRIER_LOSS              0x42
+#define TLAN_ACOMMIT                   0x43
+#define TLAN_LED_REG                   0x44
+#define                TLAN_LED_ACT            0x10
+#define                TLAN_LED_LINK           0x01
+#define TLAN_BSIZE_REG                 0x45
+#define TLAN_MAX_RX                    0x46
+#define TLAN_INT_DIS                   0x48
+#define                TLAN_ID_TX_EOC          0x04
+#define                TLAN_ID_RX_EOF          0x02
+#define                TLAN_ID_RX_EOC          0x01
+
+
+
+/* ThunderLAN Interrupt Codes */
+
+#define TLAN_INT_NUMBER_OF_INTS        8
+
+#define TLAN_INT_NONE                  0x0000
+#define TLAN_INT_TX_EOF                        0x0001
+#define TLAN_INT_STAT_OVERFLOW         0x0002
+#define TLAN_INT_RX_EOF                        0x0003
+#define TLAN_INT_DUMMY                 0x0004
+#define TLAN_INT_TX_EOC                        0x0005
+#define TLAN_INT_STATUS_CHECK          0x0006
+#define TLAN_INT_RX_EOC                        0x0007
+
+
+
+/* ThunderLAN MII Registers */
+
+/* Generic MII/PHY Registers */
+
+#define MII_GEN_CTL                    0x00
+#define        MII_GC_RESET            0x8000
+#define                MII_GC_LOOPBK           0x4000
+#define                MII_GC_SPEEDSEL         0x2000
+#define                MII_GC_AUTOENB          0x1000
+#define                MII_GC_PDOWN            0x0800
+#define                MII_GC_ISOLATE          0x0400
+#define                MII_GC_AUTORSRT         0x0200
+#define                MII_GC_DUPLEX           0x0100
+#define                MII_GC_COLTEST          0x0080
+#define                MII_GC_RESERVED         0x007F
+#define MII_GEN_STS                    0x01
+#define                MII_GS_100BT4           0x8000
+#define                MII_GS_100BTXFD         0x4000
+#define                MII_GS_100BTXHD         0x2000
+#define                MII_GS_10BTFD           0x1000
+#define                MII_GS_10BTHD           0x0800
+#define                MII_GS_RESERVED         0x07C0
+#define                MII_GS_AUTOCMPLT        0x0020
+#define                MII_GS_RFLT             0x0010
+#define                MII_GS_AUTONEG          0x0008
+#define                MII_GS_LINK             0x0004
+#define                MII_GS_JABBER           0x0002
+#define                MII_GS_EXTCAP           0x0001
+#define MII_GEN_ID_HI                  0x02
+#define MII_GEN_ID_LO                  0x03
+#define        MII_GIL_OUI             0xFC00
+#define        MII_GIL_MODEL           0x03F0
+#define        MII_GIL_REVISION        0x000F
+#define MII_AN_ADV                     0x04
+#define MII_AN_LPA                     0x05
+#define MII_AN_EXP                     0x06
+
+/* ThunderLAN Specific MII/PHY Registers */
+
+#define TLAN_TLPHY_ID                  0x10
+#define TLAN_TLPHY_CTL                 0x11
+#define        TLAN_TC_IGLINK          0x8000
+#define                TLAN_TC_SWAPOL          0x4000
+#define                TLAN_TC_AUISEL          0x2000
+#define                TLAN_TC_SQEEN           0x1000
+#define                TLAN_TC_MTEST           0x0800
+#define                TLAN_TC_RESERVED        0x07F8
+#define                TLAN_TC_NFEW            0x0004
+#define                TLAN_TC_INTEN           0x0002
+#define                TLAN_TC_TINT            0x0001
+#define TLAN_TLPHY_STS                 0x12
+#define                TLAN_TS_MINT            0x8000
+#define                TLAN_TS_PHOK            0x4000
+#define                TLAN_TS_POLOK           0x2000
+#define                TLAN_TS_TPENERGY        0x1000
+#define                TLAN_TS_RESERVED        0x0FFF
+
+
+#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
+
+/* Routines to access internal registers. */
+
+inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr)
+{
+       outw(internal_addr, base_addr + TLAN_DIO_ADR);
+       return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3)));
+       
+} /* TLan_DioRead8 */
+
+
+
+
+inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr)
+{
+       outw(internal_addr, base_addr + TLAN_DIO_ADR);
+       return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2)));
+
+} /* TLan_DioRead16 */
+
+
+
+
+inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr)
+{
+       outw(internal_addr, base_addr + TLAN_DIO_ADR);
+       return (inl(base_addr + TLAN_DIO_DATA));
+
+} /* TLan_DioRead32 */
+
+
+
+
+inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data)
+{
+       outw(internal_addr, base_addr + TLAN_DIO_ADR);
+       outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3));
+
+}
+
+
+
+
+inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data)
+{
+       outw(internal_addr, base_addr + TLAN_DIO_ADR);
+       outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+
+inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data)
+{
+       outw(internal_addr, base_addr + TLAN_DIO_ADR);
+       outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+#if 0
+inline void TLan_ClearBit(u8 bit, u16 port)
+{
+       outb_p(inb_p(port) & ~bit, port);
+}
+
+
+
+
+inline int TLan_GetBit(u8 bit, u16 port)
+{
+       return ((int) (inb_p(port) & bit));
+}
+
+
+
+
+inline void TLan_SetBit(u8 bit, u16 port)
+{
+       outb_p(inb_p(port) | bit, port);
+}
+#endif
+
+#define TLan_ClearBit( bit, port )     outb_p(inb_p(port) & ~bit, port)
+#define TLan_GetBit( bit, port )       ((int) (inb_p(port) & bit))
+#define TLan_SetBit( bit, port )       outb_p(inb_p(port) | bit, port)
+
+
+inline u32     xor( u32 a, u32 b )
+{
+       return ( ( a && ! b ) || ( ! a && b ) );
+}
+#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) )
+#define DA( a, bit )                                   ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) )
+
+inline u32 TLan_HashFunc( u8 *a )
+{
+       u32     hash;
+
+       hash  = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) );
+       hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1;
+       hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2;
+       hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3;
+       hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4;
+       hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5;
+
+       return hash;
+
+} 
+
+
+
+
+#endif
index 94a2319757f321ec7cc8f81c031962a2f08b3952..8d38bfd8d8340704e5590e1cfc9e8977917df358 100644 (file)
@@ -1,3 +1,75 @@
+Fri Jan 2 18:00 1998 Gerard Roudier (groudier@club-internet.fr)
+       * Revision 2.5f
+       - Use FAST-5 instead of SLOW for slow scsi devices according to 
+         new SPI-2 draft.
+       - Make some changes in order to accomodate with 875 rev <= 3 
+         device errata listing 397. Minor consequences are:
+         . Leave use of PCI Write and Invalidate under user control.
+           Now, by default the driver does not enable PCI MWI and option
+           'specf:y' is required in order to enable this feature.
+         . Memory Read Line is not enabled for 875 and 875-like chips.
+         . Programmed burst length set to 64 DWORDS (instead of 128).
+           (Note: SYMBIOS uses 32 DWORDS for the SDMS BIOS)
+
+Sun Oct 26 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.5e
+       - Add 'buschk' boot option.
+         This option enables checking of SCSI BUS data lines after SCSI 
+         RESET (set by default). (Submitted by Richard Waltham).
+       - Update the README file.
+
+Sat Oct 4 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.5d
+       - Dispatch CONDITION MET and RESERVATION CONFLICT scsi status 
+         as OK driver status.
+       - Update the README file and the Symbios NVRAM format definition 
+         with removable media flags values (available with SDMS 4.09).
+
+Sat Sep 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.5c
+       - Several PCI configuration registers fix-ups for powerpc.
+         (Patch sent by Cort).
+
+Thu Aug 28 10:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.5b
+       - Add 'ncr53c8xx' char pointer variable. This variable allows to 
+         pass a boot command to the driver when it is loaded as a module.
+         Option separator is ' ' instead of ','. Example:
+           insmod <mod_path>/ncr53c8xx.o ncr53c8xx='verb:2 sync:0 specf:n'
+       - Always use 'driver_setup.settle_delay' for internal resets.
+         2 seconds hardcoded is sometimes too short. Suggested by Richard W. 
+         This delay may be shortenned in order to avoid spurious timeouts.
+       - Fix release module stuff that failed for more than 1 controller.
+       - For linux versions > 1.3.70, trust the 'dev_id' parameter passed 
+         to the interrupt handler (dev_id = struct ncb *).
+       - Fix up in 'ncr_log_hard_error()' when the DSP points outside scripts.
+         Suggested by Stefan Esser.
+
+Tue Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.5a
+       - Update Configure.help for inclusion in linux-2.1.51/2/3
+       - Use BASE_2 address from PCI config space instead of some 
+         IO register for getting the on-board SRAM bus address.
+       - Remove error testing of pcibios_read/write functions.
+         These functions are intended to be used for successfully 
+         detected PCI devices. Expecting error condition from them 
+         is nothing but paranoia.
+
+Thu Aug 21 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.5
+       - 53C860 chip support fix.
+       - Move the 'host_status' to the last DWORD of the CCB header.
+         This header is copied back by the script processor. This 
+         guarantees that the header is entirely copied back over 
+         the PCI when the CPU completes a CCB.
+       - (re)read ISTAT prior to scanning CCBs for completion. This
+         ensure that any posted buffer are flushed prior CCBs scan.
+       - Support for BIG ENDIAN cpu. Added by Cort <cort@cs.nmt.edu>.
+         Initial patch did'nt support disconnections and tagged commands.
+         I've completed the patch and it seems that all is ok now.
+         Only some powerpc under 2.1.X is supported for the moment.
+       - Misc. trivial fixes and cleanups.
+
 Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
        * revision 2.4
        Several clean-ups:
index 1acf9887f377644d8be4b7f07810c9923a8e7eb3..4dab71b4a63e8022306ed27c7f3c3b8a5fe58b46 100644 (file)
@@ -4,7 +4,7 @@ Written by Gerard Roudier <groudier@club-internet.fr>
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-19 June 1997
+2 January 1998
 ===============================================================================
 
 1.  Introduction
@@ -30,6 +30,7 @@ Written by Gerard Roudier <groudier@club-internet.fr>
       10.3 Advised boot setup commands
       10.4 PCI configuration fix-up boot option
       10.5 Serial NVRAM support boot option
+      10.6 SCSI BUS checking boot option
 11. Some constants and flags of the ncr53c8xx.h header file
 12. Installation
       12.1 Provided files
@@ -38,6 +39,8 @@ Written by Gerard Roudier <groudier@club-internet.fr>
 14. Known problems
       14.1 Tagged commands with Iomega Jaz device
       14.2 Device names change when another controller is added
+      14.3 Using only 8 bit devices with a WIDE SCSI controller.
+      14.4 Possible data corruption during a Memory Write and Invalidate
 15. SCSI problem troubleshooting
 16. Synchonous transfer negotiation tables
       16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
@@ -46,6 +49,9 @@ Written by Gerard Roudier <groudier@club-internet.fr>
       17.1 Features
       17.2 Symbios NVRAM layout
       17.3 Tekram  NVRAM layout
+18. Support for Big Endian
+      18.1 Big Endian CPU
+      18.2 NCR chip in Big Endian mode of operations
 
 ===============================================================================
 
@@ -83,7 +89,7 @@ This short documentation only describes the features of the NCR53C8XX
 driver, configuration parameters and control commands available
 through the proc SCSI file system read / write operations.
 
-This driver has been tested OK with linux/i386 and Linux/Alpha.
+This driver has been tested OK with linux/i386, Linux/Alpha and Linux/PPC.
 
 Latest driver version and patches are available at:
 
@@ -429,7 +435,10 @@ this feature after boot-up only for devices that support it safely.
 
 CONFIG_SCSI_NCR53C8XX_IOMAPPED       (default answer: n)
     Answer "y" if you suspect your mother board to not allow memory mapped I/O.
-    May slow down performance a little.
+    May slow down performance a little.  This option is required by
+    Linux/PPC and is used no matter what you select here.  Linux/PPC
+    suffers no performance loss with this option since all IO is memory
+    mapped anyway.
 
 CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE    (default answer: n)
     Answer "y" if you are sure that all your SCSI devices that are able to 
@@ -484,7 +493,9 @@ CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
 
 10.1 Syntax
 
-Setup commands can be passed to the driver at boot time.
+Setup commands can be passed to the driver either at boot time or as a 
+string variable using 'insmod'.
+
 A boot setup command for the ncr53c8xx driver begins with the driver name 
 "ncr53c8xx=". The kernel syntax parser then expects an optionnal list of
 integers separated with comma followed by an optionnal list of  comma- 
@@ -496,7 +507,14 @@ lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200
 - set synchronous negotiation speed to 10 Mega-transfers / second.
 - set DEBUG_NEGO flag.
 
-For the moment, the integer list of arguments is disgarded by the driver. 
+Since comma seems not to be allowed when defining a string variable using  
+'insmod', the driver also accepts <space> as option separator. 
+The following command will install driver module with the same options as 
+above.
+
+insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200"
+
+For the moment, the integer list of arguments is discarded by the driver. 
 It will be used in the future in order to allow a per controller setup.
 
 Each string argument must be specified as "keyword:value". Only lower-case 
@@ -519,8 +537,12 @@ Scsi disconnections
 Special features
    Only apply to 810A, 825A, 860 and 875 controllers.
    Have no effect with normal 810 and 825.
-      specf:y    enabled
-      specf:n    disabled
+      specf:y    (or 1) enabled
+      specf:n    (or 0) disabled
+      specf:3           enabled except Memory Write And Invalidate
+   The default driver setup is 'specf:3'. As a consequence, option 'specf:y' 
+   must be specified in the boot setup command to enable Memory Write And 
+   Invalidate.
 
 Ultra SCSI support
    Only apply to 860 and 875 controllers.
@@ -622,6 +644,7 @@ Fix up PCI configuration space
     pcifix:<option bits>
 
     Available option bits:
+        0x0:   No attempt to fix PCI configuration space registers values.
         0x1:   Set PCI cache-line size register if not set.
         0x2:   Set write and invalidate bit in PCI command register.
         0x4:   Increase if necessary PCI latency timer according to burst max.
@@ -632,6 +655,14 @@ Serial NVRAM
     nvram:n     do not look for serial NVRAM
     nvram:y     test controllers for onboard serial NVRAM
 
+Check SCSI BUS 
+    buschk:<option bits>
+
+    Available option bits:
+        0x0:   No check.
+        0x1:   Check and donnot attach the controller on error.  
+        0x2:   Check and just warn on error.
+
 Boot fail safe
     safe:y     load the following assumed fail safe initial setup
 
@@ -654,13 +685,14 @@ Boot fail safe
   settle time                  10 seconds              settle:10
   differential support         from BIOS settings      diff:1
   irq mode                     from BIOS settings      irqm:1
+  SCSI BUS check               donnot attach on error  buschk:1
 
 10.3 Advised boot setup commands
 
 If the driver has been configured with default options, the equivalent 
 boot setup is:
 
-   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:3,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
              tags:0,sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
 
 For an installation diskette or a safe but not fast system,
@@ -673,7 +705,7 @@ boot setup can be:
 
 My personnal system works flawlessly with the following equivalent setup:
 
-   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:1,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
              tags:8,sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
 
 The driver prints its actual setup when verbosity level is 2. You can try 
@@ -759,6 +791,21 @@ Using "nvram=0x7" allows me to boot in 8 bits/async and to let the driver
 use its setup for synchronous and wide negotiations.
 
 
+10.6 SCSI BUS checking boot option.
+
+When this option is set to a non-zero value, the driver checks SCSI lines 
+logic state, 100 micro-seconds after having asserted the SCSI RESET line.
+The driver just reads SCSI lines and checks all lines read FALSE except RESET.
+Since SCSI devices shall release the BUS at most 800 nano-seconds after SCSI 
+RESET has been asserted, any signal to TRUE may indicate a SCSI BUS problem.
+Unfortunately, the following common SCSI BUS problems are not detected:
+- Only 1 terminator installed.
+- Misplaced terminators.
+- Bad quality terminators.
+On the other hand, either bad cabling, broken devices, not conformant 
+devices, ... may cause a SCSI signal to be wrong when te driver reads it.
+
+
 11. Some constants and flags of the ncr53c8xx.h header file
 
 Some of these are defined from the configuration parameters.  To
@@ -946,6 +993,50 @@ If your controllers do not have NvRAM, you can:
 - Make appropriate changes in the fstab.
 - Use the 'scsidev' tool from Eric Youngdale.
 
+14.3 Using only 8 bit devices with a WIDE SCSI controller.
+
+When only 8 bit NARROW devices are connected to a 16 bit WIDE SCSI controller, 
+you must ensure that lines of the wide part of the SCSI BUS are pulled-up.
+This can be achieved by ENABLING the WIDE TERMINATOR portion of the SCSI 
+controller card.
+The TYAN 1365 documentation revision 1.2 is not correct about such settings.
+(page 10, figure 3.3).
+
+14.4 Possible data corruption during a Memory Write and Invalidate
+
+This problem is described in SYMBIOS DEL 397, Part Number 69-039241, ITEM 4.
+
+In some complex situations, 53C875 chips revision <= 3 may start a PCI 
+Write and Invalidate Command at a not cache-line-aligned 4 DWORDS boundary.
+This is only possible when Cache Line Size is 8 DWORDS or greater.
+Pentium systems use a 8 DWORDS cache line size and so are concerned by 
+this chip bug, unlike i486 systems that use a 4 DWORDS cache line size.
+
+When this situation occurs, the chip may complete the Write and Invalidate 
+command after having only filled part of the last cache line involved in 
+the transfer, leaving to data corruption the remainder of this cache line.
+
+Not using Write And Invalidate obviously gets rid of this chip bug, and so 
+it is now the default setting of the driver.
+However, for people like me who want to enable this feature, I have added 
+part of a work-around suggested by SYMBIOS. This work-around resets the 
+addressing logic when the DATA IN phase is entered and so prevents the bug 
+from being triggered for the first SCSI MOVE of the phase. This work-around 
+should be enough according to the following:
+
+The only driver internal data structure that is greater than 8 DWORDS  and 
+that is moved by the SCRIPTS processor is the 'CCB header' that contains 
+the context of the SCSI transfer. This data structure is aligned on 8 DWORDS 
+boundary (Pentium Cache Line Size), and so is immune to this chip bug, at 
+least on Pentium systems.
+But the conditions of this bug can be met when a SCSI read command is 
+performed using a buffer that is 4 DWORDS but not cache-line aligned.
+This cannot happen under Linux when scatter/gather lists are used since 
+they only refer to system buffers that are well aligned. So, a work around 
+may only be needed under Linux when a scatter/gather list is not used and 
+when the SCSI DATA IN phase is reentered after a phase mismatch.
+
 15. SCSI problem troubleshooting
 
 Most SCSI problems are due to a non conformant SCSI bus or to buggy
@@ -1210,8 +1301,14 @@ header
 controller set up
 
 00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
-                   |     |                 |
-                   |     |                  -- host ID
+                   |     |           |     |
+                   |     |           |      -- host ID
+                   |     |           |
+                   |     |            --Removable Media Support
+                   |     |               0x00 = none
+                   |     |               0x01 = Bootable Device
+                   |     |               0x02 = All with Media
+                   |     |
                    |      --flag bits 2
                    |        0x00000001= scan order hi->low
                    |            (default 0x00 - scan low->hi)
@@ -1224,6 +1321,7 @@ remaining bytes unknown - they do not appear to change in my
 current set up for any of the controllers.
 
 default set up is identical for 53c810a and 53c875 NVRAM
+(Removable Media added Symbios BIOS version 4.09)
 -----------------------------------------------------------
 boot configuration
 
@@ -1430,8 +1528,29 @@ default nvram data:
 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
 
-===============================================================================
-End of NCR53C8XX driver README file
 
+18. Support for Big Endian
+
+The PCI local bus has been primarily designed for x86 architecture.
+As a consequence, PCI devices generally expect DWORDS using little endian 
+byte ordering.
+
+18.1 Big Endian CPU
 
+In order to support NCR chips on a Big Endian architecture the driver has to 
+perform byte reordering each time it is needed. This feature has been 
+added to the driver by Cort <cort@cs.nmt.edu> and is available in driver 
+version 2.5 and later ones. For the moment Big Endian support has only 
+been tested on Linux/PPC (PowerPC).
 
+18.2 NCR chip in Big Endian mode of operations
+
+It can be read in SYMBIOS documentation that some chips support a special 
+Big Endian mode, on paper: 53C815, 53C825A, 53C875, 53C875N, 53C895.
+This mode of operations is not software-selectable, but needs pin named 
+BigLit to be pulled-up. Using this mode, most of byte reorderings should 
+be avoided when the driver is running on a Big Endian CPU.
+Driver version 2.5 is also, in theory, ready for this feature.
+
+===============================================================================
+End of NCR53C8XX driver README file
index aeda681c1a8055d5f7e41edfb7f5782277d956ea..da2603a6af0d0b61abb51cb65b41162e89981942 100644 (file)
@@ -1,6 +1,14 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *   
+ *       4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
+ *          io_port is now unsigned long.
+ *
+ *      17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
+ *          Use new scsi error handling code (if linux version >= 2.1.88).
+ *          Use new interrupt code.
+ *
+ *
  *      12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
  *          Use of udelay inside the wait loops to avoid timeout
  *          problems with fast cpus.
  *          This driver is based on the CAM (Common Access Method Committee)
  *          EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
  *
- *  Copyright (C) 1994-1997 Dario Ballabio (dario@milano.europe.dg.com)
+ *  Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com)
  *
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that redistributions of source
@@ -331,9 +339,7 @@ struct proc_dir_entry proc_scsi_eata2x = {
 #undef  DEBUG_LINKED_COMMANDS
 #undef  DEBUG_DETECT
 #undef  DEBUG_INTERRUPT
-#undef  DEBUG_STATISTICS
 #undef  DEBUG_RESET
-#undef  DEBUG_SMP
 
 #define MAX_ISA 4
 #define MAX_VESA 0 
@@ -343,15 +349,14 @@ struct proc_dir_entry proc_scsi_eata2x = {
 #define MAX_CHANNEL 4
 #define MAX_LUN 32
 #define MAX_TARGET 32
-#define MAX_IRQ 16
 #define MAX_MAILBOXES 64
 #define MAX_SGLIST 64
-#define MAX_LARGE_SGLIST 252
+#define MAX_LARGE_SGLIST 122
 #define MAX_INTERNAL_RETRIES 64
 #define MAX_CMD_PER_LUN 2
 #define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
 
-#define SKIP UINT_MAX
+#define SKIP ULONG_MAX
 #define FALSE 0
 #define TRUE 1
 #define FREE 0
@@ -415,15 +420,15 @@ struct eata_info {
    ulong  data_len;     /* Number of valid bytes after this field */
    ulong  sign;         /* ASCII "EATA" signature */
    unchar        :4,    /* unused low nibble */
-         version:4;    /* EATA version, should be 0x1 */
+          version:4;    /* EATA version, should be 0x1 */
    unchar  ocsena:1,    /* Overlap Command Support Enabled */
-          tarsup:1,    /* Target Mode Supported */
+           tarsup:1,    /* Target Mode Supported */
            trnxfr:1,    /* Truncate Transfer Cmd NOT Necessary */
            morsup:1,    /* More Supported */
-          dmasup:1,    /* DMA Supported */
-          drqvld:1,    /* DRQ Index (DRQX) is valid */
-             ata:1,    /* This is an ATA device */
-          haaval:1;    /* Host Adapter Address Valid */
+           dmasup:1,    /* DMA Supported */
+           drqvld:1,    /* DRQ Index (DRQX) is valid */
+              ata:1,    /* This is an ATA device */
+           haaval:1;    /* Host Adapter Address Valid */
    ushort cp_pad_len;   /* Number of pad bytes after cp_len */
    unchar host_addr[4]; /* Host Adapter SCSI ID for channels 3, 2, 1, 0 */
    ulong  cp_len;       /* Number of valid bytes in cp */
@@ -432,19 +437,19 @@ struct eata_info {
    ushort unused;
    ushort scatt_size;   /* Max number of entries in scatter/gather table */
    unchar     irq:4,    /* Interrupt Request assigned to this controller */
-          irq_tr:1,    /* 0 for edge triggered, 1 for level triggered */
-          second:1,    /* 1 if this is a secondary (not primary) controller */
-            drqx:2;    /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
+           irq_tr:1,    /* 0 for edge triggered, 1 for level triggered */
+           second:1,    /* 1 if this is a secondary (not primary) controller */
+             drqx:2;    /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
    unchar  sync;        /* 1 if scsi target id 7...0 is running sync scsi */
 
    /* Structure extension defined in EATA 2.0B */
    unchar  isaena:1,    /* ISA i/o addressing is disabled/enabled */
-        forcaddr:1,    /* Port address has been forced */
+         forcaddr:1,    /* Port address has been forced */
          large_sg:1,    /* 1 if large SG lists are supported */
              res1:1,
-                :4;
+                 :4;
    unchar  max_id:5,    /* Max SCSI target ID number */
-        max_chan:3;    /* Max SCSI channel number on this board */
+         max_chan:3;    /* Max SCSI channel number on this board */
 
    /* Structure extension defined in EATA 2.0C */
    unchar   max_lun;    /* Max SCSI LUN number */
@@ -463,17 +468,17 @@ struct eata_info {
 struct eata_config {
    ushort len;          /* Number of bytes following this field */
    unchar edis:1,       /* Disable EATA interface after config command */
-        ocena:1,       /* Overlapped Commands Enabled */
-       mdpena:1,       /* Transfer all Modified Data Pointer Messages */
-       tarena:1,       /* Target Mode Enabled for this controller */
-             :4;
+         ocena:1,       /* Overlapped Commands Enabled */
+        mdpena:1,       /* Transfer all Modified Data Pointer Messages */
+        tarena:1,       /* Target Mode Enabled for this controller */
+              :4;
    unchar cpad[511];
    };
 
 /* Returned status packet structure */
 struct mssp {
    unchar adapter_status:7,    /* State related to current command */
-                    eoc:1;    /* End Of Command (1 = command completed) */
+                     eoc:1;    /* End Of Command (1 = command completed) */
    unchar target_status;       /* SCSI status received after data transfer */
    unchar unused[2];
    ulong inv_res_len;          /* Number of bytes not transferred */
@@ -489,13 +494,13 @@ struct sg_list {
 /* MailBox SCSI Command Packet */
 struct mscp {
    unchar  sreset:1,     /* SCSI Bus Reset Signal should be asserted */
-            init:1,     /* Re-initialize controller and self test */
-          reqsen:1,     /* Transfer Request Sense Data to addr using DMA */
-              sg:1,     /* Use Scatter/Gather */
-                :1,
-          interp:1,     /* The controller interprets cp, not the target */ 
-            dout:1,     /* Direction of Transfer is Out (Host to Target) */
-             din:1;     /* Direction of Transfer is In (Target to Host) */
+             init:1,     /* Re-initialize controller and self test */
+           reqsen:1,     /* Transfer Request Sense Data to addr using DMA */
+               sg:1,     /* Use Scatter/Gather */
+                 :1,
+           interp:1,     /* The controller interprets cp, not the target */ 
+             dout:1,     /* Direction of Transfer is Out (Host to Target) */
+              din:1;     /* Direction of Transfer is In (Target to Host) */
    unchar sense_len;     /* Request Sense Length */
    unchar unused[3];
    unchar  fwnest:1,     /* Send command to a component of an Array Group */
@@ -507,9 +512,9 @@ struct mscp {
    unchar  target:5,     /* SCSI target ID */
           channel:3;     /* SCSI channel number */
    unchar     lun:5,     /* SCSI logical unit number */
-          luntar:1,     /* This cp is for Target (not LUN) */
-          dispri:1,     /* Disconnect Privilege granted */
-             one:1;     /* 1 */
+           luntar:1,     /* This cp is for Target (not LUN) */
+           dispri:1,     /* Disconnect Privilege granted */
+              one:1;     /* 1 */
    unchar mess[3];       /* Massage to/from Target */
    unchar cdb[12];       /* Command Descriptor Block */
    ulong  data_len;      /* If sg=0 Data Length, if sg=1 sglist length */
@@ -526,7 +531,6 @@ struct hostdata {
    unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
    unsigned int last_cp_used;           /* Index of last mailbox used */
    unsigned int iocount;                /* Total i/o done for this board */
-   unsigned int multicount;             /* Total ... in second ihdlr loop */
    int board_number;                    /* Number of this board */
    char board_name[16];                 /* Name of this board */
    char board_id[256];                  /* data from INQUIRY on this board */
@@ -542,9 +546,12 @@ struct hostdata {
 
 static struct Scsi_Host *sh[MAX_BOARDS + 1];
 static const char *driver_name = "EATA";
-static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
+static char sha[MAX_BOARDS];
 
-static unsigned int io_port[] __initdata = { 
+/* Initialize num_boards so that ihdlr can work while detect is in progress */
+static unsigned int num_boards = MAX_BOARDS;
+
+static unsigned long io_port[] __initdata = { 
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -665,7 +672,7 @@ static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
    return;
 }
 
-static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
+static inline int wait_on_busy(unsigned long iobase, unsigned int loop) {
 
    while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED) {
       udelay(1L);
@@ -675,7 +682,7 @@ static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
    return FALSE;
 }
 
-static inline int do_dma(unsigned int iobase, unsigned int addr, unchar cmd) {
+static inline int do_dma(unsigned long iobase, unsigned int addr, unchar cmd) {
 
    if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP))) return TRUE;
 
@@ -690,7 +697,7 @@ static inline int do_dma(unsigned int iobase, unsigned int addr, unchar cmd) {
    return FALSE;
 }
 
-static inline int read_pio(unsigned int iobase, ushort *start, ushort *end) {
+static inline int read_pio(unsigned long iobase, ushort *start, ushort *end) {
    unsigned int loop = MAXLOOP;
    ushort *p;
 
@@ -698,7 +705,7 @@ static inline int read_pio(unsigned int iobase, ushort *start, ushort *end) {
 
       while (!(inb(iobase + REG_STATUS) & DRQ_ASSERTED)) {
          udelay(1L);
-        if (--loop == 0) return TRUE;
+         if (--loop == 0) return TRUE;
          }
 
       loop = MAXLOOP;
@@ -709,7 +716,7 @@ static inline int read_pio(unsigned int iobase, ushort *start, ushort *end) {
 }
 
 __initfunc (static inline int port_detect \
-      (unsigned int port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
+      (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char protocol_rev;
    struct eata_info info;
@@ -723,7 +730,7 @@ __initfunc (static inline int port_detect \
    sprintf(name, "%s%d", driver_name, j);
 
    if(check_region(port_base, REGION_SIZE)) {
-      printk("%s: address 0x%03x in use, skipping probe.\n", name, port_base);
+      printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
       return FALSE;
       }
 
@@ -738,7 +745,7 @@ __initfunc (static inline int port_detect \
 
    if (DEV2H(info.data_len) < EATA_2_0A_SIZE) {
       printk("%s: config structure size (%ld bytes) too short, detaching.\n", 
-            name, DEV2H(info.data_len));
+             name, DEV2H(info.data_len));
       return FALSE;
       }
    else if (DEV2H(info.data_len) == EATA_2_0A_SIZE)
@@ -774,7 +781,7 @@ __initfunc (static inline int port_detect \
       }
 
    if (!info.haaval || info.ata) {
-      printk("%s: address 0x%03x, unusable %s board (%d%d), detaching.\n",
+      printk("%s: address 0x%03lx, unusable %s board (%d%d), detaching.\n",
              name, port_base, bus_type, info.haaval, info.ata);
       return FALSE;
       }
@@ -782,7 +789,7 @@ __initfunc (static inline int port_detect \
    if (info.drqvld) {
 
       if (subversion ==  ESA)
-        printk("%s: warning, weird %s board using DMA.\n", name, bus_type);
+         printk("%s: warning, weird %s board using DMA.\n", name, bus_type);
 
       subversion = ISA;
       dma_channel = dma_channel_table[3 - info.drqx];
@@ -790,7 +797,7 @@ __initfunc (static inline int port_detect \
    else {
 
       if (subversion ==  ISA)
-        printk("%s: warning, weird %s board not using DMA.\n", name, bus_type);
+         printk("%s: warning, weird %s board not using DMA.\n", name, bus_type);
 
       subversion = ESA;
       dma_channel = NO_DMA;
@@ -803,19 +810,20 @@ __initfunc (static inline int port_detect \
 
    if (subversion == ESA && !info.irq_tr)
       printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
-            name, irq);
+             name, irq);
 
-   /* Board detected, allocate its IRQ if not already done */
-   if ((irq >= MAX_IRQ) || (!irqlist[irq] && request_irq(irq,
-              eata2x_interrupt_handler, SA_INTERRUPT, driver_name, NULL))) {
+   /* Board detected, allocate its IRQ */
+   if (request_irq(irq, eata2x_interrupt_handler,
+             SA_INTERRUPT | ((subversion == ESA) ? SA_SHIRQ : 0),
+             driver_name, (void *) &sha[j])) {
       printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
       return FALSE;
       }
 
    if (subversion == ISA && request_dma(dma_channel, driver_name)) {
       printk("%s: unable to allocate DMA channel %u, detaching.\n",
-            name, dma_channel);
-      free_irq(irq, NULL);
+             name, dma_channel);
+      free_irq(irq, &sha[j]);
       return FALSE;
       }
 
@@ -840,7 +848,7 @@ __initfunc (static inline int port_detect \
    if (sh[j] == NULL) {
       printk("%s: unable to register host, detaching.\n", name);
 
-      if (!irqlist[irq]) free_irq(irq, NULL);
+      free_irq(irq, &sha[j]);
 
       if (subversion == ISA) free_dma(dma_channel);
 
@@ -865,7 +873,6 @@ __initfunc (static inline int port_detect \
    HD(j)->subversion = subversion;
    HD(j)->protocol_rev = protocol_rev;
    HD(j)->board_number = j;
-   irqlist[irq]++;
 
    if (HD(j)->subversion == ESA)
       sh[j]->unchecked_isa_dma = FALSE;
@@ -937,10 +944,11 @@ __initfunc (static inline int port_detect \
       }
    else                                 tag_type = 'n';
 
-   printk("%s: 2.0%c, %s 0x%03x, IRQ %u, %s, SG %d, MB %d, tc:%c, lc:%c, "\
-          "mq:%d.\n", BN(j), HD(j)->protocol_rev, bus_type, sh[j]->io_port,
-          sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
-          tag_type, YESNO(linked_comm), max_queue_depth);
+   printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d, tc:%c, lc:%c, "\
+          "mq:%d.\n", BN(j), HD(j)->protocol_rev, bus_type,
+          (unsigned long)sh[j]->io_port, sh[j]->irq, dma_name,
+          sh[j]->sg_tablesize, sh[j]->can_queue, tag_type, YESNO(linked_comm),
+          max_queue_depth);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -1056,11 +1064,6 @@ __initfunc (int eata2x_detect(Scsi_Host_Template *tpnt)) {
       }
 #endif
 
-   for (k = 0; k < MAX_IRQ; k++) {
-      irqlist[k] = 0;
-      calls[k] = 0;
-      }
-
    for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
 
    if (!setup_done) add_pci_ports();
@@ -1073,8 +1076,9 @@ __initfunc (int eata2x_detect(Scsi_Host_Template *tpnt)) {
       }
 
    if (j > 0) 
-      printk("EATA/DMA 2.0x: Copyright (C) 1994-1997 Dario Ballabio.\n");
+      printk("EATA/DMA 2.0x: Copyright (C) 1994-1998 Dario Ballabio.\n");
 
+   num_boards = j;
    restore_flags(flags);
    return j;
 }
@@ -1138,26 +1142,26 @@ int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
       if (i >= sh[j]->can_queue) i = 0;
 
       if (HD(j)->cp_stat[i] == FREE) {
-        HD(j)->last_cp_used = i;
-        break;
-        }
+         HD(j)->last_cp_used = i;
+         break;
+         }
       }
 
    if (k == sh[j]->can_queue) {
       printk("%s: qcomm, no free mailbox, resetting.\n", BN(j));
 
       if (HD(j)->in_reset) 
-        printk("%s: qcomm, already in reset.\n", BN(j));
+         printk("%s: qcomm, already in reset.\n", BN(j));
       else if (eata2x_reset(SCpnt, SCSI_RESET_SUGGEST_BUS_RESET)
                == SCSI_RESET_SUCCESS) 
-        panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
+         panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
 
       SCpnt->result = DID_BUS_BUSY << 16; 
       SCpnt->host_scribble = NULL;
       printk("%s: qcomm, pid %ld, DID_BUS_BUSY, done.\n", BN(j), SCpnt->pid);
       restore_flags(flags);
       done(SCpnt);    
-      return 0;
+      return 1;
       }
 
    /* Set pointer to control packet structure */
@@ -1178,21 +1182,21 @@ int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    SCpnt->host_scribble = (unsigned char *) &cpp->index;
 
    if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
-                       BN(j), i, SCpnt->channel, SCpnt->target,
+                        BN(j), i, SCpnt->channel, SCpnt->target,
                         SCpnt->lun, SCpnt->pid);
 
    for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
      if (SCpnt->cmnd[0] == data_out_cmds[k]) {
-       cpp->dout = TRUE;
-       break;
-       }
+        cpp->dout = TRUE;
+        break;
+        }
 
    if ((cpp->din = !cpp->dout))
       for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
         if (SCpnt->cmnd[0] == data_none_cmds[k]) {
-          cpp->din = FALSE;
-          break;
-          }
+           cpp->din = FALSE;
+           break;
+           }
 
    cpp->reqsen = TRUE;
    cpp->dispri = TRUE;
@@ -1253,7 +1257,7 @@ int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
              SCpnt->pid);
       restore_flags(flags);
       done(SCpnt);    
-      return 0;
+      return 1;
       }
 
    HD(j)->cp_stat[i] = IN_USE;
@@ -1272,14 +1276,14 @@ int eata2x_abort(Scsi_Cmnd *SCarg) {
    if (SCarg->host_scribble == NULL
        || SCarg->serial_number != SCarg->serial_number_at_timeout) {
       printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
-            BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+             BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
       restore_flags(flags);
       return SCSI_ABORT_NOT_RUNNING;
       }
 
    i = *(unsigned int *)SCarg->host_scribble;
    printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n", 
-         BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+          BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
 
    if (i >= sh[j]->can_queue)
       panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
@@ -1300,8 +1304,8 @@ int eata2x_abort(Scsi_Cmnd *SCarg) {
       printk("%s: abort, mbox %d is in use.\n", BN(j), i);
 
       if (SCarg != HD(j)->cp[i].SCpnt)
-        panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
-              BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+         panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+               BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
 
       if (inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)
          printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
@@ -1327,7 +1331,7 @@ int eata2x_abort(Scsi_Cmnd *SCarg) {
       SCarg->host_scribble = NULL;
       HD(j)->cp_stat[i] = FREE;
       printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
-            BN(j), i, SCarg->pid);
+             BN(j), i, SCarg->pid);
       SCarg->scsi_done(SCarg);
       restore_flags(flags);
       return SCSI_ABORT_SUCCESS;
@@ -1347,7 +1351,7 @@ int eata2x_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
    cli();
    j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
    printk("%s: reset, enter, target %d.%d:%d, pid %ld, reset_flags %u.\n", 
-         BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
+          BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
           reset_flags);
 
    if (SCarg->host_scribble == NULL)
@@ -1384,13 +1388,13 @@ int eata2x_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
       if (HD(j)->cp_stat[i] == FREE) continue;
 
       if (HD(j)->cp_stat[i] == LOCKED) {
-        HD(j)->cp_stat[i] = FREE;
-        printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
-        continue;
-        }
+         HD(j)->cp_stat[i] = FREE;
+         printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+         continue;
+         }
 
       if (!(SCpnt = HD(j)->cp[i].SCpnt))
-        panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+         panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
 
       if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
          HD(j)->cp_stat[i] = ABORTING;
@@ -1405,13 +1409,13 @@ int eata2x_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
          }
 
       if (SCpnt->host_scribble == NULL)
-        panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+         panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
 
       if (*(unsigned int *)SCpnt->host_scribble != i) 
-        panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+         panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
 
       if (SCpnt->scsi_done == NULL) 
-        panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+         panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
 
       if (SCpnt == SCarg) arg_done = TRUE;
       }
@@ -1446,7 +1450,7 @@ int eata2x_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
          HD(j)->cp_stat[i] = LOCKED;
 
          printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
-               BN(j), i, SCpnt->pid);
+                BN(j), i, SCpnt->pid);
          }
 
       else if (HD(j)->cp_stat[i] == ABORTING) {
@@ -1458,7 +1462,7 @@ int eata2x_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
          HD(j)->cp_stat[i] = FREE;
 
          printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
-               BN(j), i, SCpnt->pid);
+                BN(j), i, SCpnt->pid);
          }
 
       else
@@ -1640,229 +1644,200 @@ static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
 
 }
 
-static void eata2x_interrupt_handler(int irq, void *dev_id,
+static void eata2x_interrupt_handler(int irq, void *shap,
                                      struct pt_regs *regs) {
    Scsi_Cmnd *SCpnt;
-   unsigned long flags;
-   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, reg;
+   unsigned int i, j, k, c, status, tstatus, reg;
+   unsigned int n, n_ready, il[MAX_MAILBOXES];
    struct mssp *spp;
    struct mscp *cpp;
 
-   save_flags(flags);
-   cli();
+   /* Check if the interrupt must be processed by this handler */
+   if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
+   if (sh[j]->irq != irq)
+       panic("%s: ihdlr, irq %d, sh[j]->irq %d.\n", BN(j), irq, sh[j]->irq);
+   if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
+                        HD(j)->iocount);
+   /* Check if this board need to be serviced */
+   if (!(inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)) return;
 
-   if (!irqlist[irq]) {
-      printk("%s, ihdlr, irq %d, unexpected interrupt.\n", driver_name, irq);
-      restore_flags(flags);
-      return;
-      }
+   n_ready = 0;
 
-   if (do_trace) printk("%s: ihdlr, enter, irq %d, calls %d.\n", 
-                       driver_name, irq, calls[irq]);
+   /* Find the mailboxes to be serviced on this board */
+   for (i = 0; i < sh[j]->can_queue; i++) {
+      spp = &HD(j)->sp[i];
 
-   /* Service all the boards configured on this irq */
-   for (j = 0; sh[j] != NULL; j++) {
+      /* Check if this mailbox has completed the operation */
+      if (spp->eoc == FALSE) continue;
 
-      if (sh[j]->irq != irq) continue;
+      spp->eoc = FALSE;
+      il[n_ready++] = i;
+      }
 
-      loops = 0;
+   /* Read the status register to clear the interrupt indication */
+   reg = inb(sh[j]->io_port + REG_STATUS);
 
-      /* Loop until all interrupts for a board are serviced */
-      while (inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED) {
-        total_loops++;
-        loops++;
+   /* Mailbox service loop */
+   for (n = 0; n < n_ready; n++) {
+      i = il[n];
+      spp = &HD(j)->sp[i];
 
-        if (do_trace) printk("%s: ihdlr, start service, count %d.\n",
-                             BN(j), HD(j)->iocount);
-   
-        /* Read the status register to clear the interrupt indication */
-        reg = inb(sh[j]->io_port + REG_STATUS);
-   
-        /* Service all mailboxes of this board */
-        for (i = 0; i < sh[j]->can_queue; i++) {
-           spp = &HD(j)->sp[i];
-   
-           /* Check if this mailbox has completed the operation */
-           if (spp->eoc == FALSE) continue;
-   
-           spp->eoc = FALSE;
-   
-           if (HD(j)->cp_stat[i] == IGNORE) {
-              HD(j)->cp_stat[i] = FREE;
-              continue;
-              }
-           else if (HD(j)->cp_stat[i] == LOCKED) {
-              HD(j)->cp_stat[i] = FREE;
-              printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
-                     BN(j), i, HD(j)->iocount);
-              continue;
-              }
-           else if (HD(j)->cp_stat[i] == FREE) {
-              printk("%s: ihdlr, mbox %d is free, count %d.\n", 
-                     BN(j), i, HD(j)->iocount);
-              continue;
-              }
-           else if (HD(j)->cp_stat[i] == IN_RESET)
-              printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
-           else if (HD(j)->cp_stat[i] != IN_USE) 
-              panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
-   
-           HD(j)->cp_stat[i] = FREE;
-           cpp = &HD(j)->cp[i];
-           SCpnt = spp->SCpnt;
-   
-           if (SCpnt == NULL)
-              panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
-   
-           if (SCpnt != cpp->SCpnt)
-              panic("%s: ihdlr, mbox %d, sp SCpnt %p, cp SCpnt %p.\n",
-                    BN(j), i, SCpnt, cpp->SCpnt);
-   
-           if (SCpnt->host_scribble == NULL)
-              panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n",
-                    BN(j), i, SCpnt->pid, SCpnt);
-   
-           if (*(unsigned int *)SCpnt->host_scribble != i) 
-              panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d,"\
-                    " irq %d.\n", BN(j), i, SCpnt->pid, 
-                    *(unsigned int *)SCpnt->host_scribble, irq);
-   
-         if (linked_comm && SCpnt->device->queue_depth > 2
-                                           && TLDEV(SCpnt->device->type))
-            flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+      if (HD(j)->cp_stat[i] == IGNORE) {
+         HD(j)->cp_stat[i] = FREE;
+         return;
+         }
+      else if (HD(j)->cp_stat[i] == LOCKED) {
+         HD(j)->cp_stat[i] = FREE;
+         printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
+                HD(j)->iocount);
+         return;
+         }
+      else if (HD(j)->cp_stat[i] == FREE) {
+         printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
+                HD(j)->iocount);
+         return;
+         }
+      else if (HD(j)->cp_stat[i] == IN_RESET)
+         printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+      else if (HD(j)->cp_stat[i] != IN_USE) 
+         panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
 
-           tstatus = status_byte(spp->target_status);
-   
-           switch (spp->adapter_status) {
-              case ASOK:     /* status OK */
-   
-                 /* Forces a reset if a disk drive keeps returning BUSY */
-                 if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE) 
-                    status = DID_ERROR << 16;
-   
-                 /* If there was a bus reset, redo operation on each target */
-                 else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
-                          && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
-                    status = DID_BUS_BUSY << 16;
-   
-                 /* Works around a flaw in scsi.c */
-                 else if (tstatus == CHECK_CONDITION
-                          && SCpnt->device->type == TYPE_DISK
-                          && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
-                    status = DID_BUS_BUSY << 16;
-
-                 else
-                    status = DID_OK << 16;
-   
-                 if (tstatus == GOOD)
-                    HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
-   
-                 if (spp->target_status && SCpnt->device->type == TYPE_DISK)
-                    printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
-                            "target_status 0x%x, sense key 0x%x.\n", BN(j), 
-                           SCpnt->channel, SCpnt->target, SCpnt->lun, 
-                            SCpnt->pid, spp->target_status, 
-                            SCpnt->sense_buffer[2]);
-   
-                 HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
-   
-                  if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
+      HD(j)->cp_stat[i] = FREE;
+      cpp = &HD(j)->cp[i];
+      SCpnt = spp->SCpnt;
 
-                 break;
-              case ASST:     /* Selection Time Out */
-              case 0x02:     /* Command Time Out   */
-   
-                 if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
-                    status = DID_ERROR << 16;
-                 else {
-                    status = DID_TIME_OUT << 16;
-                    HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
-                    }
-   
-                 break;
+      if(SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
 
-               /* Perform a limited number of internal retries */
-              case 0x03:     /* SCSI Bus Reset Received */
-              case 0x04:     /* Initial Controller Power-up */
-   
-                 for (c = 0; c <= sh[j]->max_channel; c++) 
-                    for (k = 0; k < sh[j]->max_id; k++) 
-                       HD(j)->target_redo[k][c] = TRUE;
-   
-                 if (SCpnt->device->type != TYPE_TAPE
-                      && HD(j)->retries < MAX_INTERNAL_RETRIES) {
-                    status = DID_BUS_BUSY << 16;
-                    HD(j)->retries++;
-                     HD(j)->last_retried_pid = SCpnt->pid;
-                     }
-                 else 
-                    status = DID_ERROR << 16;
-
-                 break;
-              case 0x05:     /* Unexpected Bus Phase */
-              case 0x06:     /* Unexpected Bus Free */
-              case 0x07:     /* Bus Parity Error */
-              case 0x08:     /* SCSI Hung */
-              case 0x09:     /* Unexpected Message Reject */
-              case 0x0a:     /* SCSI Bus Reset Stuck */
-              case 0x0b:     /* Auto Request-Sense Failed */
-              case 0x0c:     /* Controller Ram Parity Error */
-              default:
-                 status = DID_ERROR << 16;
-                 break;
-              }
-   
-           SCpnt->result = status | spp->target_status;
-           HD(j)->iocount++;
+      if (SCpnt != cpp->SCpnt)
+         panic("%s: ihdlr, mbox %d, sp SCpnt %p, cp SCpnt %p.\n", BN(j), i,
+               SCpnt, cpp->SCpnt);
 
-           if (loops > 1) HD(j)->multicount++;
+      if (SCpnt->host_scribble == NULL)
+         panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
+               SCpnt->pid, SCpnt);
+
+      if (*(unsigned int *)SCpnt->host_scribble != i) 
+         panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d, irq %d.\n",
+              BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble, irq);
+
+      if (linked_comm && SCpnt->device->queue_depth > 2
+                                        && TLDEV(SCpnt->device->type))
+      flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+
+      tstatus = status_byte(spp->target_status);
+
+      switch (spp->adapter_status) {
+         case ASOK:     /* status OK */
+
+            /* Forces a reset if a disk drive keeps returning BUSY */
+            if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE) 
+               status = DID_ERROR << 16;
+
+            /* If there was a bus reset, redo operation on each target */
+            else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+                     && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
+               status = DID_BUS_BUSY << 16;
+
+            /* Works around a flaw in scsi.c */
+            else if (tstatus == CHECK_CONDITION
+                     && SCpnt->device->type == TYPE_DISK
+                     && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+               status = DID_BUS_BUSY << 16;
+
+            else
+               status = DID_OK << 16;
+
+            if (tstatus == GOOD)
+               HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
+
+            if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+               printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
+                      "target_status 0x%x, sense key 0x%x.\n", BN(j), 
+                      SCpnt->channel, SCpnt->target, SCpnt->lun, 
+                      SCpnt->pid, spp->target_status, 
+                      SCpnt->sense_buffer[2]);
+
+            HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
+
+            if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
+
+            break;
+         case ASST:     /* Selection Time Out */
+         case 0x02:     /* Command Time Out   */
+
+            if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
+               status = DID_ERROR << 16;
+            else {
+               status = DID_TIME_OUT << 16;
+               HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
+               }
+
+            break;
+
+         /* Perform a limited number of internal retries */
+         case 0x03:     /* SCSI Bus Reset Received */
+         case 0x04:     /* Initial Controller Power-up */
+
+            for (c = 0; c <= sh[j]->max_channel; c++) 
+               for (k = 0; k < sh[j]->max_id; k++) 
+                  HD(j)->target_redo[k][c] = TRUE;
+
+            if (SCpnt->device->type != TYPE_TAPE
+                && HD(j)->retries < MAX_INTERNAL_RETRIES) {
+               status = DID_BUS_BUSY << 16;
+               HD(j)->retries++;
+               HD(j)->last_retried_pid = SCpnt->pid;
+               }
+            else 
+               status = DID_ERROR << 16;
+
+            break;
+         case 0x05:     /* Unexpected Bus Phase */
+         case 0x06:     /* Unexpected Bus Free */
+         case 0x07:     /* Bus Parity Error */
+         case 0x08:     /* SCSI Hung */
+         case 0x09:     /* Unexpected Message Reject */
+         case 0x0a:     /* SCSI Bus Reset Stuck */
+         case 0x0b:     /* Auto Request-Sense Failed */
+         case 0x0c:     /* Controller Ram Parity Error */
+         default:
+            status = DID_ERROR << 16;
+            break;
+         }
+
+      SCpnt->result = status | spp->target_status;
+      HD(j)->iocount++;
 
 #if defined (DEBUG_INTERRUPT)
-           if (SCpnt->result || do_trace)
+      if (SCpnt->result || do_trace)
 #else
-           if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
-               (spp->adapter_status != ASOK && 
-               spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
-               do_trace || msg_byte(spp->target_status))
+      if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
+          (spp->adapter_status != ASOK && 
+          spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+          do_trace || msg_byte(spp->target_status))
 #endif
-              printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
-                     " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
-                     BN(j), i, spp->adapter_status, spp->target_status,
-                     SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                      reg, HD(j)->iocount);
-   
-           /* Set the command state to inactive */
-           SCpnt->host_scribble = NULL;
-   
-           restore_flags(flags);
-           SCpnt->scsi_done(SCpnt);
-           cli();
-
-           }   /* Mailbox loop */
+         printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
+                " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+                BN(j), i, spp->adapter_status, spp->target_status,
+                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
+                reg, HD(j)->iocount);
 
-        }   /* Multiple command loop */
-
-      }   /* Boards loop */
+      /* Set the command state to inactive */
+      SCpnt->host_scribble = NULL;
 
-   calls[irq]++;
+      SCpnt->scsi_done(SCpnt);
 
-#if defined (DEBUG_SMP)
-   if (total_loops == 0) 
-     printk("%s: ihdlr, irq %d, no command completed, calls %d.\n",
-           driver_name, irq, calls[irq]);
-#endif
+      }   /* Mailbox loop */
 
-   if (do_trace) printk("%s: ihdlr, exit, irq %d, calls %d.\n", 
-                       driver_name, irq, calls[irq]);
+   if (n_ready > 1)
+      printk("%s: ihdlr, multiple commands (%d) completed.\n", BN(j), n_ready);
 
-#if defined (DEBUG_STATISTICS)
-   if ((calls[irq] % 100000) == 10000)
-      for (j = 0; sh[j] != NULL; j++)
-        printk("%s: ihdlr, calls %d, count %d, multi %d.\n", BN(j),
-               calls[(sh[j]->irq)], HD(j)->iocount, HD(j)->multicount);
-#endif
+   if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq,
+                        HD(j)->iocount);
 
-   restore_flags(flags);
    return;
 }
 
@@ -1881,7 +1856,7 @@ int eata2x_release(struct Scsi_Host *shpnt) {
    for (i = 0; i < sh[j]->can_queue; i++) 
       if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
 
-   if (! --irqlist[sh[j]->irq]) free_irq(sh[j]->irq, NULL);
+   free_irq(sh[j]->irq, &sha[j]);
 
    if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel);
 
index 8292ef5148065053973471a87861f886f9ab1fc2..3322a5eb512e9967b380af1c857ad3b92202f358 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *     eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
+ *        eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
  */
 #ifndef _EATA_H
 #define _EATA_H
@@ -12,30 +12,41 @@ int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int eata2x_abort(Scsi_Cmnd *);
 int eata2x_reset(Scsi_Cmnd *, unsigned int);
 
-#define EATA_VERSION "3.11.00"
-
-
-#define EATA {                                                 \
-               NULL, /* Ptr for modules */                    \
-               NULL, /* usage count for modules */            \
-               NULL,                                          \
-               NULL,                                          \
-               "EATA/DMA 2.0x rev. " EATA_VERSION " ",        \
-               eata2x_detect,                                 \
-               eata2x_release,                                \
-               NULL,                                          \
-               NULL,                                          \
-               eata2x_queuecommand,                           \
-               eata2x_abort,                                  \
-               eata2x_reset,                                  \
-               NULL,                                          \
-               scsicam_bios_param,                            \
-               0,   /* can_queue, reset by detect */          \
-               7,   /* this_id, reset by detect */            \
-               0,   /* sg_tablesize, reset by detect */       \
-               0,   /* cmd_per_lun, reset by detect */        \
-               0,   /* number of boards present */            \
-               1,   /* unchecked isa dma, reset by detect */  \
-               ENABLE_CLUSTERING                              \
-               }
+#define EATA_VERSION "4.02.00"
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,88)
+
+#define EATA {                                                               \
+                name:              "EATA/DMA 2.0x rev. " EATA_VERSION " ",   \
+                detect:            eata2x_detect,                            \
+                release:           eata2x_release,                           \
+                queuecommand:      eata2x_queuecommand,                      \
+                abort:             eata2x_abort,                             \
+                reset:             eata2x_reset,                             \
+                bios_param:        scsicam_bios_param,                       \
+                this_id:           7,                                        \
+                unchecked_isa_dma: 1,                                        \
+                use_clustering:    ENABLE_CLUSTERING,                        \
+                use_new_eh_code: 1    /* Enable new error code */            \
+             }
+
+#else /* Use old scsi code */
+
+#define EATA {                                                               \
+                name:              "EATA/DMA 2.0x rev. " EATA_VERSION " ",   \
+                detect:            eata2x_detect,                            \
+                release:           eata2x_release,                           \
+                queuecommand:      eata2x_queuecommand,                      \
+                abort:             eata2x_abort,                             \
+                reset:             eata2x_reset,                             \
+                bios_param:        scsicam_bios_param,                       \
+                this_id:           7,                                        \
+                unchecked_isa_dma: 1,                                        \
+                use_clustering:    ENABLE_CLUSTERING                         \
+             }
+
+#endif
+
 #endif
index d9179494f4c48d76958244a05ea67116ee1bd618..eafc5d3c41c39cfa523b42dbd34cdda54df89a8e 100644 (file)
 **  May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
 **     Support for NvRAM detection and reading.
 **
+**  August 18 1997 by Cort <cort@cs.nmt.edu>:
+**     Support for Power/PC (Big Endian).
+**
 *******************************************************************************
 */
 
 /*
-**     21 August 1997, version 2.4a
+**     2 January 1998, version 2.5f
 **
 **     Supported SCSI-II features:
 **         Synchronous negotiation
@@ -306,6 +309,7 @@ typedef     int             vm_size_t;
 **     architecture.
 */
 
+#ifndef NCR_IOMAPPED
 __initfunc(
 static vm_offset_t remap_pci_mem(u_long base, u_long size)
 )
@@ -332,6 +336,7 @@ static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
                vfree((void *) (vaddr & PAGE_MASK));
 #endif
 }
+#endif /* !NCR_IOMAPPED */
 
 #else /* linux-1.2.13 */
 
@@ -452,9 +457,9 @@ static int guess_xfer_direction(int opcode);
 /*
 **     Head of list of NCR boards
 **
-**     Host is retrieved by its irq level.
-**     If interrupts are shared, the internal host control block 
-**     address (struct ncb) is used as device id.
+**     For kernel version < 1.3.70, host is retrieved by its irq level.
+**     For later kernels, the internal host control block address 
+**     (struct ncb) is used as device id parameter of the irq stuff.
 */
 
 static struct Scsi_Host                *first_host     = NULL;
@@ -499,7 +504,7 @@ struct ncr_driver_setup {
        unsigned master_parity  : 1;
        unsigned scsi_parity    : 1;
        unsigned disconnection  : 1;
-       unsigned special_features : 1;
+       unsigned special_features : 2;
        unsigned ultra_scsi     : 2;
        unsigned force_sync_nego: 1;
        unsigned reverse_probe: 1;
@@ -515,6 +520,7 @@ struct ncr_driver_setup {
        u_char  settle_delay;
        u_char  diff_support;
        u_char  irqm;
+       u_char  bus_check;
 };
 
 static struct ncr_driver_setup
@@ -523,6 +529,9 @@ static struct ncr_driver_setup
 #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
 static struct ncr_driver_setup
        driver_safe_setup __initdata    = SCSI_NCR_DRIVER_SAFE_SETUP;
+#ifdef MODULE
+char *ncr53c8xx = 0;   /* command line passed by insmod */
+#endif
 #endif
 
 /*
@@ -570,7 +579,8 @@ struct Symbios_nvram {
        u_short flags1;
 #define SYMBIOS_SCAN_HI_LO     (1)
        u_short word10;         /* 0x00 */
-       u_short word12;         /* 0x00 */
+       u_short flags3;         /* 0x00 */
+#define SYMBIOS_REMOVABLE_FLAGS        (3)             /* 0=none, 1=bootable, 2=all */
        u_char  host_id;
        u_char  byte15;         /* 0x04 */
        u_short word16;         /* 0x0410 */
@@ -657,6 +667,7 @@ typedef struct {
        int     bus;
        u_char  device_fn;
        u_int   base;
+       u_int   base_2;
        u_int   io_port;
        int     irq;
 /* port and reg fields to use INB, OUTB macros */
@@ -684,7 +695,7 @@ typedef struct {
        ncr_slot  slot;
        ncr_chip  chip;
        ncr_nvram *nvram;
-       int attached;
+       int attach_done;
 } ncr_device;
 
 /*==========================================================
@@ -743,79 +754,156 @@ typedef struct {
 
 /*==========================================================
 **
-**     Access to the controller chip.
-**
-**     If NCR_IOMAPPED is defined, only IO are used by the driver.
+**     Big/Little endian support.
 **
 **==========================================================
 */
 
 /*
-**     IO mapped only input / ouput
+**     If the NCR uses big endian addressing mode over the 
+**     PCI, actual io register addresses for byte and word 
+**     accesses must be changed according to lane routing.
+**     Btw, ncr_offb() and ncr_offw() macros only apply to 
+**     constants and so donnot generate bloated code.
 */
 
-#define        IOM_INB(r)              inb (np->port + offsetof(struct ncr_reg, r))
-#define        IOM_INB_OFF(o)          inb (np->port + (o))
-#define        IOM_INW(r)              inw (np->port + offsetof(struct ncr_reg, r))
-#define        IOM_INL(r)              inl (np->port + offsetof(struct ncr_reg, r))
-#define        IOM_INL_OFF(o)          inl (np->port + (o))
+#if    defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o)    (((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o)    (((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define ncr_offb(o)    (o)
+#define ncr_offw(o)    (o)
 
-#define        IOM_OUTB(r, val)        outb ((val), np->port+offsetof(struct ncr_reg,r))
-#define        IOM_OUTW(r, val)        outw ((val), np->port+offsetof(struct ncr_reg,r))
-#define        IOM_OUTL(r, val)        outl ((val), np->port+offsetof(struct ncr_reg,r))
-#define        IOM_OUTL_OFF(o, val)    outl ((val), np->port + (o))
+#endif
 
 /*
-**     MEMORY mapped IO input / output
+**     If the CPU and the NCR use same endian-ness adressing,
+**     no byte reordering is needed for script patching.
+**     Macro cpu_to_scr() is to be used for script patching.
+**     Macro scr_to_cpu() is to be used for getting a DWORD 
+**     from the script.
 */
 
-#define MMIO_INB(r)            readb(&np->reg->r)
-#define MMIO_INB_OFF(o)                readb((char *)np->reg + (o))
-#define MMIO_INW(r)            readw(&np->reg->r)
-#define MMIO_INL(r)            readl(&np->reg->r)
-#define MMIO_INL_OFF(o)                readl((char *)np->reg + (o))
+#if    defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+#elif  defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_be32(dw)
+#define scr_to_cpu(dw) be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw) (dw)
+#define scr_to_cpu(dw) (dw)
+
+#endif
 
-#define MMIO_OUTB(r, val)      writeb((val), &np->reg->r)
-#define MMIO_OUTW(r, val)      writew((val), &np->reg->r)
-#define MMIO_OUTL(r, val)      writel((val), &np->reg->r)
-#define MMIO_OUTL_OFF(o, val)  writel((val), (char *)np->reg + (o))
+/*==========================================================
+**
+**     Access to the controller chip.
+**
+**     If NCR_IOMAPPED is defined, only IO are used by the driver.
+**
+**==========================================================
+*/
 
 /*
-**     IO mapped input / output
+**     If the CPU and the NCR use same endian-ness adressing,
+**     no byte reordering is needed for accessing chip io 
+**     registers. Functions suffixed by '_raw' are assumed 
+**     to access the chip over the PCI without doing byte 
+**     reordering. Functions suffixed by '_l2b' are 
+**     assumed to perform little-endian to big-endian byte 
+**     reordering, those suffixed by '_b2l' blah, blah,
+**     blah, ...
 */
 
 #if defined(NCR_IOMAPPED)
 
-#define INB(r)             IOM_INB(r)
-#define INB_OFF(o)         IOM_INB_OFF(o)
-#define INW(r)             IOM_INW(r)
-#define INL(r)             IOM_INL(r)
-#define INL_OFF(o)         IOM_INL_OFF(o)
+/*
+**     IO mapped only input / ouput
+*/
+
+#define        INB_OFF(o)              inb (np->port + ncr_offb(o))
+#define        OUTB_OFF(o, val)        outb ((val), np->port + ncr_offb(o))
+
+#if    defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define        INW_OFF(o)              inw_l2b (np->port + ncr_offw(o))
+#define        INL_OFF(o)              inl_l2b (np->port + (o))
+
+#define        OUTW_OFF(o, val)        outw_b2l ((val), np->port + ncr_offw(o))
+#define        OUTL_OFF(o, val)        outl_b2l ((val), np->port + (o))
 
-#define OUTB(r, val)       IOM_OUTB(r, val)
-#define OUTW(r, val)       IOM_OUTW(r, val)
-#define OUTL(r, val)       IOM_OUTL(r, val)
-#define OUTL_OFF(o, val)   IOM_OUTL_OFF(o, val)
+#elif  defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define        INW_OFF(o)              inw_b2l (np->port + ncr_offw(o))
+#define        INL_OFF(o)              inl_b2l (np->port + (o))
+
+#define        OUTW_OFF(o, val)        outw_l2b ((val), np->port + ncr_offw(o))
+#define        OUTL_OFF(o, val)        outl_l2b ((val), np->port + (o))
+
+#else
+
+#define        INW_OFF(o)              inw_raw (np->port + ncr_offw(o))
+#define        INL_OFF(o)              inl_raw (np->port + (o))
+
+#define        OUTW_OFF(o, val)        outw_raw ((val), np->port + ncr_offw(o))
+#define        OUTL_OFF(o, val)        outl_raw ((val), np->port + (o))
+
+#endif /* ENDIANs */
+
+#else  /* defined NCR_IOMAPPED */
 
 /*
-**     MEMORY mapped only input / output
+**     MEMORY mapped IO input / output
 */
 
+#define INB_OFF(o)             readb((char *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val)       writeb((val), (char *)np->reg + ncr_offb(o))
+
+#if    defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o)             readw_l2b((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)             readl_l2b((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val)       writew_b2l((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)       writel_b2l((val), (char *)np->reg + (o))
+
+#elif  defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o)             readw_b2l((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)             readl_b2l((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val)       writew_l2b((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)       writel_l2b((val), (char *)np->reg + (o))
+
 #else
 
-#define INB(r)             MMIO_INB(r)
-#define INB_OFF(o)         MMIO_INB_OFF(o)
-#define INW(r)             MMIO_INW(r)
-#define INL(r)             MMIO_INL(r)
-#define INL_OFF(o)         MMIO_INL_OFF(o)
+#define INW_OFF(o)             readw_raw((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o)             readl_raw((char *)np->reg + (o))
 
-#define OUTB(r, val)       MMIO_OUTB(r, val)
-#define OUTW(r, val)       MMIO_OUTW(r, val)
-#define OUTL(r, val)       MMIO_OUTL(r, val)
-#define OUTL_OFF(o, val)   MMIO_OUTL_OFF(o, val)
+#define OUTW_OFF(o, val)       writew_raw((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val)       writel_raw((val), (char *)np->reg + (o))
 
 #endif
 
+#endif /* defined NCR_IOMAPPED */
+
+#define INB(r)         INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r)         INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r)         INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val)   OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val)   OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val)   OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
 /*
 **     Set bit field ON, OFF 
 */
@@ -827,6 +915,7 @@ typedef struct {
 #define OUTONL(r, m)   OUTL(r, INL(r) | (m))
 #define OUTOFFL(r, m)  OUTL(r, INL(r) & ~(m))
 
+
 /*==========================================================
 **
 **     Command control block states.
@@ -1264,27 +1353,36 @@ struct head {
        **      status fields.
        */
 
-       u_char          status[8];
+       u_char          scr_st[4];      /* script status */
+       u_char          status[4];      /* host status. Must be the last */
+                                       /* DWORD of the CCB header */
 };
 
 /*
 **     The status bytes are used by the host and the script processor.
 **
-**     The first four byte are copied to the scratchb register
+**     The byte corresponding to the host_status must be stored in the 
+**     last DWORD of the CCB header since it is used for command 
+**     completion (ncr_wakeup()). Doing so, we are sure that the header 
+**     has been entirely copied back to the CCB when the host_status is 
+**     seen complete by the CPU.
+**
+**     The last four bytes (status[4]) are copied to the scratchb register
 **     (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect,
 **     and copied back just after disconnecting.
 **     Inside the script the XX_REG are used.
 **
-**     The last four bytes are used inside the script by "COPY" commands.
+**     The first four bytes (scr_st[4]) are used inside the script by 
+**     "COPY" commands.
 **     Because source and destination must have the same alignment
-**     in a longword, the fields HAVE to be at the choosen offsets.
-**             xerr_st (4)     0       (0x34)  scratcha
-**             sync_st (5)     1       (0x05)  sxfer
-**             wide_st (7)     3       (0x03)  scntl3
+**     in a DWORD, the fields HAVE to be at the choosen offsets.
+**             xerr_st         0       (0x34)  scratcha
+**             sync_st         1       (0x05)  sxfer
+**             wide_st         3       (0x03)  scntl3
 */
 
 /*
-**     First four bytes (script)
+**     Last four bytes (script)
 */
 #define  QU_REG        scr0
 #define  HS_REG        scr1
@@ -1293,7 +1391,7 @@ struct head {
 #define  PS_REG        scr3
 
 /*
-**     First four bytes (host)
+**     Last four bytes (host)
 */
 #define  actualquirks  phys.header.status[0]
 #define  host_status   phys.header.status[1]
@@ -1301,15 +1399,15 @@ struct head {
 #define  parity_status phys.header.status[3]
 
 /*
-**     Last four bytes (script)
+**     First four bytes (script)
 */
-#define  xerr_st       header.status[4]        /* MUST be ==0 mod 4 */
-#define  sync_st       header.status[5]        /* MUST be ==1 mod 4 */
-#define  nego_st       header.status[6]
-#define  wide_st       header.status[7]        /* MUST be ==3 mod 4 */
+#define  xerr_st       header.scr_st[0]
+#define  sync_st       header.scr_st[1]
+#define  nego_st       header.scr_st[2]
+#define  wide_st       header.scr_st[3]
 
 /*
-**     Last four bytes (host)
+**     First four bytes (host)
 */
 #define  xerr_status   phys.xerr_st
 #define  sync_status   phys.sync_st
@@ -1677,9 +1775,11 @@ struct ncb {
        /*
        **      Timeout handler
        */
+#if 0
        u_long          heartbeat;
        u_short         ticks;
        u_short         latetime;
+#endif
        u_long          lasttime;
 
        /*-----------------------------------------------
@@ -1771,7 +1871,7 @@ struct ncb {
 **     of 825A, 875 and 895 chips.
 */
 struct script {
-       ncrcmd  start           [  7];
+       ncrcmd  start           [  4];
        ncrcmd  start0          [  2];
        ncrcmd  start1          [  3];
        ncrcmd  startpos        [  1];
@@ -1785,7 +1885,7 @@ struct script {
        ncrcmd  prepare2        [ 24];
        ncrcmd  setmsg          [  5];
        ncrcmd  clrack          [  2];
-       ncrcmd  dispatch        [ 33];
+       ncrcmd  dispatch        [ 38];
        ncrcmd  no_data         [ 17];
        ncrcmd  checkatn        [ 10];
        ncrcmd  command         [ 15];
@@ -1887,6 +1987,7 @@ static    int     ncr_snooptest   (ncb_p np);
 static void    ncr_timeout     (ncb_p np);
 static  void    ncr_wakeup      (ncb_p np, u_long code);
 static void    ncr_start_reset (ncb_p np, int settle_delay);
+static int     ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay);
 
 #ifdef SCSI_NCR_USER_COMMAND_SUPPORT
 static void    ncr_usercmd     (ncb_p np);
@@ -1917,16 +2018,6 @@ static   int     ncr_get_Tekram_nvram    (ncr_slot *np, Tekram_nvram *nvram);
 **==========================================================
 */
 
-#if 0
-static char ident[] =
-       "\n$Id: ncr.c,v 1.67 1996/03/11 19:36:07 se Exp $\n";
-static u_long  ncr_version = NCR_VERSION       * 11
-       + (u_long) sizeof (struct ncb)  *  7
-       + (u_long) sizeof (struct ccb)  *  5
-       + (u_long) sizeof (struct lcb)  *  3
-       + (u_long) sizeof (struct tcb)  *  2;
-#endif
-
 #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
 static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
 #endif
@@ -1984,13 +2075,14 @@ static void *script_kvars[] __initdata =
 
 static struct script script0 __initdata = {
 /*--------------------------< START >-----------------------*/ {
+#if 0
        /*
        **      Claim to be still alive ...
        */
        SCR_COPY (sizeof (((struct ncb *)0)->heartbeat)),
                KVAR(SCRIPT_KVAR_JIFFIES),
                NADDR (heartbeat),
-
+#endif
        /*
        **      Make data structure address invalid.
        **      clear SIGP.
@@ -2291,8 +2383,21 @@ static   struct script script0 __initdata = {
                0,
        SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)),
                0,
-       SCR_RETURN ^ IFTRUE (IF (SCR_DATA_IN)),
+       /*
+       **      DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 4.
+       **      Possible data corruption during Memory Write and Invalidate.
+       **      This work-around resets the addressing logic prior to the 
+       **      start of the first MOVE of a DATA IN phase.
+       **      (See README.ncr53c8xx for more information)
+       */
+       SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
+               20,
+       SCR_COPY (4),
+               RADDR (scratcha),
+               RADDR (scratcha),
+       SCR_RETURN,
                0,
+
        SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
                PADDR (msg_out),
        SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)),
@@ -3603,7 +3708,8 @@ static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int le
 
        while (src < end) {
 
-               *dst++ = opcode = *src++;
+               opcode = *src++;
+               *dst++ = cpu_to_scr(opcode);
 
                /*
                **      If we forget to change the length
@@ -3648,7 +3754,7 @@ static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int le
                        **      the NO FLUSH bit if present.
                        */
                        if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
-                               dst[-1] = (opcode & ~SCR_NO_FLUSH);
+                               dst[-1] = cpu_to_scr(opcode & ~SCR_NO_FLUSH);
                                ++opchanged;
                        }
                        break;
@@ -3721,10 +3827,10 @@ static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int le
                                        break;
                                }
 
-                               *dst++ = new;
+                               *dst++ = cpu_to_scr(new);
                        }
                } else
-                       *dst++ = *src++;
+                       *dst++ = cpu_to_scr(*src++);
 
        };
        if (bootverbose > 1 && opchanged)
@@ -3983,15 +4089,6 @@ static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
        period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
        np->maxsync = period > 2540 ? 254 : period / 10;
 
-       /*
-       **      Get on-board RAM bus address when supported
-       */
-       if (np->features & FE_RAM) {
-               OUTONB(nc_ctest2, 0x8);
-               np->paddr2 = INL(nc_scr0);
-               OUTOFFB(nc_ctest2, 0x8);
-       }
-
        /*
        **      Prepare initial value of other IO registers
        */
@@ -4348,7 +4445,8 @@ printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
        **      virtual and physical memory.
        */
 
-       np->paddr = device->slot.base;
+       np->paddr       = device->slot.base;
+       np->paddr2      = (np->features & FE_RAM)? device->slot.base_2 : 0;
 
 #ifndef NCR_IOMAPPED
        np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
@@ -4456,17 +4554,20 @@ printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
        */
 
        if (np->features & FE_LED0) {
-               np->script0->reselect[0]  = SCR_REG_REG(gpreg, SCR_OR,  0x01);
-               np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
-               np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+               np->script0->reselect[0]  =
+                               cpu_to_scr(SCR_REG_REG(gpreg, SCR_OR,  0x01));
+               np->script0->reselect1[0] =
+                               cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
+               np->script0->reselect2[0] =
+                               cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
        }
 
        /*
        **      init data structure
        */
 
-       np->jump_tcb.l_cmd      = SCR_JUMP;
-       np->jump_tcb.l_paddr    = NCB_SCRIPTH_PHYS (np, abort);
+       np->jump_tcb.l_cmd      = cpu_to_scr(SCR_JUMP);
+       np->jump_tcb.l_paddr    = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
 
        /*
        **      Reset chip.
@@ -4497,7 +4598,7 @@ printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
                        SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
 #else
        if (request_irq(device->slot.irq, ncr53c8xx_intr,
-                       SA_INTERRUPT, "ncr53c8xx", NULL)) {
+                       SA_INTERRUPT, "ncr53c8xx", np)) {
 #endif
 #else
        if (request_irq(device->slot.irq, ncr53c8xx_intr,
@@ -4517,7 +4618,11 @@ printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
        **      Then enable disconnects.
        */
        save_flags(flags); cli();
-       ncr_start_reset(np, driver_setup.settle_delay);
+       if (ncr_reset_scsi_bus(np, 0, driver_setup.settle_delay) != 0) {
+               printf("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, TERMINATION, DEVICE POWER etc.!\n", ncr_name(np));
+               restore_flags(flags);
+               goto attach_error;
+       }
        ncr_exception (np);
        restore_flags(flags);
 
@@ -4585,6 +4690,16 @@ attach_error:
 #endif
                release_region(np->port, 128);
        }
+       if (np->irq) {
+#ifdef DEBUG_NCR53C8XX
+               printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
+#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+               free_irq(np->irq, np);
+#else
+               free_irq(np->irq);
+#endif
+       }
        scsi_unregister(instance);
 
         return -1;
@@ -4654,7 +4769,9 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 
        /*---------------------------------------------------
        **
-       **      Assign a ccb / bind cmd
+       **      Assign a ccb / bind cmd.
+       **      If resetting, shorten settle_time if necessary
+       **      in order to avoid spurious timeouts.
        **      If resetting or no free ccb,
        **      insert cmd into the waiting list.
        **
@@ -4662,6 +4779,11 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        */
        save_flags(flags); cli();
 
+       if (np->settle_time && cmd->timeout_per_command >= HZ &&
+               np->settle_time > jiffies + cmd->timeout_per_command - HZ) {
+               np->settle_time = jiffies + cmd->timeout_per_command - HZ;
+       }
+
         if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
                insert_into_waiting_list(np, cmd);
                restore_flags(flags);
@@ -4933,21 +5055,23 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
                u_long endp;
        default:
        case XferBoth:
-               cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_io);
+               cp->phys.header.savep =
+                       cpu_to_scr(NCB_SCRIPT_PHYS (np, data_io));
                cp->phys.header.goalp = cp->phys.header.savep;
                break;
        case XferIn:
                endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
-               cp->phys.header.goalp = endp + 8;
-               cp->phys.header.savep = endp - segments*16;
+               cp->phys.header.goalp = cpu_to_scr(endp + 8);
+               cp->phys.header.savep = cpu_to_scr(endp - segments*16);
                break;
        case XferOut:
                endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
-               cp->phys.header.goalp = endp + 8;
-               cp->phys.header.savep = endp - segments*16;
+               cp->phys.header.goalp = cpu_to_scr(endp + 8);
+               cp->phys.header.savep = cpu_to_scr(endp - segments*16);
                break;
        case XferNone:
-               cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
+               cp->phys.header.savep =
+                       cpu_to_scr(NCB_SCRIPT_PHYS (np, no_data));
                cp->phys.header.goalp = cp->phys.header.savep;
                break;
        }
@@ -4968,8 +5092,9 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        /*
        **      Startqueue
        */
-       cp->phys.header.launch.l_paddr  = NCB_SCRIPT_PHYS (np, select);
-       cp->phys.header.launch.l_cmd    = SCR_JUMP;
+       cp->phys.header.launch.l_paddr  =
+                       cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+       cp->phys.header.launch.l_cmd    = cpu_to_scr(SCR_JUMP);
        /*
        **      select
        */
@@ -4979,21 +5104,21 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        /*
        **      message
        */
-       cp->phys.smsg.addr              = CCB_PHYS (cp, scsi_smsg);
-       cp->phys.smsg.size              = msglen;
+       cp->phys.smsg.addr              = cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
+       cp->phys.smsg.size              = cpu_to_scr(msglen);
 
-       cp->phys.smsg2.addr             = CCB_PHYS (cp, scsi_smsg2);
-       cp->phys.smsg2.size             = msglen2;
+       cp->phys.smsg2.addr             = cpu_to_scr(CCB_PHYS (cp, scsi_smsg2));
+       cp->phys.smsg2.size             = cpu_to_scr(msglen2);
        /*
        **      command
        */
-       cp->phys.cmd.addr               = vtophys (&cmd->cmnd[0]);
-       cp->phys.cmd.size               = cmd->cmd_len;
+       cp->phys.cmd.addr               = cpu_to_scr(vtophys (&cmd->cmnd[0]));
+       cp->phys.cmd.size               = cpu_to_scr(cmd->cmd_len);
        /*
        **      sense command
        */
-       cp->phys.scmd.addr              = CCB_PHYS (cp, sensecmd);
-       cp->phys.scmd.size              = 6;
+       cp->phys.scmd.addr              = cpu_to_scr(CCB_PHYS (cp, sensecmd));
+       cp->phys.scmd.size              = cpu_to_scr(6);
        /*
        **      patch requested size into sense command
        */
@@ -5003,8 +5128,9 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        /*
        **      sense data
        */
-       cp->phys.sense.addr             = vtophys (&cmd->sense_buffer[0]);
-       cp->phys.sense.size             = sizeof(cmd->sense_buffer);
+       cp->phys.sense.addr             =
+                       cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
+       cp->phys.sense.size             = cpu_to_scr(sizeof(cmd->sense_buffer));
        /*
        **      status
        */
@@ -5029,8 +5155,10 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        **      reselect pattern and activate this job.
        */
 
-       cp->jump_ccb.l_cmd      = (SCR_JUMP ^ IFFALSE (DATA (cp->tag)));
-       /* Compute a time limit bigger than the middle-level driver one */
+       cp->jump_ccb.l_cmd      =
+               cpu_to_scr((SCR_JUMP ^ IFFALSE (DATA (cp->tag))));
+
+       /* Compute a time limit greater than the middle-level driver one */
        if (cmd->timeout_per_command > 0)
                cp->tlimit      = jiffies + cmd->timeout_per_command + NCR_TIMEOUT_INCREASE;
        else
@@ -5043,14 +5171,14 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 
        qidx = np->squeueput + 1;
        if (qidx >= MAX_START) qidx=0;
-       np->squeue [qidx         ] = NCB_SCRIPT_PHYS (np, idle);
-       np->squeue [np->squeueput] = CCB_PHYS (cp, phys);
+       np->squeue [qidx         ] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+       np->squeue [np->squeueput] = cpu_to_scr(CCB_PHYS (cp, phys));
        np->squeueput = qidx;
 
        if(DEBUG_FLAGS & DEBUG_QUEUE)
                printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
                np->squeueput,
-               (unsigned)(np->script->startpos[0]
+               (unsigned)(scr_to_cpu(np->script->startpos[0]) 
                           (NCB_SCRIPTH_PHYS (np, tryloop))));
 
        /*
@@ -5093,21 +5221,64 @@ static void ncr_start_reset(ncb_p np, int settle_delay)
        save_flags(flags); cli();
 
        if (!np->settle_time) {
-               if (bootverbose > 1)
-                       printf("%s: resetting, command processing suspended for %d seconds\n",
-                               ncr_name(np), settle_delay);
-               np->settle_time = jiffies + settle_delay * HZ;
-               OUTB (nc_istat, SRST);
-               DELAY (1000);
-               OUTB (nc_istat, 0);
-               OUTW (nc_sien, RST);
-               OUTB (nc_scntl1, CRST);
-               DELAY (100);
+               (void) ncr_reset_scsi_bus(np, 1, settle_delay);
        }
-
        restore_flags(flags);
 }
 
+static int ncr_reset_scsi_bus(ncb_p np, int enab_int, int settle_delay)
+{
+       u_int32 term;
+       int retv = 0;
+
+       np->settle_time = jiffies + settle_delay * HZ;
+
+       if (bootverbose > 1)
+               printf("%s: resetting, "
+                       "command processing suspended for %d seconds\n",
+                       ncr_name(np), settle_delay);
+
+       OUTB (nc_istat, SRST);
+       DELAY (1000);
+       OUTB (nc_istat, 0);
+       if (enab_int)
+               OUTW (nc_sien, RST);
+       OUTB (nc_scntl1, CRST);
+       DELAY (100);
+
+       if (!driver_setup.bus_check)
+               goto out;
+       /*
+       **      Check for no terminators or SCSI bus shorts to ground.
+       **      Read SCSI data bus, data parity bits and control signals.
+       **      We are expecting RESET to be TRUE and other signals to be 
+       **      FALSE.
+       */
+       term =  INB(nc_sstat0);                         /* rst, sdp0 */
+       term =  ((term & 2) << 7) + ((term & 1) << 16);
+       term |= ((INB(nc_sstat2) & 0x01) << 25) |       /* sdp1 */
+               (INW(nc_sbdl) << 9) |                   /* d15-0 */
+               INB(nc_sbcl);   /* req, ack, bsy, sel, atn, msg, cd, io */
+
+       if (!(np->features & FE_WIDE))
+               term &= 0x3ffff;
+
+       if (term != (2<<7)) {
+               printf("%s: suspicious SCSI data while resetting the BUS.\n",
+                       ncr_name(np));
+               printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
+                       "0x%lx, expecting 0x%lx\n",
+                       ncr_name(np),
+                       (np->features & FE_WIDE) ? "dp1,d15-8," : "",
+                       (u_long)term, (u_long)(2<<7));
+               if (driver_setup.bus_check == 1)
+                       retv = 1;
+       }
+out:
+       OUTB (nc_scntl1, 0);
+       return retv;
+}
+
 /*==========================================================
 **
 **
@@ -5146,7 +5317,7 @@ int ncr_reset_bus (Scsi_Cmnd *cmd, int sync_reset)
  * Commands will now be queued in the waiting list until a settle 
  * delay of 2 seconds will be completed.
  */
-       ncr_start_reset(np, 2);
+       ncr_start_reset(np, driver_setup.settle_delay);
 /*
  * First, look in the wakeup list
  */
@@ -5256,10 +5427,12 @@ static int ncr_abort_command (Scsi_Cmnd *cmd)
        **      this condition in order to complete the canceled command 
        **      after the script skipped the ccb, if necessary.
        */
-       cp->jump_ccb.l_cmd = (SCR_JUMP);
-       if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) {
+       cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+       if (cp->phys.header.launch.l_paddr ==
+               cpu_to_scr(NCB_SCRIPT_PHYS (np, select))) {
                printf ("%s: abort ccb=%p (skip)\n", ncr_name (np), cp);
-               cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip);
+               cp->phys.header.launch.l_paddr =
+                       cpu_to_scr(NCB_SCRIPT_PHYS (np, skip));
        }
 
        cp->tlimit = 0;
@@ -5292,7 +5465,7 @@ static int ncr_abort_command (Scsi_Cmnd *cmd)
 */
 
 #ifdef MODULE
-static int ncr_detach(ncb_p np, int irq)
+static int ncr_detach(ncb_p np)
 {
        ccb_p cp;
        tcb_p tp;
@@ -5331,16 +5504,12 @@ static int ncr_detach(ncb_p np, int irq)
 */
 
 #ifdef DEBUG_NCR53C8XX
-       printf("%s: freeing irq %d\n", ncr_name(np), irq);
+       printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
 #endif
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-#   ifdef SCSI_NCR_SHARE_IRQ
-       free_irq(irq, np);
-#   else
-       free_irq(irq, NULL);
-#   endif
+       free_irq(np->irq, np);
 #else
-       free_irq(irq);
+       free_irq(np->irq);
 #endif
 
        /*
@@ -5450,12 +5619,12 @@ void ncr_complete (ncb_p np, ccb_p cp)
        /*
        **      No Reselect anymore.
        */
-       cp->jump_ccb.l_cmd = (SCR_JUMP);
+       cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
 
        /*
        **      No starting.
        */
-       cp->phys.header.launch.l_paddr= NCB_SCRIPT_PHYS (np, idle);
+       cp->phys.header.launch.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
 
        /*
        **      timestamp
@@ -5522,10 +5691,12 @@ void ncr_complete (ncb_p np, ccb_p cp)
        **      Check the status.
        */
        if (   (cp->host_status == HS_COMPLETE)
-               && (cp->scsi_status == S_GOOD)) {
-
+               && (cp->scsi_status == S_GOOD ||
+                   cp->scsi_status == S_COND_MET)) {
                /*
-               **      All went well.
+               **      All went well (GOOD status).
+               **      CONDITION MET status is returned on 
+               **      `Pre-Fetch' or `Search data' success.
                */
                cmd->result = ScsiResult(DID_OK, cp->scsi_status);
 
@@ -5620,7 +5791,8 @@ void ncr_complete (ncb_p np, ccb_p cp)
                }
 
        } else if ((cp->host_status == HS_COMPLETE)
-               && (cp->scsi_status == S_BUSY)) {
+               && (cp->scsi_status == S_BUSY ||
+                   cp->scsi_status == S_CONFLICT)) {
 
                /*
                **   Target is busy.
@@ -5807,14 +5979,14 @@ void ncr_init (ncb_p np, char * msg, u_long code)
        **      Clear Start Queue
        */
        for (i=0;i<MAX_START;i++)
-               np -> squeue [i] = NCB_SCRIPT_PHYS (np, idle);
+               np -> squeue [i] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
 
        /*
        **      Start at first entry.
        */
        np->squeueput = 0;
-       np->script0->startpos[0] = NCB_SCRIPTH_PHYS (np, tryloop);
-       np->script0->start0  [0] = SCR_INT ^ IFFALSE (0);
+       np->script0->startpos[0] = cpu_to_scr(NCB_SCRIPTH_PHYS (np, tryloop));
+       np->script0->start0  [0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
 
        /*
        **      Wakeup all pending jobs.
@@ -5866,7 +6038,11 @@ void ncr_init (ncb_p np, char * msg, u_long code)
        if (np->vaddr2) {
                if (bootverbose)
                        printf ("%s: copying script fragments into the on-board RAM ...\n", ncr_name(np));
-               bcopy(np->script0, np->script, sizeof(struct script));
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
+               memcpy_toio(np->script, np->script0, sizeof(struct script));
+#else
+               memcpy(np->script, np->script0, sizeof(struct script));
+#endif
        }
 
        /*
@@ -6150,7 +6326,7 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
                if      (tp->period < 500)      scsi = "FAST-40";
                else if (tp->period < 1000)     scsi = "FAST-20";
                else if (tp->period < 2000)     scsi = "FAST-10";
-               else                            scsi = "SLOW";
+               else                            scsi = "FAST-5";
 
                printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
                        tp->widedone > 1 ? "WIDE " : "",
@@ -6487,7 +6663,6 @@ static void ncr_timeout (ncb_p np)
 {
        u_long  thistime = jiffies;
        u_long  count = 0;
-       long signed   t;
        ccb_p cp;
        u_long flags;
 
@@ -6561,10 +6736,12 @@ static void ncr_timeout (ncb_p np)
                **
                **----------------------------------------------------
                */
-
-               t = (thistime - np->heartbeat) / HZ;
-
-               if (t<2) np->latetime=0; else np->latetime++;
+#if 0
+               if (thistime < np->heartbeat + HZ + HZ)
+                       np->latetime = 0;
+               else
+                       np->latetime++;
+#endif
 
                /*----------------------------------------------------
                **
@@ -6607,7 +6784,7 @@ static void ncr_timeout (ncb_p np)
                                ** still in start queue ?
                                */
                                if (cp->phys.header.launch.l_paddr ==
-                                       NCB_SCRIPT_PHYS (np, skip))
+                                       cpu_to_scr(NCB_SCRIPT_PHYS (np, skip)))
                                        continue;
 
                                /* fall through */
@@ -6691,14 +6868,20 @@ static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
                script_base     = (u_char *) np->script;
                script_name     = "script";
        }
-       else {
+       else if (np->p_scripth < dsp && 
+                dsp <= np->p_scripth + sizeof(struct scripth)) {
                script_ofs      = dsp - np->p_scripth;
                script_size     = sizeof(struct scripth);
                script_base     = (u_char *) np->scripth;
                script_name     = "scripth";
+       } else {
+               script_ofs      = dsp;
+               script_size     = 0;
+               script_base     = 0;
+               script_name     = "mem";
        }
 
-       printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n",
+       printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n",
                ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
                (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
                (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
@@ -6756,15 +6939,18 @@ void ncr_exception (ncb_p np)
 
        /*
        **      interrupt on the fly ?
-       */
-       while ((istat = INB (nc_istat)) & INTF) {
+       **      Since the global header may be copied back to a CCB 
+       **      using a posted PCI memory write, the last operation on 
+       **      the istat register is a READ in order to flush posted 
+       **      PCI commands (Btw, the 'do' loop is probably useless).
+       */
+       istat = INB (nc_istat);
+       if (istat & INTF) {
+               do {
+                       OUTB (nc_istat, (istat & SIGP) | INTF);
+                       istat = INB (nc_istat);
+               } while (istat & INTF);
                if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
-#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-       if (np->stalling)
-               OUTB (nc_istat, INTF);
-       else
-#endif
-               OUTB (nc_istat, (istat & SIGP) | INTF);
                np->profile.num_fly++;
                ncr_wakeup (np, 0);
        };
@@ -6819,9 +7005,16 @@ void ncr_exception (ncb_p np)
                        ncr_int_sir (np);
                        return;
                }
-               if (!(sist & (SBMC|PAR)) && !(dstat & SSI))
-                       printf("%s: unknown interrupt(s) ignored sist=%x dstat=%x\n",
-                               ncr_name(np), sist, dstat);
+               /*
+               **  DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 2.
+               */
+               if (!(sist & (SBMC|PAR)) && !(dstat & SSI)) {
+                       printf( "%s: unknown interrupt(s) ignored, "
+                               "ISTAT=%x DSTAT=%x SIST=%x\n",
+                               ncr_name(np), istat, dstat, sist);
+                       return;
+               }
+
                OUTONB (nc_dcntl, (STD|NOCOM));
                return;
        };
@@ -6848,6 +7041,11 @@ void ncr_exception (ncb_p np)
 
        if ((sist & STO) &&
                !(dstat & (MDPE|BF|ABRT))) {
+       /*
+       **      DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 1.
+       */
+               OUTONB (nc_ctest3, CLF);
+
                ncr_int_sto (np);
                return;
        };
@@ -6880,13 +7078,13 @@ void ncr_exception (ncb_p np)
 
        if ((sist & (SGE)) ||
                (dstat & (MDPE|BF|ABORT|IID))) {
-               ncr_start_reset(np, 2);
+               ncr_start_reset(np, driver_setup.settle_delay);
                return;
        };
 
        if (sist & HTH) {
                printf ("%s: handshake timeout\n", ncr_name(np));
-               ncr_start_reset(np, 2);
+               ncr_start_reset(np, driver_setup.settle_delay);
                return;
        };
 
@@ -6896,7 +7094,7 @@ void ncr_exception (ncb_p np)
                        OUTB (nc_scr1, HS_UNEXPECTED);
                        OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
                };
-               ncr_start_reset(np, 2);
+               ncr_start_reset(np, driver_setup.settle_delay);
                return;
        };
 
@@ -6954,7 +7152,7 @@ void ncr_int_sto (ncb_p np)
 /*     assert ((diff <= MAX_START * 20) && !(diff % 20));*/
 
        if ((diff <= MAX_START * 20) && !(diff % 20)) {
-               np->script->startpos[0] = scratcha;
+               np->script->startpos[0] = cpu_to_scr(scratcha);
                OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
                return;
        };
@@ -6987,7 +7185,7 @@ static int ncr_int_sbmc (ncb_p np)
                ncr_name(np), np->scsi_mode, scsi_mode);
 
        np->scsi_mode = scsi_mode;
-       ncr_start_reset(np, 2);
+       ncr_start_reset(np, driver_setup.settle_delay);
 
        return 1;
 }
@@ -7146,20 +7344,20 @@ static void ncr_int_ma (ncb_p np)
        **      get old startaddress and old length.
        */
 
-       oadr = vdsp[1];
+       oadr = scr_to_cpu(vdsp[1]);
 
        if (cmd & 0x10) {       /* Table indirect */
                tblp = (u_int32 *) ((char*) &cp->phys + oadr);
-               olen = tblp[0];
-               oadr = tblp[1];
+               olen = scr_to_cpu(tblp[0]);
+               oadr = scr_to_cpu(tblp[1]);
        } else {
                tblp = (u_int32 *) 0;
-               olen = vdsp[0] & 0xffffff;
+               olen = scr_to_cpu(vdsp[0]) & 0xffffff;
        };
 
        if (DEBUG_FLAGS & DEBUG_PHASE) {
                printf ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
-                       (unsigned) (vdsp[0] >> 24),
+                       (unsigned) (scr_to_cpu(vdsp[0]) >> 24),
                        tblp,
                        (unsigned) olen,
                        (unsigned) oadr);
@@ -7169,10 +7367,10 @@ static void ncr_int_ma (ncb_p np)
        **      check cmd against assumed interrupted script command.
        */
 
-       if (cmd != (vdsp[0] >> 24)) {
+       if (cmd != (scr_to_cpu(vdsp[0]) >> 24)) {
                PRINT_ADDR(cp->cmd);
                printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
-                       (unsigned)cmd, (unsigned)vdsp[0] >> 24);
+                       (unsigned)cmd, (unsigned)scr_to_cpu(vdsp[0]) >> 24);
                
                return;
        }
@@ -7204,25 +7402,25 @@ static void ncr_int_ma (ncb_p np)
        */
 
        newcmd = cp->patch;
-       if (cp->phys.header.savep == vtophys (newcmd)) newcmd+=4;
+       if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
 
        /*
        **      fillin the commands
        */
 
-       newcmd[0] = ((cmd & 0x0f) << 24) | rest;
-       newcmd[1] = oadr + olen - rest;
-       newcmd[2] = SCR_JUMP;
-       newcmd[3] = nxtdsp;
+       newcmd[0] = cpu_to_scr(((cmd & 0x0f) << 24) | rest);
+       newcmd[1] = cpu_to_scr(oadr + olen - rest);
+       newcmd[2] = cpu_to_scr(SCR_JUMP);
+       newcmd[3] = cpu_to_scr(nxtdsp);
 
        if (DEBUG_FLAGS & DEBUG_PHASE) {
                PRINT_ADDR(cp->cmd);
                printf ("newcmd[%d] %x %x %x %x.\n",
                        (int) (newcmd - cp->patch),
-                       (unsigned)newcmd[0],
-                       (unsigned)newcmd[1],
-                       (unsigned)newcmd[2],
-                       (unsigned)newcmd[3]);
+                       (unsigned)scr_to_cpu(newcmd[0]),
+                       (unsigned)scr_to_cpu(newcmd[1]),
+                       (unsigned)scr_to_cpu(newcmd[2]),
+                       (unsigned)scr_to_cpu(newcmd[3]));
        }
        /*
        **      fake the return address (to the patch).
@@ -7309,20 +7507,23 @@ void ncr_int_sir (ncb_p np)
 */
                if (num == SIR_DATA_IO_IS_OUT) {
                        endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
-                       cp->phys.header.goalp = endp + 8;
-                       cp->phys.header.savep = endp - cp->segments*16;
+                       cp->phys.header.goalp = cpu_to_scr(endp + 8);
+                       cp->phys.header.savep =
+                               cpu_to_scr(endp - cp->segments*16);
                } else {
                        endp = NCB_SCRIPT_PHYS (np, data_in)  + MAX_SCATTER*16;
-                       cp->phys.header.goalp = endp + 8;
-                       cp->phys.header.savep = endp - cp->segments*16;
+                       cp->phys.header.goalp = cpu_to_scr(endp + 8);
+                       cp->phys.header.savep =
+                               cpu_to_scr(endp - cp->segments*16);
                }
 
                cp->phys.header.lastp   = cp->phys.header.savep;
                np->header.savep        = cp->phys.header.savep;
                np->header.goalp        = cp->phys.header.goalp;
                np->header.lastp        = cp->phys.header.lastp;
-               OUTL (nc_temp,  np->header.savep);
-               OUTL (nc_dsp,   np->header.savep);
+
+               OUTL (nc_temp,  scr_to_cpu(np->header.savep));
+               OUTL (nc_dsp,   scr_to_cpu(np->header.savep));
                return;
                /* break; */
 
@@ -7369,7 +7570,7 @@ void ncr_int_sir (ncb_p np)
                **      no job, resume normal processing
                */
                if (DEBUG_FLAGS & DEBUG_RESTART) printf (" -- remove trap\n");
-               np->script->start0[0] =  SCR_INT ^ IFFALSE (0);
+               np->script->start0[0] =  cpu_to_scr(SCR_INT ^ IFFALSE (0));
                break;
 
        case SIR_SENSE_FAILED:
@@ -7395,7 +7596,7 @@ void ncr_int_sir (ncb_p np)
                /*
                **      And patch code to restart it.
                */
-               np->script->start0[0] =  SCR_INT;
+               np->script->start0[0] =  cpu_to_scr(SCR_INT);
                break;
 
 /*-----------------------------------------------------------------------------
@@ -7752,7 +7953,7 @@ void ncr_int_sir (ncb_p np)
 
                PRINT_ADDR(cp->cmd);
                printf ("M_REJECT received (%x:%x).\n",
-                       (unsigned)np->lastmsg, np->msgout[0]);
+                       (unsigned)scr_to_cpu(np->lastmsg), np->msgout[0]);
                break;
 
        case SIR_REJECT_SENT:
@@ -7802,8 +8003,8 @@ void ncr_int_sir (ncb_p np)
                printf ("M_DISCONNECT received, but datapointer not saved: "
                        "data=%x save=%x goal=%x.\n",
                        (unsigned) INL (nc_temp),
-                       (unsigned) np->header.savep,
-                       (unsigned) np->header.goalp);
+                       (unsigned) scr_to_cpu(np->header.savep),
+                       (unsigned) scr_to_cpu(np->header.goalp));
                break;
 
 #if 0   /* This stuff does not work */
@@ -7834,7 +8035,7 @@ void ncr_int_sir (ncb_p np)
                PRINT_ADDR(cp->cmd);
                printf ("queue full.\n");
 
-               np->script->start1[0] =  SCR_INT;
+               np->script->start1[0] =  cpu_to_scr(SCR_INT);
 
                /*
                **      Try to disable tagged transfers.
@@ -7882,7 +8083,7 @@ void ncr_int_sir (ncb_p np)
                */
 
                printf ("%s: queue empty.\n", ncr_name (np));
-               np->script->start1[0] =  SCR_INT ^ IFFALSE (0);
+               np->script->start1[0] =  cpu_to_scr(SCR_INT ^ IFFALSE (0));
                break;
 #endif   /* This stuff does not work */
        };
@@ -8021,33 +8222,37 @@ static  void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
        tp=&np->target[target];
 
        if (!tp->jump_tcb.l_cmd) {
-
                /*
                **      initialize it.
                */
-               tp->jump_tcb.l_cmd   = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
+               tp->jump_tcb.l_cmd   =
+                       cpu_to_scr((SCR_JUMP^IFFALSE (DATA (0x80 + target))));
                tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
 
-               tp->getscr[0] =
-                       (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
-               tp->getscr[1] = vtophys (&tp->sval);
-               tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer);
-               tp->getscr[3] =
-                       (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
-               tp->getscr[4] = vtophys (&tp->wval);
-               tp->getscr[5] = np->paddr + offsetof (struct ncr_reg, nc_scntl3);
+               tp->getscr[0] = (np->features & FE_PFEN) ?
+                       cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
+               tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+               tp->getscr[2] =
+               cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_sxfer));
+
+               tp->getscr[3] = (np->features & FE_PFEN) ?
+                       cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
+               tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+               tp->getscr[5] =
+               cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_scntl3));
 
                assert (( (offsetof(struct ncr_reg, nc_sxfer) ^
                        offsetof(struct tcb    , sval    )) &3) == 0);
                assert (( (offsetof(struct ncr_reg, nc_scntl3) ^
                        offsetof(struct tcb    , wval    )) &3) == 0);
 
-               tp->call_lun.l_cmd   = (SCR_CALL);
-               tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun);
+               tp->call_lun.l_cmd   = cpu_to_scr(SCR_CALL);
+               tp->call_lun.l_paddr =
+                       cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_lun));
 
-               tp->jump_lcb.l_cmd   = (SCR_JUMP);
-               tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
-               np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb);
+               tp->jump_lcb.l_cmd   = cpu_to_scr(SCR_JUMP);
+               tp->jump_lcb.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+               np->jump_tcb.l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
        }
 
        /*
@@ -8070,14 +8275,17 @@ static  void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
                **      Initialize it
                */
                bzero (lp, sizeof (*lp));
-               lp->jump_lcb.l_cmd   = (SCR_JUMP ^ IFFALSE (DATA (lun)));
+               lp->jump_lcb.l_cmd   =
+                       cpu_to_scr(SCR_JUMP ^ IFFALSE (DATA (lun)));
                lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr;
 
-               lp->call_tag.l_cmd   = (SCR_CALL);
-               lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag);
+               lp->call_tag.l_cmd   = cpu_to_scr(SCR_CALL);
+               lp->call_tag.l_paddr =
+                       cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tag));
 
-               lp->jump_ccb.l_cmd   = (SCR_JUMP);
-               lp->jump_ccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag);
+               lp->jump_ccb.l_cmd   = cpu_to_scr(SCR_JUMP);
+               lp->jump_ccb.l_paddr =
+                       cpu_to_scr(NCB_SCRIPTH_PHYS (np, aborttag));
 
                lp->actlink = 1;
 
@@ -8086,7 +8294,7 @@ static    void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
                /*
                **   Chain into LUN list
                */
-               tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb);
+               tp->jump_lcb.l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
                tp->lp[lun] = lp;
 
                ncr_setmaxtags (np, tp, driver_setup.default_tags);
@@ -8138,11 +8346,11 @@ static  void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
        /*
        **      Chain into reselect list
        */
-       cp->jump_ccb.l_cmd   = SCR_JUMP;
+       cp->jump_ccb.l_cmd   = cpu_to_scr(SCR_JUMP);
        cp->jump_ccb.l_paddr = lp->jump_ccb.l_paddr;
-       lp->jump_ccb.l_paddr = CCB_PHYS (cp, jump_ccb);
-       cp->call_tmp.l_cmd   = SCR_CALL;
-       cp->call_tmp.l_paddr = NCB_SCRIPT_PHYS (np, resel_tmp);
+       lp->jump_ccb.l_paddr = cpu_to_scr(CCB_PHYS (cp, jump_ccb));
+       cp->call_tmp.l_cmd   = cpu_to_scr(SCR_CALL);
+       cp->call_tmp.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tmp));
 
        /*
        **      Chain into wakeup list
@@ -8255,9 +8463,9 @@ static    int     ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
        if (!use_sg) {
                if (cmd->request_bufflen) {
                        data = &data[MAX_SCATTER - 1];
-                       data[0].addr    = vtophys(cmd->request_buffer);
-                       data[0].size    = cmd->request_bufflen;
-                       cp->data_len    = data[0].size;
+                       data[0].addr = cpu_to_scr(vtophys(cmd->request_buffer));
+                       data[0].size = cpu_to_scr(cmd->request_bufflen);
+                       cp->data_len = cmd->request_bufflen;
                        segment = 1;
                }
        }
@@ -8266,9 +8474,11 @@ static   int     ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
 
                data = &data[MAX_SCATTER - use_sg];
                while (segment < use_sg) {
-                       data[segment].addr = vtophys(scatter[segment].address);
-                       data[segment].size = scatter[segment].length;
-                       cp->data_len       += data[segment].size;
+                       data[segment].addr =
+                               cpu_to_scr(vtophys(scatter[segment].address));
+                       data[segment].size =
+                               cpu_to_scr(scatter[segment].length);
+                       cp->data_len       += scatter[segment].length;
                        ++segment;
                }
        }
@@ -8338,7 +8548,7 @@ static int ncr_snooptest (struct ncb* np)
        /*
        **      Set memory and register.
        */
-       np->ncr_cache = host_wr;
+       np->ncr_cache = cpu_to_scr(host_wr);
        OUTL (nc_temp, ncr_wr);
        /*
        **      Start script (exchange values)
@@ -8357,7 +8567,7 @@ static int ncr_snooptest (struct ncb* np)
        /*
        **      Read memory and register.
        */
-       host_rd = np->ncr_cache;
+       host_rd = scr_to_cpu(np->ncr_cache);
        ncr_rd  = INL (nc_scratcha);
        ncr_bk  = INL (nc_temp);
        /*
@@ -8793,6 +9003,8 @@ void ncr53c8xx_setup(char *str, int *ints)
                        driver_setup.irqm       = val;
                else if (!strncmp(cur, "pcifix:", 7))
                        driver_setup.pci_fix_up = val;
+               else if (!strncmp(cur, "buschk:", 7))
+                       driver_setup.bus_check  = val;
 #ifdef SCSI_NCR_NVRAM_SUPPORT
                else if (!strncmp(cur, "nvram:", 6))
                        driver_setup.use_nvram  = val;
@@ -8803,7 +9015,11 @@ void ncr53c8xx_setup(char *str, int *ints)
                else
                        printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
 
+#ifdef MODULE
+               if ((cur = strchr(cur, ' ')) != NULL)
+#else
                if ((cur = strchr(cur, ',')) != NULL)
+#endif
                        ++cur;
        }
 #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
@@ -8832,9 +9048,9 @@ static void ncr_print_driver_setup(void)
 )
 {
 #define YesNo(y)       y ? 'y' : 'n'
-       printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
+       printk("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
                        YesNo(driver_setup.disconnection),
-                       YesNo(driver_setup.special_features),
+                       driver_setup.special_features,
                        YesNo(driver_setup.ultra_scsi),
                        driver_setup.default_tags,
                        driver_setup.default_sync,
@@ -8933,10 +9149,10 @@ ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr
                            h->device_id == devp->chip.device_id)
                                break;
                }
-               if (j < count && !devp->attached &&
-                   !ncr_attach (tpnt, attach_count, devp)) {
-                       attach_count++;
-                       devp->attached = 1;
+               if (j < count && !devp->attach_done) {
+                       if (!ncr_attach (tpnt, attach_count, devp))
+                               attach_count++;
+                       devp->attach_done = 1;
                }
        }
 
@@ -8979,6 +9195,11 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 # endif
 #endif
 
+#if    defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+       ncr53c8xx_setup(ncr53c8xx, (int *) 0);
+#endif
+
        /* 
        ** Detect all 53c8xx hosts and then attach them.
        **
@@ -9040,8 +9261,6 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 #endif
                        printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
                                device[count].chip.name, msg);
-
-                       device[count].attached = 0;
                        ++count;
                }
        }
@@ -9054,9 +9273,9 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
        ** so try to attach them here.
        */
        for (i= 0; i < count; i++) {
-               if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+               if (!device[i].attach_done && 
+                   !ncr_attach (tpnt, attach_count, &device[i])) {
                        attach_count++;
-                       device[i].attached = 1;
                }
        }
 
@@ -9078,11 +9297,11 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
        uchar cache_line_size, latency_timer;
        uchar irq, revision;
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-       uint base, io_port; 
+       uint base, base_2, io_port; 
 #else
-       ulong base, io_port
+       ulong base, base_2
 #endif
-       int i, error;
+       int i;
 
 #ifdef SCSI_NCR_NVRAM_SUPPORT
        ncr_nvram *nvram = device->nvram;
@@ -9092,20 +9311,32 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
        printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
                bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
        /*
-        * Read info from the PCI config space
+        * Read info from the PCI config space.
+        * pcibios_read_config_xxx() functions are assumed to be used for 
+        * successfully detected PCI devices.
+        * Expecting error conditions from them is just paranoia,
+        * thus void cast.
         */
-       if (
-               (error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id))     ||
-               (error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id))     ||
-               (error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command))        ||
-               (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port))  || 
-               (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base))    ||
-               (error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision))  ||
-               (error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq))      ||
-               (error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
-               (error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
-       )
-               goto err_pcibios;
+       (void) pcibios_read_config_word(bus, device_fn,
+                                       PCI_VENDOR_ID, &vendor_id);
+       (void) pcibios_read_config_word(bus, device_fn,
+                                       PCI_DEVICE_ID, &device_id);
+       (void) pcibios_read_config_word(bus, device_fn,
+                                       PCI_COMMAND, &command);
+       (void) pcibios_read_config_dword(bus, device_fn,
+                                       PCI_BASE_ADDRESS_0, &io_port);  
+       (void) pcibios_read_config_dword(bus, device_fn,
+                                       PCI_BASE_ADDRESS_1, &base);
+       (void) pcibios_read_config_dword(bus, device_fn,
+                                       PCI_BASE_ADDRESS_2, &base_2);
+       (void) pcibios_read_config_byte(bus, device_fn,
+                                       PCI_CLASS_REVISION,&revision);  
+       (void) pcibios_read_config_byte(bus, device_fn,
+                                       PCI_INTERRUPT_LINE, &irq);
+       (void) pcibios_read_config_byte(bus, device_fn,
+                                       PCI_CACHE_LINE_SIZE, &cache_line_size);
+       (void) pcibios_read_config_byte(bus, device_fn,
+                                       PCI_LATENCY_TIMER, &latency_timer);
 
        /*
         *      Check if the chip is supported
@@ -9126,6 +9357,28 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
                return -1;
        }
 
+#ifdef __powerpc__
+       /*
+        *      Severall fix-up for power/pc.
+        *      Should not be performed by the driver.
+        */
+       if ((command &
+               (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) !=
+               (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) {
+               printk("ncr53c8xx : setting PCI master/io/command bit\n");
+               command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY;
+               pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+       }
+       if (io_port >= 0x10000000) {
+               io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+               pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
+       }
+       if (base >= 0x10000000) {
+               base = (base & 0x00FFFFFF) | 0x01000000;
+               pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
+       }
+#endif
+
        /*
         * Check availability of IO space, memory space and master capability.
         */
@@ -9158,6 +9411,8 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
                return -1;
        }
 
+       base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
+
        if (io_port && check_region (io_port, 128)) {
                printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
                        (int) io_port, (int) (io_port + 127));
@@ -9172,8 +9427,12 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
        /*
         * Fix some features according to driver setup.
         */
-       if (!driver_setup.special_features)
+       if (!(driver_setup.special_features & 1))
                chip->features &= ~FE_SPECIAL_SET;
+       else {
+               if (driver_setup.special_features & 2)
+                       chip->features &= ~FE_WRIE;
+       }
        if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
                chip->features |=  FE_ULTRA;
                chip->features &= ~FE_ULTRA2;
@@ -9192,15 +9451,18 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
 #if defined(__i386) && !defined(MODULE)
        if ((driver_setup.pci_fix_up & 1) &&
            (chip->features & FE_CLSE) && cache_line_size == 0) {
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
                extern char x86;
                switch(x86) {
+#else
+               switch(boot_cpu_data.x86) {
+#endif
                case 4: cache_line_size = 4; break;
                case 5: cache_line_size = 8; break;
                }
                if (cache_line_size)
-                       error = pcibios_write_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, cache_line_size);
-               if (error)
-                       goto err_pcibios;
+                       (void) pcibios_write_config_byte(bus, device_fn,
+                                       PCI_CACHE_LINE_SIZE, cache_line_size);
                if (initverbose)
                        printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
        }
@@ -9208,9 +9470,8 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
        if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
            (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
                command |= PCI_COMMAND_INVALIDATE;
-               error=pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
-               if (error)
-                       goto err_pcibios;
+               (void) pcibios_write_config_word(bus, device_fn,
+                                               PCI_COMMAND, command);
                if (initverbose)
                        printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
        }
@@ -9252,10 +9513,8 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
                        latency_timer = lt;
                        if (initverbose)
                                printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
-                       error = pcibios_write_config_byte(bus, device_fn,
+                        (void) pcibios_write_config_byte(bus, device_fn,
                                        PCI_LATENCY_TIMER, latency_timer);
-                       if (error)
-                               goto err_pcibios;
                }
        }
 
@@ -9281,9 +9540,10 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
        device->slot.bus        = bus;
        device->slot.device_fn  = device_fn;
        device->slot.base       = base;
+       device->slot.base_2     = base_2;
        device->slot.io_port    = io_port;
        device->slot.irq        = irq;
-       device->attached        = 0;
+       device->attach_done     = 0;
 #ifdef SCSI_NCR_NVRAM_SUPPORT
        if (!nvram)
                goto out;
@@ -9331,11 +9591,6 @@ out:
 
 #endif /* SCSI_NCR_NVRAM_SUPPORT */
        return 0;     
-
-err_pcibios:
-       printk("ncr53c8xx: error %s reading configuration space\n",
-               pcibios_strerror(error));
-       return -1;
 }
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -9394,47 +9649,41 @@ printk("ncr53c8xx : command successfully queued\n");
 }
 
 /*
-**   Linux entry point of the interrupt handler
+**   Linux entry point of the interrupt handler.
+**   Fort linux versions > 1.3.70, we trust the kernel for 
+**   passing the internal host descriptor as 'dev_id'.
+**   Otherwise, we scan the host list and call the interrupt 
+**   routine for each host that uses this IRQ.
 */
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
 static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+{
+#ifdef DEBUG_NCR53C8XX
+     printk("ncr53c8xx : interrupt received\n");
+#endif
+
+     if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+     ncr_exception((ncb_p) dev_id);
+     if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+}
+
 #else
 static void ncr53c8xx_intr(int irq, struct pt_regs * regs)
-#endif
 {
      struct Scsi_Host *host;
      struct host_data *host_data;
-#if 0
-     u_long flags;
-
-     save_flags(flags); cli();
-#endif
-
-#ifdef DEBUG_NCR53C8XX
-printk("ncr53c8xx : interrupt received\n");
-#endif
 
      for (host = first_host; host; host = host->next) {
          if (host->hostt == the_template && host->irq == irq) {
               host_data = (struct host_data *) host->hostdata;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-#   ifdef SCSI_NCR_SHARE_IRQ
-               if (dev_id == host_data->ncb) {
-#else
-               if (1) {
-#   endif
-#endif
-                    if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
-                   ncr_exception(host_data->ncb);
-                    if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
-               }
+               if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+               ncr_exception(host_data->ncb);
+               if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
          }
      }
-#if 0
-     restore_flags(flags);
-#endif
 }
+#endif
 
 /*
 **   Linux entry point of the timer handler
@@ -9540,17 +9789,10 @@ int ncr53c8xx_abort(Scsi_Cmnd *cmd)
 #ifdef MODULE
 int ncr53c8xx_release(struct Scsi_Host *host)
 {
-     struct host_data *host_data;
 #ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : release\n");
 #endif
-
-     for (host = first_host; host; host = host->next) {
-         if (host->hostt == the_template) {
-              host_data = (struct host_data *) host->hostdata;
-              ncr_detach(host_data->ncb, host->irq);
-         }
-     }
+     ncr_detach(((struct host_data *) host->hostdata)->ncb);
 
      return 1;
 }
index 6f8be172ddcd659a751fed281106c6464f13b6d4..9b54fc3b231a73d1732b1219a8083e9c08b145da 100644 (file)
@@ -45,7 +45,7 @@
 /*
 **     Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME           "ncr53c8xx - revision 2.4a"
+#define SCSI_NCR_DRIVER_NAME           "ncr53c8xx - revision 2.5f"
 
 /*
 **     Check supported Linux versions
  * For Ultra2 SCSI support option, use special features and allow 40Mhz 
  * synchronous data transfers.
  */
-#define        SCSI_NCR_SETUP_SPECIAL_FEATURES         (1)
+#define        SCSI_NCR_SETUP_SPECIAL_FEATURES         (3)
 #define SCSI_NCR_SETUP_ULTRA_SCSI              (2)
 #define SCSI_NCR_MAX_SYNC                      (40)
 
 #endif
 
 /*
- * Use normal IO if configured. Forced for alpha.
+ * Use normal IO if configured. Forced for alpha and ppc.
  */
-#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) || defined(__alpha__)
+#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED)
+#define        SCSI_NCR_IOMAPPED
+#elif defined(__alpha__) || defined(__powerpc__)
 #define        SCSI_NCR_IOMAPPED
 #endif
 
@@ -288,33 +290,89 @@ int ncr53c8xx_release(struct Scsi_Host *);
 #define ncr53c8xx_release NULL
 #endif
 
-#if    LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-
-#define NCR53C8XX {NULL,NULL,NULL,NULL,SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\
-       ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL,             \
-       ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset,      \
-        NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\
-       /* id */ 7, SCSI_NCR_SG_TABLESIZE /* SG */, /* cmd per lun */ SCSI_NCR_CMD_PER_LUN,             \
-        /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} 
+#if    LINUX_VERSION_CODE >= LinuxVersionCode(2,1,75)
+
+#define NCR53C8XX {     name:           SCSI_NCR_DRIVER_NAME,  \
+                       detect:         ncr53c8xx_detect,       \
+                       release:        ncr53c8xx_release,      \
+                       queuecommand:   ncr53c8xx_queue_command,\
+                       abort:          ncr53c8xx_abort,        \
+                       reset:          ncr53c8xx_reset,        \
+                       bios_param:     scsicam_bios_param,     \
+                       can_queue:      SCSI_NCR_CAN_QUEUE,     \
+                       this_id:        7,                      \
+                       sg_tablesize:   SCSI_NCR_SG_TABLESIZE,  \
+                       cmd_per_lun:    SCSI_NCR_CMD_PER_LUN,   \
+                       use_clustering: DISABLE_CLUSTERING} 
+  
+#elif  LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 
+#define NCR53C8XX {    NULL, NULL, NULL, NULL,                         \
+                       SCSI_NCR_DRIVER_NAME,   ncr53c8xx_detect,       \
+                       ncr53c8xx_release,      NULL,   NULL,           \
+                       ncr53c8xx_queue_command,ncr53c8xx_abort,        \
+                       ncr53c8xx_reset, NULL,  scsicam_bios_param,     \
+                       SCSI_NCR_CAN_QUEUE,     7,                      \
+                       SCSI_NCR_SG_TABLESIZE,  SCSI_NCR_CMD_PER_LUN,   \
+                       0,      0,      DISABLE_CLUSTERING} 
 
 #else
 
+#define NCR53C8XX {    NULL, NULL,                                     \
+                       SCSI_NCR_DRIVER_NAME,   ncr53c8xx_detect,       \
+                       ncr53c8xx_release,      NULL,   NULL,           \
+                       ncr53c8xx_queue_command,ncr53c8xx_abort,        \
+                       ncr53c8xx_reset, NULL,  scsicam_bios_param,     \
+                       SCSI_NCR_CAN_QUEUE,     7,                      \
+                       SCSI_NCR_SG_TABLESIZE,  SCSI_NCR_CMD_PER_LUN,   \
+                       0,      0,      DISABLE_CLUSTERING} 
 
-#define NCR53C8XX {NULL, NULL, SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\
-       ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL,             \
-       ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset,      \
-        NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\
-       /* id */ 7, SCSI_NCR_SG_TABLESIZE /* SG */, /* cmd per lun */ SCSI_NCR_CMD_PER_LUN,             \
-        /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} 
-
-#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) */
+#endif /* LINUX_VERSION_CODE */
 
 #endif /* defined(HOSTS_C) || defined(MODULE) */ 
 
 
 #ifndef HOSTS_C
 
+/*
+**     IO functions definition for big/little endian support.
+**     For now, the NCR is only supported in little endian addressing mode, 
+**     and big endian byte ordering is only supported for the PPC.
+**     MMIO is not used on PPC.
+*/
+
+#ifdef __BIG_ENDIAN
+
+#if    LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#error "BIG ENDIAN byte ordering needs kernel version >= 2.1.0"
+#endif
+
+#ifdef __powerpc__
+#define        inw_l2b         inw
+#define        inl_l2b         inl
+#define        outw_b2l        outw
+#define        outl_b2l        outl
+#else
+#error "Support for BIG ENDIAN is only available for the PowerPC"
+#endif
+
+#else  /* Assumed x86 or alpha */
+
+#define        inw_raw         inw
+#define        inl_raw         inl
+#define        outw_raw        outw
+#define        outl_raw        outl
+#define        readw_raw       readw
+#define        readl_raw       readl
+#define        writew_raw      writew
+#define        writel_raw      writel
+
+#endif
+
+#ifdef SCSI_NCR_BIG_ENDIAN
+#error "The NCR in BIG ENDIAN adressing mode is not (yet) supported"
+#endif
+
 /*
 **     NCR53C8XX Device Ids
 */
@@ -395,6 +453,19 @@ typedef struct {
 #define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
 } ncr_chip;
 
+/*
+**     DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 3.
+**     Memory Read transaction terminated by a retry followed by 
+**     Memory Read Line command.
+*/
+#define FE_CACHE0_SET  (FE_CACHE_SET & ~FE_ERL)
+
+/*
+**     DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 5.
+**     On paper, this errata is harmless. But it is a good reason for 
+**     using a shorter programmed burst length (64 DWORDS instead of 128).
+*/
+
 #define SCSI_NCR_CHIP_TABLE                                            \
 {                                                                      \
  {PCI_DEVICE_ID_NCR_53C810, 0x0f, "810",  4,  8, 4,                    \
@@ -412,23 +483,23 @@ typedef struct {
  {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825",  4,  8, 4,                    \
  FE_WIDE|FE_ERL|FE_BOF}                                                        \
  ,                                                                     \
- {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 7,  8, 4,                    \
- FE_WIDE|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}           \
+ {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 6,  8, 4,                    \
+ FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}          \
  ,                                                                     \
  {PCI_DEVICE_ID_NCR_53C860, 0xff, "860",  4,  8, 5,                    \
  FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN}               \
  ,                                                                     \
- {PCI_DEVICE_ID_NCR_53C875, 0x01, "875",  7, 16, 5,                    \
- FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875, 0x01, "875",  6, 16, 5,                    \
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,                                                                     \
- {PCI_DEVICE_ID_NCR_53C875, 0xff, "875",  7, 16, 5,                    \
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875, 0xff, "875",  6, 16, 5,                    \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,                                                                     \
- {PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J",  7, 16, 5,                  \
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5,                    \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,                                                                     \
- {PCI_DEVICE_ID_NCR_53C885, 0xff, "885",  7, 16, 5,                    \
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C885, 0xff, "885",  6, 16, 5,                    \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,                                                                     \
  {PCI_DEVICE_ID_NCR_53C895, 0xff, "895",  7, 31, 7,                    \
  FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
@@ -478,7 +549,8 @@ typedef struct {
        1,                                      \
        SCSI_NCR_SETUP_SETTLE_TIME,             \
        SCSI_NCR_SETUP_DIFF_SUPPORT,            \
-       0                                       \
+       0,                                      \
+       1                                       \
 }
 
 /*
@@ -506,6 +578,7 @@ typedef struct {
        0,                                      \
        10,                                     \
        1,                                      \
+       1,                                      \
        1                                       \
 }
 
index fa980051a6510ecf30234d9ca2382f0056e81a89..0095a6b0759b32211480dc600155b5a2f3085103 100644 (file)
@@ -1,6 +1,13 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *       4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
+ *          io_port is now unsigned long.
+ *
+ *      17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
+ *          Use new scsi error handling code (if linux version >= 2.1.88).
+ *          Use new interrupt code.
+ *
  *      12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
  *          Use of udelay inside the wait loops to avoid timeout
  *          problems with fast cpus.
  * 
  *          Multiple U14F and/or U34F host adapters are supported.
  *
- *  Copyright (C) 1994-1997 Dario Ballabio (dario@milano.europe.dg.com)
+ *  Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com)
  *
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that redistributions of source
@@ -329,9 +336,7 @@ struct proc_dir_entry proc_scsi_u14_34f = {
 #undef  DEBUG_LINKED_COMMANDS
 #undef  DEBUG_DETECT
 #undef  DEBUG_INTERRUPT
-#undef  DEBUG_STATISTICS
 #undef  DEBUG_RESET
-#undef  DEBUG_SMP
 
 #define MAX_ISA 3
 #define MAX_VESA 1 
@@ -341,7 +346,6 @@ struct proc_dir_entry proc_scsi_u14_34f = {
 #define MAX_CHANNEL 1
 #define MAX_LUN 8
 #define MAX_TARGET 8
-#define MAX_IRQ 16
 #define MAX_MAILBOXES 16
 #define MAX_SGLIST 32
 #define MAX_SAFE_SGLIST 16
@@ -349,7 +353,7 @@ struct proc_dir_entry proc_scsi_u14_34f = {
 #define MAX_CMD_PER_LUN 2
 #define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
 
-#define SKIP UINT_MAX
+#define SKIP ULONG_MAX
 #define FALSE 0
 #define TRUE 1
 #define FREE 0
@@ -424,7 +428,6 @@ struct hostdata {
    unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
    unsigned int last_cp_used;           /* Index of last mailbox used */
    unsigned int iocount;                /* Total i/o done for this board */
-   unsigned int multicount;             /* Total ... in second ihdlr loop */
    int board_number;                    /* Number of this board */
    char board_name[16];                 /* Name of this board */
    char board_id[256];                  /* data from INQUIRY on this board */
@@ -443,9 +446,12 @@ struct hostdata {
 
 static struct Scsi_Host *sh[MAX_BOARDS + 1];
 static const char *driver_name = "Ux4F";
-static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
+static char sha[MAX_BOARDS];
 
-static unsigned int io_port[] __initdata = { 
+/* Initialize num_boards so that ihdlr can work while detect is in progress */
+static unsigned int num_boards = MAX_BOARDS;
+
+static unsigned long io_port[] __initdata = { 
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -462,10 +468,10 @@ static unsigned int io_port[] __initdata = {
 #define BN(board) (HD(board)->board_name)
 
 #define SWAP_BYTE(x) ((unsigned long)( \
-       (((unsigned long)(x) & 0x000000ffU) << 24) | \
-       (((unsigned long)(x) & 0x0000ff00U) <<  8) | \
-       (((unsigned long)(x) & 0x00ff0000U) >>  8) | \
-       (((unsigned long)(x) & 0xff000000U) >> 24)))
+        (((unsigned long)(x) & 0x000000ffU) << 24) | \
+        (((unsigned long)(x) & 0x0000ff00U) <<  8) | \
+        (((unsigned long)(x) & 0x00ff0000U) >>  8) | \
+        (((unsigned long)(x) & 0xff000000U) >> 24)))
 
 #if defined(__BIG_ENDIAN)
 #define H2DEV(x) SWAP_BYTE(x)
@@ -560,7 +566,7 @@ static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
    return;
 }
 
-static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
+static inline int wait_on_busy(unsigned long iobase, unsigned int loop) {
 
    while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED) {
       udelay(1L);
@@ -614,7 +620,7 @@ static int board_inquiry(unsigned int j) {
 }
 
 __initfunc (static inline int port_detect \
-      (unsigned int port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
+      (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char in_byte;
    char *bus_type, dma_name[16];
@@ -637,8 +643,8 @@ __initfunc (static inline int port_detect \
       unsigned char heads;
       unsigned char sectors;
       } mapping_table[4] = { 
-          { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 }
-          };
+           { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 }
+           };
 
    struct config_1 {
       unsigned char bios_segment: 3;
@@ -659,7 +665,7 @@ __initfunc (static inline int port_detect \
    sprintf(name, "%s%d", driver_name, j);
 
    if(check_region(port_base, REGION_SIZE)) {
-      printk("%s: address 0x%03x in use, skipping probe.\n", name, port_base);
+      printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
       return FALSE;
       }
 
@@ -676,17 +682,18 @@ __initfunc (static inline int port_detect \
    dma_channel = dma_channel_table[config_1.dma_channel];
    subversion = (in_byte & 0x0f);
 
-   /* Board detected, allocate its IRQ if not already done */
-   if ((irq >= MAX_IRQ) || (!irqlist[irq] && request_irq(irq,
-              u14_34f_interrupt_handler, SA_INTERRUPT, driver_name, NULL))) {
+   /* Board detected, allocate its IRQ */
+   if (request_irq(irq, u14_34f_interrupt_handler,
+             SA_INTERRUPT | ((subversion == ESA) ? SA_SHIRQ : 0),
+             driver_name, (void *) &sha[j])) {
       printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
       return FALSE;
       }
 
    if (subversion == ISA && request_dma(dma_channel, driver_name)) {
       printk("%s: unable to allocate DMA channel %u, detaching.\n",
-            name, dma_channel);
-      free_irq(irq, NULL);
+             name, dma_channel);
+      free_irq(irq, &sha[j]);
       return FALSE;
       }
 
@@ -695,7 +702,7 @@ __initfunc (static inline int port_detect \
    if (sh[j] == NULL) {
       printk("%s: unable to register host, detaching.\n", name);
 
-      if (!irqlist[irq]) free_irq(irq, NULL);
+      free_irq(irq, &sha[j]);
 
       if (subversion == ISA) free_dma(dma_channel);
 
@@ -734,7 +741,6 @@ __initfunc (static inline int port_detect \
    HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors;
    HD(j)->subversion = subversion;
    HD(j)->board_number = j;
-   irqlist[irq]++;
 
    if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
 
@@ -767,12 +773,12 @@ __initfunc (static inline int port_detect \
       HD(j)->board_id[40] = 0;
 
       if (strcmp(&HD(j)->board_id[32], "06000600")) {
-        printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
-        printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n",
-               BN(j), &HD(j)->board_id[32]);
-        sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
-        sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
-        }
+         printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
+         printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n",
+                BN(j), &HD(j)->board_id[32]);
+         sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+         sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+         }
       }
 
    if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
@@ -792,10 +798,11 @@ __initfunc (static inline int port_detect \
 
    if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
 
-   printk("%s: %s 0x%03x, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d, of:%c, "\
-          "lc:%c, mq:%d.\n", BN(j), bus_type, sh[j]->io_port, (int)sh[j]->base,
-          sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
-          YESNO(have_old_firmware), YESNO(linked_comm), max_queue_depth);
+   printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d, of:%c, "\
+          "lc:%c, mq:%d.\n", BN(j), bus_type, (unsigned long)sh[j]->io_port,
+          (int)sh[j]->base, sh[j]->irq, dma_name, sh[j]->sg_tablesize,
+          sh[j]->can_queue, YESNO(have_old_firmware), YESNO(linked_comm),
+          max_queue_depth);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -857,11 +864,6 @@ __initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) {
       }
 #endif
 
-   for (k = 0; k < MAX_IRQ; k++) {
-      irqlist[k] = 0;
-      calls[k] = 0;
-      }
-
    for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
 
    for (k = 0; io_port[k]; k++) {
@@ -872,8 +874,9 @@ __initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) {
       }
 
    if (j > 0) 
-      printk("UltraStor 14F/34F: Copyright (C) 1994-1997 Dario Ballabio.\n");
+      printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n");
 
+   num_boards = j;
    restore_flags(flags);
    return j;
 }
@@ -938,26 +941,26 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
       if (i >= sh[j]->can_queue) i = 0;
 
       if (HD(j)->cp_stat[i] == FREE) {
-        HD(j)->last_cp_used = i;
-        break;
-        }
+         HD(j)->last_cp_used = i;
+         break;
+         }
       }
 
    if (k == sh[j]->can_queue) {
       printk("%s: qcomm, no free mailbox, resetting.\n", BN(j));
 
       if (HD(j)->in_reset) 
-        printk("%s: qcomm, already in reset.\n", BN(j));
+         printk("%s: qcomm, already in reset.\n", BN(j));
       else if (u14_34f_reset(SCpnt, SCSI_RESET_SUGGEST_BUS_RESET) 
                == SCSI_RESET_SUCCESS) 
-        panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
+         panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
 
       SCpnt->result = DID_BUS_BUSY << 16; 
       SCpnt->host_scribble = NULL;
       printk("%s: qcomm, pid %ld, DID_BUS_BUSY, done.\n", BN(j), SCpnt->pid);
       restore_flags(flags);
       done(SCpnt);    
-      return 0;
+      return 1;
       }
 
    /* Set pointer to control packet structure */
@@ -969,16 +972,16 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    SCpnt->host_scribble = (unsigned char *) &cpp->index;
 
    if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
-                       BN(j), i, SCpnt->channel, SCpnt->target, 
+                        BN(j), i, SCpnt->channel, SCpnt->target, 
                         SCpnt->lun, SCpnt->pid);
 
    cpp->xdir = DTD_IN;
 
    for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
       if (SCpnt->cmnd[0] == data_out_cmds[k]) {
-        cpp->xdir = DTD_OUT;
-        break;
-        }
+         cpp->xdir = DTD_OUT;
+         break;
+         }
 
    if (cpp->xdir == DTD_IN)
       for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
@@ -1023,7 +1026,7 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
              SCpnt->pid);
       restore_flags(flags);
       done(SCpnt);
-      return 0;
+      return 1;
       }
 
    /* Store pointer in OGM address bytes */
@@ -1048,14 +1051,14 @@ int u14_34f_abort(Scsi_Cmnd *SCarg) {
    if (SCarg->host_scribble == NULL
        || SCarg->serial_number != SCarg->serial_number_at_timeout) {
       printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
-            BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+             BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
       restore_flags(flags);
       return SCSI_ABORT_NOT_RUNNING;
       }
 
    i = *(unsigned int *)SCarg->host_scribble;
    printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
-         BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+          BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
 
    if (i >= sh[j]->can_queue)
       panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
@@ -1076,8 +1079,8 @@ int u14_34f_abort(Scsi_Cmnd *SCarg) {
       printk("%s: abort, mbox %d is in use.\n", BN(j), i);
 
       if (SCarg != HD(j)->cp[i].SCpnt)
-        panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
-              BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+         panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+               BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
 
       if (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED)
          printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
@@ -1103,7 +1106,7 @@ int u14_34f_abort(Scsi_Cmnd *SCarg) {
       SCarg->host_scribble = NULL;
       HD(j)->cp_stat[i] = FREE;
       printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
-            BN(j), i, SCarg->pid);
+             BN(j), i, SCarg->pid);
       SCarg->scsi_done(SCarg);
       restore_flags(flags);
       return SCSI_ABORT_SUCCESS;
@@ -1123,7 +1126,7 @@ int u14_34f_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
    cli();
    j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
    printk("%s: reset, enter, target %d.%d:%d, pid %ld, reset_flags %u.\n", 
-         BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
+          BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
           reset_flags);
 
    if (SCarg->host_scribble == NULL)
@@ -1160,13 +1163,13 @@ int u14_34f_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
       if (HD(j)->cp_stat[i] == FREE) continue;
 
       if (HD(j)->cp_stat[i] == LOCKED) {
-        HD(j)->cp_stat[i] = FREE;
-        printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
-        continue;
-        }
+         HD(j)->cp_stat[i] = FREE;
+         printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+         continue;
+         }
 
       if (!(SCpnt = HD(j)->cp[i].SCpnt))
-        panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+         panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
 
       if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
          HD(j)->cp_stat[i] = ABORTING;
@@ -1181,13 +1184,13 @@ int u14_34f_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
          }
 
       if (SCpnt->host_scribble == NULL)
-        panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+         panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
 
       if (*(unsigned int *)SCpnt->host_scribble != i) 
-        panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+         panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
 
       if (SCpnt->scsi_done == NULL) 
-        panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+         panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
 
       if (SCpnt == SCarg) arg_done = TRUE;
       }
@@ -1223,7 +1226,7 @@ int u14_34f_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
          HD(j)->cp_stat[i] = LOCKED;
 
          printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
-               BN(j), i, SCpnt->pid);
+                BN(j), i, SCpnt->pid);
          }
 
       else if (HD(j)->cp_stat[i] == ABORTING) {
@@ -1235,7 +1238,7 @@ int u14_34f_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
          HD(j)->cp_stat[i] = FREE;
 
          printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
-               BN(j), i, SCpnt->pid);
+                BN(j), i, SCpnt->pid);
          }
 
       else
@@ -1428,224 +1431,182 @@ static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
       }
 }
 
-static void u14_34f_interrupt_handler(int irq, void *dev_id,
-                                      struct pt_regs *regs) {
+static void u14_34f_interrupt_handler(int irq, void *shap,
+                                               struct pt_regs *regs) {
    Scsi_Cmnd *SCpnt;
-   unsigned long flags;
-   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, reg, ret;
+   unsigned int i, j, k, c, status, tstatus, reg, ret;
    struct mscp *spp;
 
-   save_flags(flags);
-   cli();
+   /* Check if the interrupt must be processed by this handler */
+   if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
 
-   if (!irqlist[irq]) {
-      printk("%s, ihdlr, irq %d, unexpected interrupt.\n", driver_name, irq);
-      restore_flags(flags);
+   if (sh[j]->irq != irq)
+       panic("%s: ihdlr, irq %d, sh[j]->irq %d.\n", BN(j), irq, sh[j]->irq);
+
+   if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
+                        HD(j)->iocount);
+
+   /* Check if this board need to be serviced */
+   if (!((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED)) return;
+
+   spp = (struct mscp *)DEV2V(ret = inl(sh[j]->io_port + REG_ICM));
+
+   /* Clear interrupt pending flag */
+   outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+   i = spp - HD(j)->cp;
+
+   if (spp < HD(j)->cp || spp >= HD(j)->cp + sh[j]->can_queue
+                                     || i >= sh[j]->can_queue)
+      panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j),
+            (void *)ret, HD(j)->cp);
+
+   if (HD(j)->cp_stat[i] == IGNORE) {
+      HD(j)->cp_stat[i] = FREE;
+      return;
+      }
+   else if (HD(j)->cp_stat[i] == LOCKED) {
+      HD(j)->cp_stat[i] = FREE;
+      printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
+             HD(j)->iocount);
+      return;
+      }
+   else if (HD(j)->cp_stat[i] == FREE) {
+      printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
+             HD(j)->iocount);
       return;
       }
+   else if (HD(j)->cp_stat[i] == IN_RESET)
+      printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+   else if (HD(j)->cp_stat[i] != IN_USE) 
+      panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
 
-   if (do_trace) printk("%s: ihdlr, enter, irq %d, calls %d.\n", 
-                       driver_name, irq, calls[irq]);
-
-   /* Service all the boards configured on this irq */
-   for (j = 0; sh[j] != NULL; j++) {
+   HD(j)->cp_stat[i] = FREE;
+   SCpnt = spp->SCpnt;
 
-      if (sh[j]->irq != irq) continue;
+   if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
 
-      loops = 0;
+   if (SCpnt->host_scribble == NULL) 
+      panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
+            SCpnt->pid, SCpnt);
 
-      /* Loop until all interrupts for a board are serviced */
-      while ((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED) {
-        total_loops++;
-        loops++;
-
-        if (do_trace) printk("%s: ihdlr, start service, count %d.\n",
-                             BN(j), HD(j)->iocount);
+   if (*(unsigned int *)SCpnt->host_scribble != i) 
+      panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d, irq %d.\n", 
+            BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble, irq);
 
-        spp = (struct mscp *)DEV2V(ret = inl(sh[j]->io_port + REG_ICM));
+   if (linked_comm && SCpnt->device->queue_depth > 2
+                                     && TLDEV(SCpnt->device->type))
+      flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
 
-        /* Clear interrupt pending flag */
-        outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
-
-        i = spp - HD(j)->cp;
+   tstatus = status_byte(spp->target_status);
 
-        if (spp < HD(j)->cp || spp >= HD(j)->cp + sh[j]->can_queue
-                                            || i >= sh[j]->can_queue)
-           panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n",
-                  BN(j), (void *)ret, HD(j)->cp);
-
-        if (HD(j)->cp_stat[i] == IGNORE) {
-           HD(j)->cp_stat[i] = FREE;
-           continue;
-           }
-        else if (HD(j)->cp_stat[i] == LOCKED) {
-           HD(j)->cp_stat[i] = FREE;
-           printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
-                  BN(j), i, HD(j)->iocount);
-           continue;
-           }
-        else if (HD(j)->cp_stat[i] == FREE) {
-           printk("%s: ihdlr, mbox %d is free, count %d.\n", 
-                  BN(j), i, HD(j)->iocount);
-           continue;
-           }
-        else if (HD(j)->cp_stat[i] == IN_RESET)
-           printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
-        else if (HD(j)->cp_stat[i] != IN_USE) 
-           panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
-
-        HD(j)->cp_stat[i] = FREE;
-        SCpnt = spp->SCpnt;
-
-        if (SCpnt == NULL) 
-           panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
-
-        if (SCpnt->host_scribble == NULL) 
-           panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n",
-                 BN(j), i, SCpnt->pid, SCpnt);
-
-        if (*(unsigned int *)SCpnt->host_scribble != i) 
-           panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d,"\
-                 " irq %d.\n", BN(j), i, SCpnt->pid, 
-                 *(unsigned int *)SCpnt->host_scribble, irq);
-
-         if (linked_comm && SCpnt->device->queue_depth > 2
-                                           && TLDEV(SCpnt->device->type))
-            flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
-
-        tstatus = status_byte(spp->target_status);
-
-        switch (spp->adapter_status) {
-           case ASOK:     /* status OK */
-
-              /* Forces a reset if a disk drive keeps returning BUSY */
-              if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE) 
-                 status = DID_ERROR << 16;
-
-              /* If there was a bus reset, redo operation on each target */
-              else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
-                       && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
-                 status = DID_BUS_BUSY << 16;
-
-              /* Works around a flaw in scsi.c */
-              else if (tstatus == CHECK_CONDITION
-                       && SCpnt->device->type == TYPE_DISK
-                       && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
-                 status = DID_BUS_BUSY << 16;
-
-              else
-                 status = DID_OK << 16;
-
-              if (tstatus == GOOD)
-                 HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
-
-              if (spp->target_status && SCpnt->device->type == TYPE_DISK)
-                 printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
-                         "target_status 0x%x, sense key 0x%x.\n", BN(j), 
-                        SCpnt->channel, SCpnt->target, SCpnt->lun,
-                         SCpnt->pid, spp->target_status,
-                         SCpnt->sense_buffer[2]);
-
-              HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
-
-               if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
-
-              break;
-           case ASST:     /* Selection Time Out */
-
-              if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
-                 status = DID_ERROR << 16;
-              else {
-                 status = DID_TIME_OUT << 16;
-                 HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
-                 }
-
-              break;
-
-            /* Perform a limited number of internal retries */
-           case 0x93:     /* Unexpected bus free */
-           case 0x94:     /* Target bus phase sequence failure */
-           case 0x96:     /* Illegal SCSI command */
-           case 0xa3:     /* SCSI bus reset error */
-
-              for (c = 0; c <= sh[j]->max_channel; c++) 
-                 for (k = 0; k < sh[j]->max_id; k++) 
-                    HD(j)->target_redo[k][c] = TRUE;
-   
+   switch (spp->adapter_status) {
+      case ASOK:     /* status OK */
 
-           case 0x92:     /* Data over/under-run */
-
-              if (SCpnt->device->type != TYPE_TAPE
-                   && HD(j)->retries < MAX_INTERNAL_RETRIES) {
-                 status = DID_BUS_BUSY << 16;
-                 HD(j)->retries++;
-                  HD(j)->last_retried_pid = SCpnt->pid;
-                  }
-              else 
-                 status = DID_ERROR << 16;
-
-              break;
-           case 0x01:     /* Invalid command */
-           case 0x02:     /* Invalid parameters */
-           case 0x03:     /* Invalid data list */
-           case 0x84:     /* SCSI bus abort error */
-           case 0x9b:     /* Auto request sense error */
-           case 0x9f:     /* Unexpected command complete message error */
-           case 0xff:     /* Invalid parameter in the S/G list */
-           default:
-              status = DID_ERROR << 16;
-              break;
-           }
-
-        SCpnt->result = status | spp->target_status;
-        HD(j)->iocount++;
-
-        if (loops > 1) HD(j)->multicount++;
+         /* Forces a reset if a disk drive keeps returning BUSY */
+         if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE) 
+            status = DID_ERROR << 16;
 
-#if defined (DEBUG_INTERRUPT)
-        if (SCpnt->result || do_trace) 
-#else
-        if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
-            (spp->adapter_status != ASOK && 
-             spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
-              do_trace || msg_byte(spp->target_status))
-#endif
-           printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
-                  " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
-                  BN(j), i, spp->adapter_status, spp->target_status,
-                  SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                   reg, HD(j)->iocount);
+         /* If there was a bus reset, redo operation on each target */
+         else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+                  && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
+            status = DID_BUS_BUSY << 16;
+
+         /* Works around a flaw in scsi.c */
+         else if (tstatus == CHECK_CONDITION
+                  && SCpnt->device->type == TYPE_DISK
+                  && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+            status = DID_BUS_BUSY << 16;
 
-        /* Set the command state to inactive */
-        SCpnt->host_scribble = NULL;
+         else
+            status = DID_OK << 16;
 
-        restore_flags(flags);
-        SCpnt->scsi_done(SCpnt);
-        cli();
+         if (tstatus == GOOD)
+            HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
 
-        }   /* Multiple command loop */
+         if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+            printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
+                   "target_status 0x%x, sense key 0x%x.\n", BN(j), 
+                   SCpnt->channel, SCpnt->target, SCpnt->lun,
+                   SCpnt->pid, spp->target_status,
+                   SCpnt->sense_buffer[2]);
 
-      }   /* Boards loop */
+         HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
 
-   calls[irq]++;
+         if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
 
-#if defined (DEBUG_SMP)
-   if (total_loops == 0) 
-     printk("%s: ihdlr, irq %d, no command completed, calls %d.\n",
-           driver_name, irq, calls[irq]);
-#endif
+         break;
+      case ASST:     /* Selection Time Out */
+
+         if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
+            status = DID_ERROR << 16;
+         else {
+            status = DID_TIME_OUT << 16;
+            HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
+            }
 
-   if (do_trace) printk("%s: ihdlr, exit, irq %d, calls %d.\n",
-                       driver_name, irq, calls[irq]);
+         break;
 
-#if defined (DEBUG_STATISTICS)
-   if ((calls[irq] % 100000) == 10000)
-      for (j = 0; sh[j] != NULL; j++)
-        printk("%s: ihdlr, calls %d, count %d, multi %d.\n", BN(j),
-               calls[(sh[j]->irq)], HD(j)->iocount, HD(j)->multicount);
+      /* Perform a limited number of internal retries */
+      case 0x93:     /* Unexpected bus free */
+      case 0x94:     /* Target bus phase sequence failure */
+      case 0x96:     /* Illegal SCSI command */
+      case 0xa3:     /* SCSI bus reset error */
+
+         for (c = 0; c <= sh[j]->max_channel; c++) 
+            for (k = 0; k < sh[j]->max_id; k++) 
+               HD(j)->target_redo[k][c] = TRUE;
+   
+
+      case 0x92:     /* Data over/under-run */
+
+         if (SCpnt->device->type != TYPE_TAPE
+             && HD(j)->retries < MAX_INTERNAL_RETRIES) {
+            status = DID_BUS_BUSY << 16;
+            HD(j)->retries++;
+            HD(j)->last_retried_pid = SCpnt->pid;
+            }
+         else 
+            status = DID_ERROR << 16;
+
+         break;
+      case 0x01:     /* Invalid command */
+      case 0x02:     /* Invalid parameters */
+      case 0x03:     /* Invalid data list */
+      case 0x84:     /* SCSI bus abort error */
+      case 0x9b:     /* Auto request sense error */
+      case 0x9f:     /* Unexpected command complete message error */
+      case 0xff:     /* Invalid parameter in the S/G list */
+      default:
+         status = DID_ERROR << 16;
+         break;
+      }
+
+   SCpnt->result = status | spp->target_status;
+   HD(j)->iocount++;
+
+#if defined (DEBUG_INTERRUPT)
+   if (SCpnt->result || do_trace) 
+#else
+   if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
+       (spp->adapter_status != ASOK && 
+        spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+        do_trace || msg_byte(spp->target_status))
 #endif
+      printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
+             " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+             BN(j), i, spp->adapter_status, spp->target_status,
+             SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
+             reg, HD(j)->iocount);
+
+   /* Set the command state to inactive */
+   SCpnt->host_scribble = NULL;
+
+   SCpnt->scsi_done(SCpnt);
+
+   if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq,
+                        HD(j)->iocount);
 
-   restore_flags(flags);
    return;
 }
 
@@ -1664,7 +1625,7 @@ int u14_34f_release(struct Scsi_Host *shpnt) {
    for (i = 0; i < sh[j]->can_queue; i++) 
       if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
 
-   if (! --irqlist[sh[j]->irq]) free_irq(sh[j]->irq, NULL);
+   free_irq(sh[j]->irq, &sha[j]);
 
    if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel);
 
index 004936393518b9c647be987a4aafa4a6ae8b33b1..7420b6fc60d2d914d1ba3244c686f14a95258ce4 100644 (file)
@@ -11,29 +11,41 @@ int u14_34f_abort(Scsi_Cmnd *);
 int u14_34f_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "3.11.00"
-
-#define ULTRASTOR_14_34F {                                            \
-               NULL, /* Ptr for modules */                           \
-               NULL, /* usage count for modules */                   \
-               NULL,                                                 \
-               NULL,                                                 \
-               "UltraStor 14F/34F rev. " U14_34F_VERSION " ",        \
-               u14_34f_detect,                                       \
-               u14_34f_release,                                      \
-               NULL,                                                 \
-               NULL,                                                 \
-               u14_34f_queuecommand,                                 \
-               u14_34f_abort,                                        \
-               u14_34f_reset,                                        \
-               NULL,                                                 \
-               u14_34f_biosparam,                                    \
-               0,   /* can_queue, reset by detect */                 \
-               7,   /* this_id, reset by detect */                   \
-               0,   /* sg_tablesize, reset by detect */              \
-               0,   /* cmd_per_lun, reset by detect */               \
-               0,   /* number of boards present */                   \
-               1,   /* unchecked isa dma, reset by detect */         \
-               ENABLE_CLUSTERING                                     \
-               }
+#define U14_34F_VERSION "4.02.00"
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,88)
+
+#define ULTRASTOR_14_34F {                                                   \
+                name:         "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
+                detect:       u14_34f_detect,                                \
+                release:      u14_34f_release,                               \
+                queuecommand: u14_34f_queuecommand,                          \
+                abort:        u14_34f_abort,                                 \
+                reset:        u14_34f_reset,                                 \
+                bios_param:   u14_34f_biosparam,                             \
+                this_id:      7,                                             \
+                unchecked_isa_dma: 1,                                        \
+                use_clustering: ENABLE_CLUSTERING,                           \
+                use_new_eh_code: 1    /* Enable new error code */            \
+                }
+
+#else /* Use old scsi code */
+
+#define ULTRASTOR_14_34F {                                                   \
+                name:         "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
+                detect:       u14_34f_detect,                                \
+                release:      u14_34f_release,                               \
+                queuecommand: u14_34f_queuecommand,                          \
+                abort:        u14_34f_abort,                                 \
+                reset:        u14_34f_reset,                                 \
+                bios_param:   u14_34f_biosparam,                             \
+                this_id:      7,                                             \
+                unchecked_isa_dma: 1,                                        \
+                use_clustering: ENABLE_CLUSTERING,                           \
+                }
+
+#endif
+
 #endif
index e37748f9b2e97b2153e9952d22a50ec8fa4bb753..c61c779d178b54d96c252d30509c36c969c99866 100644 (file)
@@ -143,10 +143,8 @@ static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo
                pte_t * pte = pte_alloc_kernel(pmd, address);
                if (!pte)
                        return -ENOMEM;
-               if (alloc_area_pte(pte, address, end - address)) {
-                       pte_free_kernel(pte);
+               if (alloc_area_pte(pte, address, end - address))
                        return -ENOMEM;
-               }
                address = (address + PMD_SIZE) & PMD_MASK;
                pmd++;
        }
@@ -164,10 +162,8 @@ static int alloc_area_pages(unsigned long address, unsigned long size)
                pmd_t *pmd = pmd_alloc_kernel(dir, address);
                if (!pmd)
                        return -ENOMEM;
-               if (alloc_area_pmd(pmd, address, end - address)) {
-                       pmd_free_kernel(pmd);
+               if (alloc_area_pmd(pmd, address, end - address))
                        return -ENOMEM;
-               }
                set_pgdir(address, *dir);
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
@@ -228,10 +224,8 @@ static int remap_area_pages(unsigned long address, unsigned long offset, unsigne
                pmd_t *pmd = pmd_alloc_kernel(dir, address);
                if (!pmd)
                        return -ENOMEM;
-               if (remap_area_pmd(pmd, address, end - address, offset + address)) {
-                       pmd_free_kernel(pmd);
+               if (remap_area_pmd(pmd, address, end - address, offset + address))
                        return -ENOMEM;
-               }
                set_pgdir(address, *dir);
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
index 3095189fb9460b1010ad5f0e068dccabcbc0bdee..0d931896e99d590e3757757ee68e8f010dabdc4d 100644 (file)
@@ -1529,9 +1529,6 @@ int net_dev_init(void)
         *
         * Some devices want to be initialized early..
         */
-#if defined(CONFIG_LANCE)
-       lance_init();
-#endif
 #if defined(CONFIG_PI)
        pi_init();
 #endif