]> git.neil.brown.name Git - history.git/commitdiff
Import 2.0.31pre6 2.0.31pre6
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:30 +0000 (15:11 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:30 +0000 (15:11 -0500)
49 files changed:
CREDITS
Documentation/Configure.help
arch/alpha/kernel/traps.c
drivers/char/random.c
drivers/char/tty_io.c
drivers/char/tty_ioctl.c
drivers/isdn/isdn_tty.c
drivers/net/3c509.c
drivers/net/3c59x.c
drivers/net/Config.in
drivers/net/Makefile
drivers/net/Space.c
drivers/net/dgrs.c
drivers/net/eepro100.c [new file with mode: 0644]
drivers/net/ne.c
drivers/net/smc-ultra32.c
drivers/scsi/ChangeLog.ncr53c8xx
drivers/scsi/Config.in
drivers/scsi/Makefile
drivers/scsi/README.ncr53c8xx
drivers/scsi/aic7xxx.c
drivers/scsi/aic7xxx/aic7xxx.reg
drivers/scsi/aic7xxx/aic7xxx.seq
drivers/scsi/aic7xxx_reg.h
drivers/scsi/aic7xxx_seq.h
drivers/scsi/dc390.h [new file with mode: 0644]
drivers/scsi/hosts.c
drivers/scsi/ncr53c8xx.c
drivers/scsi/ncr53c8xx.h
drivers/scsi/scsiiom.c [new file with mode: 0644]
drivers/scsi/sd.c
drivers/scsi/tmscsim.c [new file with mode: 0644]
drivers/scsi/tmscsim.h [new file with mode: 0644]
drivers/scsi/ultrastor.c
fs/isofs/inode.c
fs/smbfs/dir.c
fs/smbfs/inode.c
fs/smbfs/proc.c
fs/smbfs/sock.c
include/asm-i386/shmparam.h
include/linux/kernel.h
include/linux/proc_fs.h
init/main.c
kernel/sysctl.c
net/ipv4/arp.c
net/ipv4/icmp.c
net/ipv4/ip_input.c
net/ipv4/tcp_input.c
scripts/ksymoops.cc

diff --git a/CREDITS b/CREDITS
index 9949277cbe654d23159567ba78086ece184d3769..c7a3a35a2ec861388d80a4b8c36ee4468d335d27 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -413,7 +413,7 @@ S: Newtown 2042
 S: Australia
 
 N: Ralf Flaxa
-E: rfflaxa@immd4.informatik.uni-erlangen.de
+E: rf@lst.de
 D: The Linux Support Team Erlangen
 D: Creator of LST distribution
 D: Author of installation tool LISA
index 010e58df54a67f13190ac8c5bd1bf0414b29fab5..f35ccc47ebb0d6c65f1c789009ddd20d4fee7d9d 100644 (file)
@@ -472,7 +472,10 @@ CONFIG_SYN_COOKIES
   the same end. SYN cookies use less space than RST cookies,
   but have a small probability of introducing an non timed-out
   failure to connect in the remote TCP. You can use both options
-  simultatenously.
+  simultatenously.  If you are SYN flooded, the source address
+  reported by the kernel is likely to have been forged by the attacker.
+  The source address is reported as an aid in tracing the packets to 
+  their actual source.  
 
 SYN flood protection
 CONFIG_RST_COOKIES
@@ -486,7 +489,10 @@ CONFIG_RST_COOKIES
   The SYN_COOKIES option provides an alternative method to accomplish
   the same end. RST cookies use more space than SYN cookies on your
   machine, but never increase the probability of a frozen connection
-  in a remote TCP. You can use both options simultatenously.
+  in a remote TCP. You can use both options simultatenously.  If you 
+  are SYN flooded, the source address reported by the kernel is likely 
+  to have been forged by the attacker.  The source address is reported 
+  as an aid in tracing the packets to their actual source.  
 
 Sun floppy controller support
 CONFIG_BLK_DEV_SUNFD
@@ -1554,24 +1560,41 @@ CONFIG_SCSI_NCR53C8XX
   of PCI-SCSI controllers. This driver supports parity checking,
   tagged command queuing, fast scsi II transfer up to 10 MB/s with
   narrow scsi devices and 20 MB/s with wide scsi devices.
-  This driver has been tested OK with linux/i386 and is currently
-  untested under linux/Alpha. If you intend to use this driver under
-  linux/Alpha, just try it first with read-only or mounted read-only
-  devices.  Memory mapped io is currently not supported under
-  linux/Alpha. Please read drivers/scsi/README.ncr53c8xx for more
-  information. 
-
-force normal IO
+  Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875 
+  controllers has been recently added to the driver.
+  Please read drivers/scsi/README.ncr53c8xx for more information.
+  Linux/i386 and Linux/Alpha are supported by this driver.
+
+synchronous data transfers frequency
+CONFIG_SCSI_NCR53C8XX_SYNC
+  SCSI-2 specifications allow scsi devices to negotiate a synchronous 
+  transfer period of 25 nano-seconds or more.
+  The transfer period value is 4 times the agreed transfer period.
+  So, data can be transferred at a 10 MHz frequency, allowing 10
+  MB/second throughput with 8 bits scsi-2 devices and 20 MB/second
+  with wide16 devices.  This frequency can be used safely with
+  differential devices but may cause problems with singled-ended
+  devices.
+  Specify 0 if you want to only use asynchronous data transfers.
+  Otherwise, specify a value between 5 and 10.  Commercial O/Ses
+  generally use 5 Mhz frequency for synchronous transfers.  It is a
+  reasonable default value.
+  However, a flawless singled-ended scsi bus supports 10 MHz data
+  transfers.  Regardless the value chosen in the Linux configuration,
+  the synchronous period can be changed after boot-up through the
+  /proc/scsi file system. The generic command is:
+      echo "setsync #target period" >/proc/scsi/ncr53c8xx/0
+  Use a 25 ns period for 10 Mhz synchronous data transfers.
+  If you don't know what to do now, go with the default.
+
+use normal IO
 CONFIG_SCSI_NCR53C8XX_IOMAPPED
-  Under linux/Alpha only normal io is currently supported.
-  Under linux/i386, this option allows you to force the driver to use
-  normal IO.  Memory mapped IO has less latency than normal IO.
-  During the initialization phase, the driver first tries to use
-  memory mapped io.  If nothing seems wrong, it will use memory mapped
-  io.  If a flaw is detected, it will use normal io.  However, it's
-  possible that memory mapped does not work properly for you and the
-  driver has not detected the problem; then you would want to say Y
-  here.  The normal answer therefore is N.
+  This option allows you to force the driver to use normal IO.
+  Memory mapped IO has less latency than normal IO and works for most 
+  Intel-based hardware.
+  Under Linux/Alpha only normal IO is currently supported by the driver 
+  and so, this option has no effect.
+  The normal answer therefore is N.
 
 not allow targets to disconnect
 CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
@@ -1596,36 +1619,51 @@ CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
   The safe answer therefore is N.
   The normal answer therefore is Y.
 
-force asynchronous transfer mode
-CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS
-  This option allows you to force asynchronous transfer mode for all
-  devices at linux startup. You can enable synchronous negotiation
-  with the "setsync" control command after boot-up, for example:
-     echo "setsync 2 25" >/proc/scsi/ncr53c8xx/0
-  asks the driver to set the period to 25 ns (10MB/sec) for target 2
-  of controller 0 (please read drivers/scsi/README.ncr53c8xx for more
-  information). The safe answer therefore is Y.  The normal answer
-  therefore is N.
-force synchronous negotiation
-CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-  Some scsi-2 devices support synchronous negotiations but do not
-  report this feature in byte 7 of inquiry data.
-  Answer Y only if you suspect some device to be so humble.
-  The normal answer therefore is N.
-disable master parity checking
-CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-  Some hardware may have problems with parity during master cycles on
-  PCI bus.  Only seen once. Answer Y if you suspect such problem.  The
-  normal answer therefore is N.
-
-disable scsi parity checking
-CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
-  Parity on scsi bus is a system option. If one device checks parity,
-  then all devices on the scsi bus must generate parity.  However, the
-  parity can be ignored by the scsi devices.  Answer Y only if you
-  know what you are doing.  The normal answer therefore is N.
+maximum number of queued commands
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+  This option allows you to specify the maximum number of commands
+  that can be queued to a device, when tagged command queuing is
+  possible.  The default value is 4. Minimum is 2, maximum is 12.  The
+  normal answer therefore is the default one.
+
+detect and read serial NVRAM
+CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+  Enable support for reading the serial NVRAM data on Symbios and
+  some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for
+  systems with more than one Symbios compatible controller where at least
+  one has a serial NVRAM, or for a system with a mixture of Symbios and
+  Tekram cards. Enables setting the boot order of host adaptors
+  to something other than the default order or "reverse probe" order. 
+  Also enables Symbios and Tekram cards to be distinguished so
+  CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a
+  mixture of Symbios and Tekram cards so the Symbios cards can make use of
+  the full range of Symbios features, differential, led pin, without
+  causing problems for the Tekram card(s).
+      (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+  Also enables setting host and targets SCSI features as defined in the 
+  user setup for each host using a serial NVRAM (added by the maintainer).
+  The default answer is N, the normal answer should be Y.
+  Read drivers/scsi/README.ncr53c8xx for more information.
+
+assume boards are SYMBIOS compatible
+CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+  This option allows you to enable some features depending on GPIO
+  wiring.  These General Purpose Input/Output pins can be used for
+  vendor specific features or implementation of the standard SYMBIOS
+  features.  Genuine SYMBIOS boards use GPIO0 in output for controller
+  LED and GPIO3 bit as a flag indicating singled-ended/differential
+  interface.
+  If all the boards of your system are genuine SYMBIOS boards or use
+  BIOS and drivers from SYMBIOS, you would want to enable this option.
+  The driver behaves correctly on my system with this option enabled.
+  (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev
+  0x12).  This option must be set to N if your system has at least one
+  53C8XX based scsi board with a vendor-specific BIOS (example: Tekram
+  DC-390/U/W/F).  If unsure, say N.
+  However, if all your non Symbios compatible boards have NvRAM, setting 
+  option CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT allows the driver to 
+  distinguish Symbios compatible boards from other ones.
+  So, you can answer Y if all non Symbios compatible boards have NVRAM.
 
 Always IN2000 SCSI support
 CONFIG_SCSI_IN2000
@@ -1742,6 +1780,13 @@ CONFIG_SCSI_NCR53C406A
   and removed from the running kernel whenever you want), say M here
   and read Documentation/modules.txt.
 
+Tekram DC390(T) (AMD PCscsi) SCSI support
+CONFIG_SCSI_DC390T
+  This driver supports the Tekram DC390(T) PCI SCSI Hostadapter with
+  the Am53C974A chip, and perhaps other cards using the same chip.
+  This driver does _not_ support the DC390W/U/F adaptor with the 
+  NCR/Symbios chips.
+
 AM53/79C974 PCI SCSI support
 CONFIG_SCSI_AM53C974
   This is support for the AM53/79C974 SCSI host adapters. Please read
@@ -2397,6 +2442,12 @@ CONFIG_FMV18X
   FMV-184 and it is not working, you may need to disable Plug & Play
   mode of the card.
 
+Intel EtherExpress/Pro 100B support'
+CONFIG_EEXPRESS_PRO100B
+  If you have an Intel EtherExpress Pro 100 10/100Mbps PCI Ethernet
+  card, answer yes.  As of kernel release 2.0.31 this driver was
+  still experimental.
+
 EtherExpressPro support
 CONFIG_EEXPRESS_PRO
   If you have a network (ethernet) card of this type, say Y and read
index 2c54f580254e0d73d72c2ff584e0988d6eeda97a..694820c96edee99751ec2e71732928e0cb997396 100644 (file)
@@ -420,7 +420,7 @@ asmlinkage long do_entSys(unsigned long a0, unsigned long a1, unsigned long a2,
                          unsigned long a3, unsigned long a4, unsigned long a5,
                          struct pt_regs regs)
 {
-       if (regs.r0 != 112)
+       if (regs.r0 != 112 && regs.r0 < 300)
                printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2);
        return -1;
 }
index c65e286f49cf2e70e4102a3c5f2510bee9d3fc49..2104168a27dc0d94c573a957b6fed908dcf208b3 100644 (file)
@@ -1337,8 +1337,13 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
        do_gettimeofday(&tv);
        seq = tmp[1] + tv.tv_usec+tv.tv_sec*1000000;
 #if 0
-       printk("init_seq(%lx, %lx, %d, %d) = %d\n",
-              saddr, daddr, sport, dport, seq);
+       /*
+         ugh...we can only use in_ntoa once per printk, splitting
+         a single line of info into multiple printk's confuses klogd,
+         and Linus says in_ntoa sucks anyway :)
+       */
+       printk("init_seq(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d) = %d\n",
+               NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, seq);
 #endif
        return (seq);
 }
@@ -1361,7 +1366,7 @@ __u32 secure_tcp_probe_number(__u32 saddr, __u32 daddr,
 
        /*
         * Pick a random secret the first time we open a TCP
-        * connection, and expire secretes older than 5 minutes.
+        * connection, and expire secrets older than 5 minutes.
         */
        if (is_init == 0 || jiffies-secret_timestamp[offset] > 600*HZ) {
                if (is_init == 0) valid_secret[0] = valid_secret[1] = 0;
@@ -1386,14 +1391,14 @@ __u32 secure_tcp_probe_number(__u32 saddr, __u32 daddr,
        if (!validate) {
                if (seq == sseq) seq++;
 #if 0
-               printk("init_seq(%lx, %lx, %d, %d, %d) = %d\n",
-                      saddr, daddr, sport, dport, sseq, seq);
+               printk("init_seq(%d.%d.%d.%d:%d %d.%d.%d.%d:%d, %d) = %d\n",
+                       NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq, seq);
 #endif
                return (seq);
        } else {
                if (seq == sseq || (seq+1) == sseq) {
-                       printk("validated probe(%lx, %lx, %d, %d, %d)\n",
-                               saddr, daddr, sport, dport, sseq);
+                       printk("validated probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n",
+                               NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq);
                        return 1;
                }
                if (jiffies-secret_timestamp[(offset+1)%2] <= 1200*HZ) {
@@ -1405,15 +1410,15 @@ __u32 secure_tcp_probe_number(__u32 saddr, __u32 daddr,
                        seq = tmp[1];
                        if (seq == sseq || (seq+1) == sseq) {
 #ifdef 0
-                               printk("validated probe(%lx, %lx, %d, %d, %d)\n",
-                                       saddr, daddr, sport, dport, sseq);
+                               printk("validated probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n",
+                                       NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq);
 #endif
                                return 1;
                        }
                }
 #ifdef 0
-               printk("failed validation on probe(%lx, %lx, %d, %d, %d)\n",
-                       saddr, daddr, sport, dport, sseq);
+               printk("failed validation on probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n",
+                       NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq);
 #endif
                return 0;
        }
index 2bccbc6a5f6e3e14dd8f4d1798aea91579d71b2c..7f53c7070492e60b589d61cfc257e0ca466bddb8 100644 (file)
@@ -11,7 +11,7 @@
  * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
  *
  * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
- * tty_struct and tty_queue structures.  Previously there was a array
+ * tty_struct and tty_queue structures.  Previously there was an array
  * of 256 tty_struct's which was statically allocated, and the
  * tty_queue structures were allocated at boot time.  Both are now
  * dynamically allocated only when the tty is open.
@@ -44,6 +44,9 @@
  * 
  * Restrict vt switching via ioctl()
  *      -- grif@cs.ucr.edu, 5-Dec-95
+ *
+ * Rewrote init_dev and release_dev to eliminate races.
+ *     -- Bill Hawes <whawes@star.net>, June 97
  */
 
 #include <linux/config.h>
@@ -830,14 +833,27 @@ static int tty_write(struct inode * inode, struct file * file, const char * buf,
                (unsigned int)count);
 }
 
+/* Semaphore to protect creating and releasing a tty */
+static struct semaphore tty_sem = MUTEX;
+static void down_tty_sem(int index)
+{
+       down(&tty_sem);
+}
+static void up_tty_sem(int index)
+{
+       up(&tty_sem);
+}
+static void release_mem(struct tty_struct *tty, int idx);
+
 /*
- * This is so ripe with races that you should *really* not touch this
- * unless you know exactly what you are doing. All the changes have to be
- * made atomically, or there may be incorrect pointers all over the place.
+ * Rewritten to remove races and properly clean up after a failed open.  
+ * The new code protects the open with a semaphore, so it's really 
+ * quite straightforward.  The semaphore locking can probably be
+ * relaxed for the (most common) case of reopening a tty.
  */
 static int init_dev(kdev_t device, struct tty_struct **ret_tty)
 {
-       struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
+       struct tty_struct *tty, *o_tty;
        struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
        struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
        struct tty_driver *driver;      
@@ -849,163 +865,221 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty)
                return -ENODEV;
 
        idx = MINOR(device) - driver->minor_start;
-       tty = o_tty = NULL;
+
+       /* 
+        * Check whether we need to acquire the tty semaphore to avoid
+        * race conditions.  For now, play it safe.
+        */
+       down_tty_sem(idx);
+
+       /* check whether we're reopening an existing tty */
+       tty = driver->table[idx];
+       if(tty) goto fast_track;
+
+       /*
+        * First time open is complex, especially for PTY devices.
+        * This code guarantees that either everything succeeds and the
+        * TTY is ready for operation, or else the table slots are vacated
+        * and the allocated memory released.  (Except that the termios 
+        * and locked termios may be retained.)
+        */
+
+       o_tty = NULL;
        tp = o_tp = NULL;
        ltp = o_ltp = NULL;
-       o_tty_loc = NULL;
-       o_tp_loc = o_ltp_loc = NULL;
 
-       tty_loc = &driver->table[idx];
-       tp_loc = &driver->termios[idx];
-       ltp_loc = &driver->termios_locked[idx];
+       tty = (struct tty_struct*) get_free_page(GFP_KERNEL);
+       if(!tty)
+               goto fail_no_mem;
+       initialize_tty_struct(tty);
+       tty->device = device;
+       tty->driver = *driver;
 
-repeat:
-       retval = -EIO;
-       if (driver->type == TTY_DRIVER_TYPE_PTY &&
-           driver->subtype == PTY_TYPE_MASTER &&
-           *tty_loc && (*tty_loc)->count)
-               goto end_init;
-       retval = -ENOMEM;
-       if (!*tty_loc && !tty) {
-               if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
-                       goto end_init;
-               initialize_tty_struct(tty);
-               tty->device = device;
-               tty->driver = *driver;
-               goto repeat;
-       }
-       if (!*tp_loc && !tp) {
+       tp_loc = &driver->termios[idx];
+       if (!*tp_loc) {
                tp = (struct termios *) kmalloc(sizeof(struct termios),
                                                GFP_KERNEL);
                if (!tp)
-                       goto end_init;
+                       goto free_mem_out;
                *tp = driver->init_termios;
-               goto repeat;
        }
-       if (!*ltp_loc && !ltp) {
+
+       ltp_loc = &driver->termios_locked[idx];
+       if (!*ltp_loc) {
                ltp = (struct termios *) kmalloc(sizeof(struct termios),
                                                 GFP_KERNEL);
                if (!ltp)
-                       goto end_init;
+                       goto free_mem_out;
                memset(ltp, 0, sizeof(struct termios));
-               goto repeat;
        }
-       if (driver->type == TTY_DRIVER_TYPE_PTY) {
-               o_tty_loc = &driver->other->table[idx];
-               o_tp_loc = &driver->other->termios[idx];
-               o_ltp_loc = &driver->other->termios_locked[idx];
 
-               if (!*o_tty_loc && !o_tty) {
-                       kdev_t  o_device;
-                       
-                       o_tty = (struct tty_struct *)
-                               get_free_page(GFP_KERNEL);
-                       if (!o_tty)
-                               goto end_init;
-                       o_device = MKDEV(driver->other->major,
-                                        driver->other->minor_start + idx);
-                       initialize_tty_struct(o_tty);
-                       o_tty->device = o_device;
-                       o_tty->driver = *driver->other;
-                       goto repeat;
-               }
-               if (!*o_tp_loc && !o_tp) {
+       if (driver->type == TTY_DRIVER_TYPE_PTY) {
+               o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL);
+               if (!o_tty)
+                       goto free_mem_out;
+               initialize_tty_struct(o_tty);
+               o_tty->device = (kdev_t) MKDEV(driver->other->major,
+                                       driver->other->minor_start + idx);
+               o_tty->driver = *driver->other;
+
+               o_tp_loc  = &driver->other->termios[idx];
+               if (!*o_tp_loc) {
                        o_tp = (struct termios *)
                                kmalloc(sizeof(struct termios), GFP_KERNEL);
                        if (!o_tp)
-                               goto end_init;
+                               goto free_mem_out;
                        *o_tp = driver->other->init_termios;
-                       goto repeat;
                }
-               if (!*o_ltp_loc && !o_ltp) {
+
+               o_ltp_loc = &driver->other->termios_locked[idx];
+               if (!*o_ltp_loc) {
                        o_ltp = (struct termios *)
                                kmalloc(sizeof(struct termios), GFP_KERNEL);
                        if (!o_ltp)
-                               goto end_init;
+                               goto free_mem_out;
                        memset(o_ltp, 0, sizeof(struct termios));
-                       goto repeat;
                }
-               
+
+               /*
+                * Everything allocated ... set up the o_tty structure.
+                */
+               driver->other->table[idx] = o_tty;
+               if (!*o_tp_loc)
+                       *o_tp_loc = o_tp;
+               if (!*o_ltp_loc)
+                       *o_ltp_loc = o_ltp;
+               o_tty->termios = *o_tp_loc;
+               o_tty->termios_locked = *o_ltp_loc;
+               (*driver->other->refcount)++;
+               if (driver->subtype == PTY_TYPE_MASTER)
+                       o_tty->count++;
+
+               /* Establish the links in both directions */
+               tty->link   = o_tty;
+               o_tty->link = tty;
        }
-       /* Now we have allocated all the structures: update all the pointers.. */
-       if (!*tp_loc) {
+
+       /* 
+        * All structures have been allocated, so now we install them.
+        * Failures after this point use release_mem to clean up, so 
+        * there's no need to null out the local pointers.
+        */
+       driver->table[idx] = tty;
+       if (!*tp_loc)
                *tp_loc = tp;
-               tp = NULL;
-       }
-       if (!*ltp_loc) {
+       if (!*ltp_loc)
                *ltp_loc = ltp;
-               ltp = NULL;
+       tty->termios = *tp_loc;
+       tty->termios_locked = *ltp_loc;
+       (*driver->refcount)++;
+       tty->count++;
+
+       /* 
+        * Structures all installed ... call the ldisc open routines.
+        * If we fail here just call release_mem to clean up.  No need
+        * to decrement the use counts, as release_mem doesn't care.
+        */
+       if (tty->ldisc.open) {
+               retval = (tty->ldisc.open)(tty);
+               if (retval)
+                       goto release_mem_out;
        }
-       if (!*tty_loc) {
-               tty->termios = *tp_loc;
-               tty->termios_locked = *ltp_loc;
-               *tty_loc = tty;
-               (*driver->refcount)++;
-               (*tty_loc)->count++;
-               if (tty->ldisc.open) {
-                       retval = (tty->ldisc.open)(tty);
-                       if (retval < 0) {
-                               (*tty_loc)->count--;
-                               tty = NULL;
-                               goto end_init;
-                       }
-               }
-               tty = NULL;
-       } else {
-               if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
-                       printk("Attempt to open closing tty %s.\n",
-                              tty_name(*tty_loc));
-                       printk("Ack!!!!  This should never happen!!\n");
-                       return -EINVAL;
+       if (o_tty && o_tty->ldisc.open) {
+               retval = (o_tty->ldisc.open)(o_tty);
+               if (retval) {
+                       if (tty->ldisc.close)
+                               (tty->ldisc.close)(tty);
+                       goto release_mem_out;
                }
-               (*tty_loc)->count++;
        }
-       if (driver->type == TTY_DRIVER_TYPE_PTY) {
-               if (!*o_tp_loc) {
-                       *o_tp_loc = o_tp;
-                       o_tp = NULL;
-               }
-               if (!*o_ltp_loc) {
-                       *o_ltp_loc = o_ltp;
-                       o_ltp = NULL;
-               }
-               if (!*o_tty_loc) {
-                       o_tty->termios = *o_tp_loc;
-                       o_tty->termios_locked = *o_ltp_loc;
-                       *o_tty_loc = o_tty;
-                       (*driver->other->refcount)++;
-                       if (o_tty->ldisc.open) {
-                               retval = (o_tty->ldisc.open)(o_tty);
-                               if (retval < 0) {
-                                       (*tty_loc)->count--;
-                                       o_tty = NULL;
-                                       goto end_init;
-                               }
-                       }
-                       o_tty = NULL;
-               }
-               (*tty_loc)->link = *o_tty_loc;
-               (*o_tty_loc)->link = *tty_loc;
-               if (driver->subtype == PTY_TYPE_MASTER)
-                       (*o_tty_loc)->count++;
+       goto success;
+
+       /*
+        * This fast open can be used if the tty is already open.
+        * No memory is allocated, and the only failures are from
+        * attempting to open a closing tty or attempting multiple
+        * opens on a pty master.
+        */
+fast_track:
+       retval = -EIO;
+       if (test_bit(TTY_CLOSING, &tty->flags))
+               goto end_init;
+
+       if (driver->type == TTY_DRIVER_TYPE_PTY &&
+           driver->subtype == PTY_TYPE_MASTER) {
+               /*
+                * special case for PTY masters: only one open permitted, 
+                * and the slave side open count is incremented as well.
+                */
+               if (tty->count)
+                       goto end_init;
+               tty->link->count++;
        }
-       (*tty_loc)->driver = *driver;
-       *ret_tty = *tty_loc;
+       tty->count++;
+       tty->driver = *driver; /* N.B. why do this every time?? */
+
+success:
        retval = 0;
+       *ret_tty = tty;
+       
+       /* All paths come through here to release the semaphore */
 end_init:
-       if (tty)
-               free_page((unsigned long) tty);
-       if (o_tty)
-               free_page((unsigned long) o_tty);
-       if (tp)
-               kfree_s(tp, sizeof(struct termios));
+       up_tty_sem(idx);
+       return retval;
+
+       /* Release locally allocated memory ... nothing placed in slots */
+free_mem_out:
        if (o_tp)
                kfree_s(o_tp, sizeof(struct termios));
+       if (o_tty)
+               free_page((unsigned long) o_tty);
        if (ltp)
                kfree_s(ltp, sizeof(struct termios));
-       if (o_ltp)
-               kfree_s(o_ltp, sizeof(struct termios));
-       return retval;
+       if (tp)
+               kfree_s(tp, sizeof(struct termios));
+       free_page((unsigned long) tty);
+
+fail_no_mem:
+       retval = -ENOMEM;
+       goto end_init;
+
+       /* call the tty release_mem routine to clean out this slot */
+release_mem_out:
+       printk("init_dev: ldisc open failed, clearing slot %d\n", idx);
+       release_mem(tty, idx);
+       goto end_init;
+}
+
+/*
+ * Releases memory associated with a tty structure, and clears out the
+ * driver table slots.
+ */
+static void release_mem(struct tty_struct *tty, int idx)
+{
+       struct tty_struct *o_tty;
+       struct termios *tp;
+
+       if ((o_tty = tty->link) != NULL) {
+               o_tty->driver.table[idx] = NULL;
+               if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+                       tp = o_tty->driver.termios[idx];
+                       o_tty->driver.termios[idx] = NULL;
+                       kfree_s(tp, sizeof(struct termios));
+               }
+               o_tty->magic = 0;
+               (*o_tty->driver.refcount)--;
+               free_page((unsigned long) o_tty);
+       }
+
+       tty->driver.table[idx] = NULL;
+       if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+               tp = tty->driver.termios[idx];
+               tty->driver.termios[idx] = NULL;
+               kfree_s(tp, sizeof(struct termios));
+       }
+       tty->magic = 0;
+       (*tty->driver.refcount)--;
+       free_page((unsigned long) tty);
 }
 
 /*
@@ -1016,8 +1090,7 @@ end_init:
 static void release_dev(struct file * filp)
 {
        struct tty_struct *tty, *o_tty;
-       struct termios *tp, *o_tp, *ltp, *o_ltp;
-       struct task_struct **p;
+       int     pty_master, tty_closing, o_tty_closing, do_sleep;
        int     idx;
        
        tty = (struct tty_struct *)filp->private_data;
@@ -1028,10 +1101,11 @@ static void release_dev(struct file * filp)
 
        tty_fasync(filp->f_inode, filp, 0);
 
-       tp = tty->termios;
-       ltp = tty->termios_locked;
-
        idx = MINOR(tty->device) - tty->driver.minor_start;
+       pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+                     tty->driver.subtype == PTY_TYPE_MASTER);
+       o_tty = tty->link;
+
 #ifdef TTY_PARANOIA_CHECK
        if (idx < 0 || idx >= tty->driver.num) {
                printk("release_dev: bad idx when trying to free (%s)\n",
@@ -1043,15 +1117,15 @@ static void release_dev(struct file * filp)
                       idx, kdevname(tty->device));
                return;
        }
-       if (tp != tty->driver.termios[idx]) {
-               printk("release_dev: driver.termios[%d] not termios for ("
-                      "%s)\n",
+       if (tty->termios != tty->driver.termios[idx]) {
+               printk("release_dev: driver.termios[%d] not termios "
+                      "for (%s)\n",
                       idx, kdevname(tty->device));
                return;
        }
-       if (ltp != tty->driver.termios_locked[idx]) {
-               printk("release_dev: driver.termios_locked[%d] not termios_locked for ("
-                      "%s)\n",
+       if (tty->termios_locked != tty->driver.termios_locked[idx]) {
+               printk("release_dev: driver.termios_locked[%d] not "
+                      "termios_locked for (%s)\n",
                       idx, kdevname(tty->device));
                return;
        }
@@ -1062,10 +1136,6 @@ static void release_dev(struct file * filp)
               tty->count);
 #endif
 
-       o_tty = tty->link;
-       o_tp = (o_tty) ? o_tty->termios : NULL;
-       o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
-
 #ifdef TTY_PARANOIA_CHECK
        if (tty->driver.other) {
                if (o_tty != tty->driver.other->table[idx]) {
@@ -1074,34 +1144,90 @@ static void release_dev(struct file * filp)
                               idx, kdevname(tty->device));
                        return;
                }
-               if (o_tp != tty->driver.other->termios[idx]) {
-                       printk("release_dev: other->termios[%d] not o_termios for ("
-                              "%s)\n",
+               if (o_tty->termios != tty->driver.other->termios[idx]) {
+                       printk("release_dev: other->termios[%d] not o_termios "
+                              "for (%s)\n",
                               idx, kdevname(tty->device));
                        return;
                }
-               if (o_ltp != tty->driver.other->termios_locked[idx]) {
-                       printk("release_dev: other->termios_locked[%d] not o_termios_locked for ("
-                              "%s)\n",
+               if (o_tty->termios_locked != 
+                     tty->driver.other->termios_locked[idx]) {
+                       printk("release_dev: other->termios_locked[%d] not "
+                              "o_termios_locked for (%s)\n",
                               idx, kdevname(tty->device));
                        return;
                }
-
                if (o_tty->link != tty) {
                        printk("release_dev: bad pty pointers\n");
                        return;
                }
        }
 #endif
-       
+       /*
+        * Sanity check: if tty->count is going to zero, there shouldn't be
+        * any waiters on tty->read_wait or tty->write_wait.  We test the
+        * wait queues and kick everyone out _before_ actually starting to
+        * close.  This ensures that we won't block while releasing the tty
+        * structure.
+        *
+        * The test for the o_tty closing is necessary, since the master and
+        * slave sides may close in any order.  If the slave side closes out
+        * first, its count will be one, since the master side holds an open.
+        * Thus this test wouldn't be triggered at the time the slave closes,
+        * so we do it now.
+        *
+        * Note that it's possible for the tty to be opened again while we're
+        * flushing out waiters.  By recalculating the closing flags before
+        * each iteration we avoid any problems.
+        */
+       while (1) {
+               tty_closing = tty->count <= 1;
+               o_tty_closing = o_tty &&
+                       (o_tty->count <= (pty_master ? 1 : 0));
+               do_sleep = 0;
+
+               if (tty_closing) {
+                       if (waitqueue_active(&tty->read_wait)) {
+                               wake_up(&tty->read_wait);
+                               do_sleep++;
+                       }
+                       if (waitqueue_active(&tty->write_wait)) {
+                               wake_up(&tty->write_wait);
+                               do_sleep++;
+                       }
+               }
+               if (o_tty_closing) {
+                       if (waitqueue_active(&o_tty->read_wait)) {
+                               wake_up(&o_tty->read_wait);
+                               do_sleep++;
+                       }
+                       if (waitqueue_active(&o_tty->write_wait)) {
+                               wake_up(&o_tty->write_wait);
+                               do_sleep++;
+                       }
+               }
+               if (!do_sleep)
+                       break;
+
+               printk("release_dev: %s: read/write wait queue active!\n",
+                      tty_name(tty));
+               schedule();
+       }       
+
+       /*
+        * The closing flags are now consistent with the open counts on 
+        * both sides, and we've completed the last operation that could 
+        * block, so it's safe to proceed with closing.
+        */
+
        if (tty->driver.close)
                tty->driver.close(tty, filp);
-       if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
-           tty->driver.subtype == PTY_TYPE_MASTER) {
-               if (--tty->link->count < 0) {
+
+       if (pty_master) {
+               if (--o_tty->count < 0) {
                        printk("release_dev: bad pty slave count (%d) for %s\n",
-                              tty->count, tty_name(tty));
-                       tty->link->count = 0;
+                              o_tty->count, tty_name(o_tty));
+                       o_tty->count = 0;
                }
        }
        if (--tty->count < 0) {
@@ -1109,41 +1235,48 @@ static void release_dev(struct file * filp)
                       tty->count, tty_name(tty));
                tty->count = 0;
        }
-       if (tty->count)
-               return;
 
        /*
-        * We're committed; at this point, we must not block!
+        * Perform some housekeeping before deciding whether to return.
+        *
+        * Set the TTY_CLOSING flag if this was the last open.  In the
+        * case of a pty we may have to wait around for the other side
+        * to close, and TTY_CLOSING makes sure we can't be reopened.
         */
-       if (o_tty) {
-               if (o_tty->count)
-                       return;
-               tty->driver.other->table[idx] = NULL;
-               tty->driver.other->termios[idx] = NULL;
-               kfree_s(o_tp, sizeof(struct termios));
+       if(tty_closing)
+               set_bit(TTY_CLOSING, &tty->flags);
+       if(o_tty_closing)
+               set_bit(TTY_CLOSING, &o_tty->flags);
+
+       /*
+        * If _either_ side is closing, make sure there aren't any
+        * processes that still think tty or o_tty is their controlling
+        * tty.  Also, clear redirect if it points to either tty.
+        */
+       if (tty_closing || o_tty_closing) {
+               struct task_struct *p;
+
+               for_each_task(p) {
+                       if (p->tty == tty || (o_tty && p->tty == o_tty))
+                               p->tty = NULL;
+               }
+
+               if (redirect == tty || (o_tty && redirect == o_tty))
+                       redirect = NULL;
        }
+
+       /* check whether both sides are closing ... */
+       if (!tty_closing || (o_tty && !o_tty_closing))
+               return;
+       filp->private_data = 0;
        
 #ifdef TTY_DEBUG_HANGUP
        printk("freeing tty structure...");
 #endif
-       tty->flags |= (1 << TTY_CLOSING);
-
-       /*
-        * Make sure there aren't any processes that still think this
-        * tty is their controlling tty.
-        */
-       for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
-               if (*p == 0)
-                       continue;
-               if ((*p)->tty == tty)
-                       (*p)->tty = NULL;
-               if (o_tty && (*p)->tty == o_tty)
-                       (*p)->tty = NULL;
-       }
 
        /*
-        * Shutdown the current line discipline, and reset it to
-        * N_TTY.
+        * Shutdown the current line discipline, and reset it to N_TTY.
+        * N.B. why reset ldisc when we're releasing the memory??
         */
        if (tty->ldisc.close)
                (tty->ldisc.close)(tty);
@@ -1155,41 +1288,34 @@ static void release_dev(struct file * filp)
                o_tty->ldisc = ldiscs[N_TTY];
        }
        
-       tty->driver.table[idx] = NULL;
-       if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
-               tty->driver.termios[idx] = NULL;
-               kfree_s(tp, sizeof(struct termios));
-       }
-       if (tty == redirect || o_tty == redirect)
-               redirect = NULL;
        /*
         * Make sure that the tty's task queue isn't activated.  If it
-        * is, take it out of the linked list.
+        * is, take it out of the linked list.  The tqueue isn't used by
+        * pty's, so skip the test for them.
         */
-       cli();
-       if (tty->flip.tqueue.sync) {
-               struct tq_struct *tq, *prev;
-
-               for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
-                       if (tq == &tty->flip.tqueue) {
-                               if (prev)
-                                       prev->next = tq->next;
-                               else
-                                       tq_timer = tq->next;
-                               break;
+       if (tty->driver.type != TTY_DRIVER_TYPE_PTY) {
+               cli();
+               if (tty->flip.tqueue.sync) {
+                       struct tq_struct *tq, *prev;
+
+                       for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
+                               if (tq == &tty->flip.tqueue) {
+                                       if (prev)
+                                               prev->next = tq->next;
+                                       else
+                                               tq_timer = tq->next;
+                                       break;
+                               }
                        }
                }
+               sti();
        }
-       sti();
-       tty->magic = 0;
-       (*tty->driver.refcount)--;
-       free_page((unsigned long) tty);
-       filp->private_data = 0;
-       if (o_tty) {
-               o_tty->magic = 0;
-               (*o_tty->driver.refcount)--;
-               free_page((unsigned long) o_tty);
-       }
+
+       /* 
+        * The release_mem function takes care of the details of clearing
+        * the slots and preserving the termios structure.
+        */
+       release_mem(tty, idx);
 }
 
 /*
@@ -1274,11 +1400,6 @@ retry_open:
        return 0;
 }
 
-/*
- * Note that releasing a pty master also releases the child, so
- * we have to make the redirection checks after that and on both
- * sides of a pty.
- */
 static void tty_release(struct inode * inode, struct file * filp)
 {
        release_dev(filp);
index 1991a0205188d3835000428c6e303cce1e167b36..bb5aed25d89b6ec8c91523b77b1e03ec18347087 100644 (file)
@@ -376,7 +376,6 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
 {
        struct tty_struct * real_tty;
        int retval;
-       int opt = 0;
 
        if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
            tty->driver.subtype == PTY_TYPE_MASTER)
@@ -414,19 +413,19 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
                                    sizeof (struct termios));
                        return 0;
                case TCSETSF:
-                       opt |= TERMIOS_FLUSH;
+                       return set_termios(real_tty, arg, TERMIOS_FLUSH);
                case TCSETSW:
-                       opt |= TERMIOS_WAIT;
+                       return set_termios(real_tty, arg, TERMIOS_WAIT);
                case TCSETS:
-                       return set_termios(real_tty, arg, opt);
+                       return set_termios(real_tty, arg, 0);
                case TCGETA:
                        return get_termio(real_tty,(struct termio *) arg);
                case TCSETAF:
-                       opt |= TERMIOS_FLUSH;
+                       return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_TERMIO);
                case TCSETAW:
-                       opt |= TERMIOS_WAIT;
+                       return set_termios(real_tty, arg, TERMIOS_WAIT | TERMIOS_TERMIO);
                case TCSETA:
-                       return set_termios(real_tty, arg, opt|TERMIOS_TERMIO);
+                       return set_termios(real_tty, arg, TERMIOS_TERMIO);
                case TCXONC:
                        retval = tty_check_change(tty);
                        if (retval)
index a5f31eaf3e29356f97f1fc595b0b90e769202bc2..a629ce27922dde273e08747a81cc294ab68a425a 100644 (file)
@@ -2445,13 +2445,16 @@ isdn_tty_get_msnstr(char *n, char **p)
  * Get phone-number from modem-commandbuffer
  */
 static void
-isdn_tty_getdial(char *p, char *q)
+isdn_tty_getdial(char *p, char *q, int max)
 {
        int first = 1;
 
-       while (strchr("0123456789,#.*WPTS-", *p) && *p) {
-               if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first))
+       max--;
+       while (strchr("0123456789,#.*WPTS-", *p) && *p && (max > 0)) {
+               if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) {
                        *q++ = *p;
+                       max--;
+               }
                p++;
                first = 0;
        }
@@ -3092,7 +3095,7 @@ isdn_tty_parse_at(modem_info * info)
                                break;
                        case 'D':
                                /* D - Dial */
-                               isdn_tty_getdial(++p, ds);
+                               isdn_tty_getdial(++p, ds, sizeof(ds));
                                p += strlen(p);
                                if (!strlen(m->msn))
                                        isdn_tty_modem_result(10, info);
index 33b2b9159f61a421712f7e1f37a9d9283241cc2f..f95e1f1e1d1c5900c32cf8270f32d19f462155e0 100644 (file)
@@ -279,8 +279,10 @@ int el3_probe(struct device *dev)
        memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
        dev->base_addr = ioaddr;
        dev->irq = irq;
-       dev->if_port = if_port;
-
+       if (dev->mem_start)
+               dev->if_port = dev->mem_start & 3;
+       else
+               dev->if_port = if_port;
        request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
 
        {
@@ -692,8 +694,9 @@ el3_rx(struct device *dev)
                                printk("%s: Couldn't allocate a sk_buff of size %d.\n",
                                           dev->name, pkt_len);
                }
-               lp->stats.rx_dropped++;
                outw(RxDiscard, ioaddr + EL3_CMD);
+               lp->stats.rx_dropped++;
+               SLOW_DOWN_IO;
                while (inw(ioaddr + EL3_STATUS) & 0x1000)
                        printk("        Waiting for 3c509 to discard packet, status %x.\n",
                                   inw(ioaddr + EL3_STATUS) );
@@ -724,7 +727,7 @@ set_multicast_list(struct device *dev)
                outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
        }
        else
-                outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+               outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
 }
 
 static int
index ace8c584141b8e36f10ffc59ad247f69101d5397..1427e2b97cb781ebc0ed436102efaca2061f9a09 100644 (file)
@@ -40,14 +40,8 @@ static char *version =
    Setting to > 1512 effectively disables this feature. */
 static const rx_copybreak = 200;
 
-#include <linux/config.h>
-#ifdef MODULE
 #include <linux/version.h>
 #include <linux/module.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
index 97981b12d1e4cf5af0bab9e65bdf88667c451632..87445f6bbe2cfc5c72fc88320dea0f74c028531e 100644 (file)
@@ -50,7 +50,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
       tristate '3c507 support' CONFIG_EL16
     fi
     tristate '3c509/3c579 support' CONFIG_EL3
-    tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
+    tristate '3c590/3c900 series (592/595/597/900/905) "Vortex/Boomerang" support' CONFIG_VORTEX
   fi
   bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
   if [ "$CONFIG_LANCE" = "y" ]; then
@@ -98,6 +98,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
       tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
     fi
     tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT
+    tristate 'Intel EtherExpress/Pro 100B support' CONFIG_EEXPRESS_PRO100B
     tristate 'DE425, DE434, DE435, DE450, DE500 support' CONFIG_DE4X5
     tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
     tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
index 9939b4a43ec8905e265041b1c6cb2c4c5675fe28..d1d4ca903f8c0fd0d0d638ef289b55369857004b 100644 (file)
@@ -320,6 +320,15 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_EEXPRESS_PRO100B),y)
+L_OBJS += eepro100.o
+else
+  ifeq ($(CONFIG_EEXPRESS_PRO100B),m)
+  M_OBJS += eepro100.o
+  endif
+endif
+
+
 ifeq ($(CONFIG_WAVELAN),y)
 L_OBJS += wavelan.o
 else
index 7697ed63842d9738b05881c291188a50d4908a64..8ee8257e6f3285feebb023a672eba6b700895aae 100644 (file)
@@ -50,6 +50,7 @@ extern int hp_plus_probe(struct device *dev);
 extern int znet_probe(struct device *);
 extern int express_probe(struct device *);
 extern int eepro_probe(struct device *);
+extern int eepro100_probe(struct device *);
 extern int el3_probe(struct device *);
 extern int at1500_probe(struct device *);
 extern int at1700_probe(struct device *);
@@ -163,6 +164,9 @@ ethif_probe(struct device *dev)
 #ifdef CONFIG_EEXPRESS_PRO     /* Intel EtherExpress Pro/10 */
        && eepro_probe(dev)
 #endif
+#ifdef CONFIG_EEXPRESS_PRO100B /* Intel EtherExpress Pro100B */
+       && eepro100_probe(dev)
+#endif
 #ifdef CONFIG_DEPCA            /* DEC DEPCA */
        && depca_probe(dev)
 #endif
index 9f0d399c6e37c9421511b759b7a016d3ae5f2476..19896ff95fc751c93a75d3c159b825eeac967198 100644 (file)
@@ -97,7 +97,6 @@ static char *version = "$Id: dgrs.c,v 1.12 1996/12/21 13:43:58 rick Exp $";
 #include <linux/skbuff.h>
 
 #include <linux/types.h>
-#include <linux/config.h>      /* for CONFIG_PCI */
 
 /*
  *     API changed at linux version 2.1.0
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
new file mode 100644 (file)
index 0000000..ffa0e50
--- /dev/null
@@ -0,0 +1,1731 @@
+/* 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 by Donald Becker.
+
+       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
+       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">
+*/
+
+static const char *version =
+"eepro100.c:v0.32 4/8/97 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. */
+
+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. */
+static int txdmacount = 0;     /* Tx DMA burst length, 0-127, default 0. */
+static int rxdmacount = 0;     /* Rx DMA length, 0 means no preemption. */
+
+/* If defined use the copy-only-tiny-buffer scheme for higher performance.
+   The value sets the copy breakpoint.  Lower uses more memory, but is
+   faster. */
+#define SKBUFF_RX_COPYBREAK 256
+
+#include <linux/config.h>
+#include <linux/version.h>
+#ifdef MODULE
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.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 <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* A nominally proper method to handle version dependencies is to use
+   LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */
+#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s)
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,0))
+#define KERNEL_1_2
+#else /* 1.3.0 */
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+#define NEW_MULTICAST
+#define LINUX_1_4
+#else
+#warning "This driver is tested for 1.3.44 and later development kernels only."
+#endif /* 1.3.44 */
+#endif
+#else
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#ifdef HAVE_HEADER_CACHE
+#define LINUX_1_4
+#define NEW_MULTICAST
+#else
+#ifdef ETH_P_DDCMP                             /* Warning: Bogus!  This means IS_LINUX_1_3. */
+#define KERNEL_1_3
+#else
+#define KERNEL_1_2
+#endif
+#endif
+
+#endif
+/* This should be in a header file. */
+#if (LINUX_VERSION_CODE < VERSION(1,3,44))
+struct device *init_etherdev(struct device *dev, int sizeof_priv,
+                                                        unsigned long *mem_startp);
+#endif
+#if LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)                  /* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+/* The total I/O port extent of the board.  Nominally 0x18, but rounded up
+   for PCI allocation. */
+#define SPEEDO3_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry eepro100_drv =
+{"EEPro-100", eepro100_init, SPEEDO3_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef SPEEDO3_DEBUG
+int speedo_debug = SPEEDO3_DEBUG;
+#else
+int speedo_debug = 3;
+#endif
+
+/*
+                               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 recieve 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!
+
+*/
+
+/* 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  ((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK      16
+
+/* 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);
+}
+
+/* Operational parameter that usually are not changed. */
+
+#ifndef PCI_VENDOR_ID_INTEL            /* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_INTEL            0x8086 /* Hmmmm, how did they pick that? */
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82557
+#define PCI_DEVICE_ID_INTEL_82557      0x1229
+#endif
+
+/* 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. */
+};
+
+/* 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];
+};
+
+/* 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;
+};
+
+/* 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. */
+};
+
+/* 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];
+#if (LINUX_VERSION_CODE < 0x10300)     /* Kernel v1.2.*. */
+       struct RxFD saved_skhead[RX_RING_SIZE]; /* Saved skbuff header chunk. */
+#endif
+       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. */
+       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. */
+};
+
+/* 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", "unknown-7",
+       "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, PhyUndefined, 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 options);
+
+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);
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs);
+#endif
+static int speedo_close(struct device *dev);
+static struct enet_statistics *speedo_get_stats(struct device *dev);
+static void set_rx_mode(struct device *dev);
+
+\f
+
+#ifdef MODULE
+/* 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 options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int debug = -1;                 /* The debug level */
+
+/* A list of all installed Speedo devices, for removing the driver module. */
+static struct device *root_speedo_dev = NULL;
+#endif
+
+int eepro100_init(struct device *dev)
+{
+       int cards_found = 0;
+
+       if (pcibios_present()) {
+               int pci_index;
+               for (pci_index = 0; pci_index < 8; pci_index++) {
+                       unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+                       int pci_ioaddr;
+#else
+                       long pci_ioaddr;
+#endif
+                       unsigned short pci_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);
+                       if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+                               printk("  PCI Master Bit has not been set! Setting...\n");
+                               pci_command |= PCI_COMMAND_MASTER;
+                               pcibios_write_config_word(pci_bus, pci_device_fn,
+                                                                                 PCI_COMMAND, pci_command);
+                       }
+                       pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                                                PCI_LATENCY_TIMER, &pci_latency);
+                       if (pci_latency < 10) {
+                               printk("  PCI latency timer (CFLT) is unreasonably low at %d."
+                                          "  Setting to 255 clocks.\n", pci_latency);
+                               pcibios_write_config_byte(pci_bus, pci_device_fn,
+                                                                                 PCI_LATENCY_TIMER, 255);
+                       } else if (speedo_debug > 1)
+                               printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
+
+#ifdef MODULE
+                       speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found]);
+#else
+                       speedo_found1(dev, pci_ioaddr, pci_irq_line,
+                                                 dev ? dev->mem_start : 0);
+#endif
+                       cards_found++;
+               }
+       }
+
+       return cards_found;
+}
+
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
+{
+       static int did_version = 0;                     /* Already printed version info. */
+       struct speedo_private *sp;
+       int i;
+       u16 eeprom[0x40];
+
+       if (speedo_debug > 0  &&  did_version++ == 0)
+               printk(version);
+
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+       dev = init_etherdev(dev, sizeof(struct speedo_private));
+#else
+       dev = init_etherdev(dev, sizeof(struct speedo_private), 0);
+#endif
+
+       /* 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++) {
+                       unsigned short 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. */
+       }
+
+       /* 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);
+
+       printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ",
+                  dev->name, ioaddr);
+       for (i = 0; i < 5; i++)
+               printk("%2.2X:", dev->dev_addr[i]);
+       printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#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. */
+       {
+               const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
+               /* The self-test results must be paragraph aligned. */
+               int 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 defined(notdef)
+               /* ToDo: Read and set PHY registers through MDIO port. */
+               for (i = 0; i < 2; i++)
+                       printk("  MDIO register %d is %4.4x.\n",
+                                  i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+               for (i = 5; i < 7; i++)
+                       printk("  MDIO register %d is %4.4x.\n",
+                                  i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+               printk("  MDIO register %d is %4.4x.\n",
+                          25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25));
+#endif
+               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("  DP83840 specific setup, setting register 23 to %4.4x.\n",
+                                  mdi_reg23);
+                       mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
+               }
+               if ((options >= 0) && (options & 0x60)) {
+                       printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n",
+                                  (options & 0x20 ? 100 : 10),
+                                  (options & 0x10 ? "full" : "half"));
+                       mdio_write(ioaddr, eeprom[6] & 0x1f, 0,
+                                          ((options & 0x20) ? 0x2000 : 0) |    /* 100mbps? */
+                                          ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+               }
+
+               /* Perform a system self-test. */
+               self_test_results = (int*) ((((int) str) + 15) & ~0xf);
+               self_test_results[0] = 0;
+               self_test_results[1] = -1;
+               outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
+               do {
+#ifdef _LINUX_DELAY_H
+                       udelay(10);
+#else
+                       SLOW_DOWN_IO;
+#endif
+               } 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]);
+       }
+#endif  /* kernel_bloat */
+
+       /* We do a request_region() only to register /proc/ioports info. */
+       request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
+
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+
+       if (dev->priv == NULL)
+               dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
+       sp = dev->priv;
+       memset(sp, 0, sizeof(*sp));
+#ifdef MODULE
+       sp->next_module = root_speedo_dev;
+       root_speedo_dev = dev;
+#endif
+
+       sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
+       sp->default_port = options >= 0 ? (options & 0x0f) : 0;
+
+       sp->phy[0] = eeprom[6];
+       sp->phy[1] = eeprom[7];
+       sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
+
+       printk(KERN_INFO "  Operating in %s duplex mode.\n",
+                  sp->full_duplex ? "full" : "half");
+       if (sp->rx_bug)
+         printk(KERN_INFO "  Reciever lock-up workaround activated.\n");
+
+       /* 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;
+#ifdef NEW_MULTICAST
+       dev->set_multicast_list = &set_rx_mode;
+#endif
+
+       return;
+}
+\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 is a "nasty" timing loop, but PC compatible machines are defined
+   to delay an ISA compatible period for the SLOW_DOWN_IO macro.  */
+#define eeprom_delay(nanosec)  do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* 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 read_cmd = location | EE_READ_CMD;
+       
+       outw(EE_ENB & ~EE_CS, ee_addr);
+       outw(EE_ENB, ee_addr);
+       
+       /* Shift the read command bits out. */
+       for (i = 10; 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);
+               outw(EE_ENB | dataval, ee_addr);        /* Finish EEPROM a clock tick. */
+               eeprom_delay(250);
+       }
+       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);
+       }
+
+       /* Terminate the EEPROM access. */
+       outw(EE_ENB & ~EE_CS, ee_addr);
+       return retval;
+}
+
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+       int val, boguscnt = 64*4;               /* <64 usec. to complete, typ 27 ticks */
+       outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
+       do {
+#ifdef _LINUX_DELAY_H
+               udelay(16);
+#else
+               SLOW_DOWN_IO;
+#endif
+               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*4;               /* <64 usec. to complete, typ 27 ticks */
+       outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
+                ioaddr + SCBCtrlMDI);
+       do {
+#ifdef _LINUX_DELAY_H
+               udelay(16);
+#else
+               SLOW_DOWN_IO;
+#endif
+               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)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+
+#ifdef notdef
+       /* We could reset the chip, but should not need to. */
+       outl(0, ioaddr + SCBPort);
+       for (i = 40; i >= 0; i--)
+               SLOW_DOWN_IO;                   /* At least 250ns */
+#endif
+
+#ifdef SA_SHIRQ
+       if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
+                                       "Intel EtherExpress Pro 10/100 Ethernet", dev)) {
+               return -EAGAIN;
+       }
+#else
+#ifdef USE_SHARED_IRQ
+       if (request_shared_irq(dev->irq, &speedo_interrupt, dev,
+                                                  "Intel EtherExpress Pro 10/100 Ethernet"))
+               return -EAGAIN;
+#else
+       if (dev->irq < 2  ||  dev->irq > 15  ||  irq2dev_map[dev->irq] != NULL)
+               return -EAGAIN;
+       irq2dev_map[dev->irq] = dev;
+       if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) {
+               irq2dev_map[dev->irq] = NULL;
+               return -EAGAIN;
+       }
+#endif
+#endif
+
+       if (speedo_debug > 1)
+               printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
+
+       MOD_INC_USE_COUNT;
+
+       /* Load the statistics block address. */
+       outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
+       outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
+       sp->lstats.done_marker = 0;
+
+       speedo_init_rx_ring(dev);
+       outl(0, ioaddr + SCBPointer);
+       outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+
+       /* 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);
+
+       /* Fill the first command with our physical address. */
+       { 
+               unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+               unsigned short *setup_frm = (short *)&(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;
+
+       outl(0, ioaddr + SCBPointer);
+       outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
+
+       dev->if_port = sp->default_port;
+
+       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);
+
+       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;
+#ifndef KERNEL_1_2
+               skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+#else
+               skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#endif
+               sp->rx_skbuff[i] = skb;
+               if (skb == NULL)
+                       break;                  /* Bad news!  */
+               skb->dev = dev;                 /* Mark as being used by this device. */
+
+#if LINUX_VERSION_CODE >= 0x10300
+               rxf = (struct RxFD *)skb->tail;
+               skb_reserve(skb, sizeof(struct RxFD));
+#else
+               /* Save the data in the header region -- it's restored later. */
+               rxf = (struct RxFD *)(skb->data - sizeof(struct RxFD));
+               memcpy(&sp->saved_skhead[i], rxf, sizeof(struct RxFD));
+#endif
+               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. */
+#if LINUX_VERSION_CODE < 0x10300
+               /* This field unused by i82557, we use it as a consistency check. */
+               rxf->rx_buf_addr = virt_to_bus(skb->data);
+#else
+               rxf->rx_buf_addr = virt_to_bus(skb->tail);
+#endif
+               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;
+       int i;
+
+       printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
+                  "command %4.4x.\n",
+                  dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
+#ifndef final_version
+       printk("%s:  Tx timeout  fill index %d  scavenge index %d.\n",
+                  dev->name, sp->cur_tx, sp->dirty_tx);
+       printk("    Tx queue ");
+       for (i = 0; i < TX_RING_SIZE; i++)
+         printk(" %8.8x", (int)sp->tx_ring[i].status);
+       printk(".\n    Rx ring ");
+       for (i = 0; i < RX_RING_SIZE; i++)
+         printk(" %8.8x", (int)sp->rx_ringp[i]->status);
+       printk(".\n");
+               
+#else
+       dev->if_port ^= 1;
+       printk("  (Media type switching not yet implemented.)\n");
+       /* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */
+#endif
+       if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
+         printk("%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);
+       }
+       sp->stats.tx_errors++;
+       dev->trans_start = jiffies;
+       return;
+}
+
+static int
+speedo_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+       int entry;
+
+       if (skb == NULL || skb->len <= 0) {
+               printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
+                          dev->name);
+               dev_tint(dev);
+               return 0;
+       }
+
+       /* 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 (set_bit(0, (void*)&dev->tbusy) != 0) {
+               int tickssofar = jiffies - dev->trans_start;
+               if (tickssofar < TX_TIMEOUT - 2)
+                       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;
+       }
+
+       /* 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. */
+               outw(CU_RESUME, ioaddr + SCBCmd);
+               restore_flags(flags);
+       }
+
+       /* 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;
+
+       return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs)
+#endif
+{
+#ifdef SA_SHIRQ
+       struct device *dev = (struct device *)dev_instance;
+#else
+#ifdef USE_SHARED_IRQ
+       struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
+#else
+       struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+#endif
+       struct speedo_private *sp;
+       int ioaddr, boguscnt = INTR_WORK;
+       unsigned short status;
+
+#ifndef final_version
+       if (dev == NULL) {
+               printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq);
+               return;
+       }
+#endif
+
+       ioaddr = dev->base_addr;
+       sp = (struct speedo_private *)dev->priv;
+#ifndef final_version
+       if (dev->interrupt) {
+               printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+               return;
+       }
+       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) {
+#ifdef notdef
+                 int i;
+                 printk(KERN_WARNING"%s: The EEPro100 receiver left the ready"
+                                " state -- %4.4x!  Index %d (%d).\n", dev->name, status,
+                                sp->cur_rx, sp->cur_rx % RX_RING_SIZE);
+                 printk("   Rx ring:\n ");
+                 for (i = 0; i < RX_RING_SIZE; i++)
+                       printk("   %d %8.8x %8.8x %8.8x %d %d.\n",
+                                  i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link,
+                                  sp->rx_ringp[i]->rx_buf_addr, sp->rx_ringp[i]->count,
+                                  sp->rx_ringp[i]->size);
+#endif
+
+                 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 canidate %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("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;
+                               dev->tbusy = 0;
+                               mark_bh(NET_BH);
+                       }
+
+                       sp->dirty_tx = dirty_tx;
+               }
+
+               if (--boguscnt < 0) {
+                       printk("%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));
+
+#ifndef final_version
+       /* Special code for testing *only*. */
+       {
+               static int stopit = 100;
+               if (dev->start == 0  &&  --stopit < 0) {
+                       printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n",
+                                  dev->name);
+#ifdef SA_SHIRQ
+                       free_irq(irq, dev);
+#else
+                       free_irq(irq);
+#endif
+               }
+       }
+#endif
+
+       dev->interrupt = 0;
+       return;
+}
+
+static int
+speedo_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("%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("%s: Anomalous event in speedo_rx(), status %8.8x.\n",
+                                  dev->name, status);
+               } else {
+                       /* Malloc up new buffer, compatible with net-2e. */
+                       short pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
+                       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 > SKBUFF_RX_COPYBREAK) {
+                               struct sk_buff *newskb;
+                               char *temp;
+
+                               /* Pass up the skb already on the Rx ring. */
+                               skb = sp->rx_skbuff[entry];
+#ifdef KERNEL_1_2
+                               temp = skb->data;
+                               if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+                                       printk("%s: Warning -- the skbuff addresses do not match"
+                                                  " in speedo_rx: %p vs. %p / %p.\n", dev->name,
+                                                  bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+                                                  temp, skb->data);
+                               /* Get a fresh skbuff to replace the filled one. */
+                               newskb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#else
+                               temp = skb_put(skb, pkt_len);
+                               if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+                                       printk("%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));
+#endif
+                               if (newskb) {
+                                       struct RxFD *rxf;
+                                       rx_in_place = 1;
+                                       sp->rx_skbuff[entry] = newskb;
+                                       newskb->dev = dev;
+#ifdef KERNEL_1_2
+                                       /* Restore the data in the old header region. */
+                                       memcpy(skb->data - sizeof(struct RxFD),
+                                                  &sp->saved_skhead[entry], sizeof(struct RxFD));
+                                       /* Save the data in this header region. */
+                                       rxf = (struct RxFD *)(newskb->data - sizeof(struct RxFD));
+                                       sp->rx_ringp[entry] = rxf;
+                                       memcpy(&sp->saved_skhead[entry], rxf, sizeof(struct RxFD));
+                                       rxf->rx_buf_addr = virt_to_bus(newskb->data);
+#else
+                                       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);
+#endif
+                                       rxf->status = 0x00000001;
+                               } else                  /* No memory, drop the packet. */
+                                 skb = 0;
+                       } else
+#ifdef KERNEL_1_2
+                               skb = alloc_skb(pkt_len, GFP_ATOMIC);
+#else
+                               skb = dev_alloc_skb(pkt_len + 2);
+#endif
+                       if (skb == NULL) {
+                               int i;
+                               printk("%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++;
+                               }
+                               break;
+                       }
+                       skb->dev = dev;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+                       if (! rx_in_place) {
+                               skb_reserve(skb, 2);    /* 16 byte align the data fields */
+                               memcpy(skb_put(skb, pkt_len),
+                                          bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+                       }
+                       skb->protocol = eth_type_trans(skb, dev);
+#else
+#ifdef KERNEL_1_3
+#warning This code has only been tested with later 1.3.* kernels.
+                       skb->len = pkt_len;
+                       memcpy(skb->data, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+                                  pkt_len);
+                       /* Needed for 1.3.*. */
+                       skb->protocol = eth_type_trans(skb, dev);
+#else  /* KERNEL_1_2 */
+                       skb->len = pkt_len;
+                       if (! rx_in_place) {
+                               memcpy(skb->data,
+                                          bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+                       }
+#endif
+#endif
+                       netif_rx(skb);
+                       sp->stats.rx_packets++;
+               }
+
+               /*      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;
+               }
+       }
+
+       sp->last_rx_time = jiffies;
+       return 0;
+}
+
+static int
+speedo_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;
+
+       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);
+
+#ifdef SA_SHIRQ
+       free_irq(dev->irq, dev);
+#else
+       free_irq(dev->irq);
+       irq2dev_map[dev->irq] = 0;
+#endif
+
+       /* 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);
+       }
+
+       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) {
+               printk("%s:Printing Rx ring (next to receive into %d).\n",
+                          dev->name, sp->cur_rx);
+
+               for (i = 0; i < RX_RING_SIZE; i++)
+                       printk("  Rx ring entry %d  %8.8x.\n",
+                                  i, (int)sp->rx_ringp[i]->status);
+
+               for (i = 0; i < 5; i++)
+                       printk("  PHY index %d register %d is %4.4x.\n",
+                                  1, i, mdio_read(ioaddr, 1, i));
+               for (i = 21; i < 26; i++)
+                       printk("  PHY index %d register %d is %4.4x.\n",
+                                  1, i, mdio_read(ioaddr, 1, 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!
+   */
+static struct enet_statistics *
+speedo_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)
+                       outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+       }
+       return &sp->stats;
+}
+
+/* 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) {
+               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. */
+               outw(CU_RESUME, ioaddr + SCBCmd);
+               sp->last_cmd = &sp->config_cmd;
+               restore_flags(flags);
+               if (speedo_debug > 5) {
+                       int i;
+                       printk(" 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;
+               unsigned short *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 = (short *)&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 = (unsigned short *)mclist->dmi_addr;
+                       *setup_params++ = *eaddrs++;
+                       *setup_params++ = *eaddrs++;
+                       *setup_params++ = *eaddrs++;
+               }
+
+               sp->last_cmd->command &= ~CmdSuspend;
+               /* Immediately trigger the command unit resume. */
+               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? */
+               struct dev_mc_list *mclist;
+               unsigned short *eaddrs;
+               struct descriptor *mc_setup_frm = sp->mc_setup_frm;
+               u16 *setup_params = (short *)mc_setup_frm->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;
+                       printk("%s: Allocating a setup frame of size %d.\n",
+                                  dev->name, sp->mc_setup_frm_len);
+                       sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len,
+                                                                          intr_count ? GFP_ATOMIC : GFP_KERNEL);
+                       if (sp->mc_setup_frm == NULL) {
+                         printk("%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. */
+               printk("%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 = (short *)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 = (unsigned short *)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. */
+               outw(CU_RESUME, ioaddr + SCBCmd);
+               sp->last_cmd = mc_setup_frm;
+               restore_flags(flags);
+               printk("%s: Last command at %p is %4.4x.\n",
+                          dev->name, sp->last_cmd, sp->last_cmd->command);
+       }
+
+       sp->rx_mode = new_rx_mode;
+}
+\f
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,38))     /* 1.3.38 and later */
+char kernel_version[] = UTS_RELEASE;
+#endif
+
+int
+init_module(void)
+{
+       int cards_found;
+
+       if (debug >= 0)
+               speedo_debug = debug;
+       if (speedo_debug)
+               printk(KERN_INFO "%s", version);
+
+       root_speedo_dev = NULL;
+       cards_found = eepro100_init(NULL);
+       return cards_found < 0 ? cards_found : 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;
+}
+#endif  /* MODULE */
+\f
+/*
+ * Local variables:
+ *  compile-command: "gcc -DCONFIG_MODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
index dc657089e3cc9f5ec234e6109b8be36aa38765b2..4384bac78a570d84dddf0e9a89af3b10b88525b2 100644 (file)
@@ -62,9 +62,11 @@ static const char *version =
 /* Do we have a non std. amount of memory? (in units of 256 byte pages) */
 /* #define PACKETBUF_MEMSIZE   0x40 */
 
+#if defined(HAVE_DEVLIST) || !defined(MODULE)
 /* A zero-terminated list of I/O addresses to be probed. */
 static unsigned int netcard_portlist[] =
 { 0x300, 0x280, 0x320, 0x340, 0x360, 0};
+#endif /* defined(HAVE_DEVLIST) || !defined(MODULE) */
 
 #ifdef CONFIG_PCI
 /* Ack! People are making PCI ne2000 clones! Oh the horror, the horror... */
@@ -160,7 +162,9 @@ struct netdev_entry netcard_drv =
 
 int ne_probe(struct device *dev)
 {
+#ifndef MODULE
     int i;
+#endif /* MODULE */
     int base_addr = dev ? dev->base_addr : 0;
 
     /* First check any supplied i/o locations. User knows best. <cough> */
index e31fc71c8b58cd7de2c04aea56d141704bbf2f35..418d59e1663d8c8932e1e71b85139745dd43c826 100644 (file)
@@ -47,7 +47,6 @@ static const char *version = "smc-ultra32.c: 06/97 v1.00\n";
 
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
index d13f6f5c8207e3302e58b2c4db64b49fa3904259..94a2319757f321ec7cc8f81c031962a2f08b3952 100644 (file)
@@ -1,3 +1,421 @@
+Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.4
+       Several clean-ups:
+         - Asynchronous pre-scaler calculation.
+           Synchronous divisor calculation.
+         - Use FE_ as feature identifier prefix instead of _F_.
+         - Change 'ns_sync' identifier to "minsync".
+         - Some others.
+       Apply some SPI2-R12 recommendations.
+         - Use Slow, Fast-10, Fast-20, Fast-40 SCSI instead of SCSI-2, 
+           FAST SCSI-2, ULTRA, ULTRA-2. 
+         - Reset the SCSI on bus mode change.
+
+Wed July 02 22:58 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.3c
+       - Add define SCSI_NCR_PCI_FIX_UP_SUPPORT for conditionnal compilation 
+         of the corresponding pci fix-up code when a small driver is needed.
+       - Use "ncr53c8xx" as driver name for both request_irq() and 
+          request_region(). Using different names confused 'lsdev'.
+          (Suggestion sent by Henrik Storner).
+
+Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.3b
+       - Print an error message on unexpected boot command line option.
+       - Switch to asynchronous data transfer mode after SCSI wide 
+         negotiation.
+
+Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.3b
+       - Print an error message on unexpected boot command line option.
+       - Switch to asynchronous data transfer mode after SCSI wide 
+         negotiation.
+
+Wed June 14 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.3a
+       - Add PCI LATENCY TIMER fixup code.
+         Increase it if necessary according to burst size.
+         Boot option bit : 'pcifix:4'
+       - On phase mismatch, calculate residual data size for all OUTPUT 
+         phases. That's only required for interrupted DATA OUT phase, but 
+         this information is usefull for problem solving.
+       - Add KERN_INFO to some messages printed to the log.
+         (Patch sent by Wolfram Kleff).
+
+Tue June 02 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.3
+       - NvRAM support code slightly improved (I think):
+         Use IO or MMIO according to driver setup for reading the NvRAM.
+         Use structures for NvRAM data instead of raw data.
+       - Prevent from queuing more than 1 command to the scsi SCRIPT with 
+         negotiation attached when tagged command queueing is enabled.
+       - Fix-up for old 53C8XX chips that support PCI READ LINE but not 
+         CACHE LINE SIZE. If the cache line size is unknown, set burst 
+         to 8 dwords and disable READ LINE, otherwise set burst max to 
+         the cache line size value.
+
+Sat May 24 12:30 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.2c (for linux-2.1.40)
+       - Remove reference to 'x86' symbol when MODULE is defined, since this 
+         symbol is not exported for module loading.
+         The value of 'x86' is used for fixing up the PCI CACHE LINE SIZE 
+         configuration register.
+       - Bytes/words read one bit at a time from the serial NVRAM were'nt 
+          initialized with zero.
+       - Some comments added. Minor cosmetic changes.
+
+Mon May 19 20:30 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.2b
+       - Patch for NVRAM support by Richard Waltham applied.
+         The code detects Symbios NVRAM format and Tekram NVRAM format. 
+         This enhancement allows to get hosts and devices user set up 
+         from the NVRAM.
+       - Use the NVRAM contents when present to initialize user definable 
+         target parameters.
+       - Update the README file. 
+
+Sun May 11 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.1b
+       - Cosmetic changes.
+       - Some heavy testings under pre-linux-2.1.37-6
+
+Sun May 4 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.1a
+       - PFEN wrongly used for PREFETCH feature bit testing.
+         Changed to _F_PFEN.
+       - 2 SCR_COPY that need NO FLUSH bit to be removed had been missed 
+         in tp->getscr[] script (loads SXFER and SCNTL3 on reselection).
+
+Sat May 3 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.1
+       - Use the NO FLUSH option for MOVE MEMORY (COPY) each time it is 
+         possible. More than 100 COPY with NO FLUSH and 6 with FLUSH for  
+         my configuration (max queued command / device = 8).
+         This option bit is removed from the script instance for chips 
+         that donnot support prefetching.
+       - Rewrite the ncr_exception() routine more simple (I think) and 
+         remove useless code.
+       - Change the data_in and data_out script management.
+         Use the bottom part of these scripts instead of the beginning.
+         That avoids to zero the scatter/gather array when a command is 
+         queued (1k) and to deal with some weird IID on MOVE 0 bytes when 
+         a target wants to transfer more bytes than expected.
+       - Misc. improvements in the init code.
+       - Remove IOMAPPED/MMIO automatic switching option.
+         Was useless and reported not reliable.
+       - Fix a double read of DSTAT and remove DFE testing in the 
+         Phase mismatch service routine.
+       - Etc...
+
+Fri Apr 26 20:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.0a
+       - Add support if the Diamond FirePort 40 (SYM53C875J chip)
+
+Mon Apr 22 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 2.0
+       - incorporate __initdata and __initfunc directives in order to 
+         allow 'init' to free unused memory after driver initialisations.
+         Patch sent by Roberto Fichera.
+       - rewrite the init code of the driver. Now a feature descriptor 
+         is used for each real chip types. The code is a lot more clean,
+         since the driver uses device and revision ids only in the 
+         detection procedure.
+       - add 'pcifix' boot command line. This command allows to fix up PCI 
+         config space for new chips which support features based on the 
+         cache line size and 'write and invalidate'.
+       - incorporate in the driver, the code used for error recovery  
+         testing. This code is normally not compiled; have to define 
+         SCSI_NCR_DEBUG_ERROR_RECOVERY in order to compile it.
+       - take into account actual SCSI bus mode for 53C895 LVD/SE controller.
+         In single ended mode only fast20 is supported.
+          (Just to not be late since such controllers are not yet available)
+          
+
+Sat Apr 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 1.18f
+       - fix an old bug included in the initial port (version 0.0).
+         The driver allocated 10 bytes of static data and uses 12 bytes.
+         No danger, since data are generally aligned on 4 bytes boundary 
+         and so byte 10 and 11 are free (I hope ...)
+
+Wed Apr 16 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 1.18e
+       - reset all when an unexpected data cycle is detected while 
+         disconnecting.
+       - make changes to abort() ans reset() functions according to 
+         Leonard's documentation.
+       - small fix in some message for hard errors.
+
+Sat Apr 5  13:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 1.18d
+       - Probe NCR pci device ids in reverse order if asked by user from 
+         the boot command line. Suggested by Richard Waltham.
+       - Make a separate function that prints out verbose information on 
+         severe error (assumed from hardware).
+       - Add the transfer period factor and the max commands per lun value 
+         to the proc info data. If debug flags are set or verbosity is 
+         greater than 1, debug flags and verbosity are returned in proc  
+         info data.
+       - Update the documentation.
+
+Thu Mar 20 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 1.18c
+       - Add special features support for NCR53C885 and NCR53C896 chip.
+         Quite obvious, but untested, and based on the fact that:
+         The 885 supports same features as the 875.
+         The 896 is a 64 bits PCI version of the 895.
+       - Improve recovery from SCSI GROSS ERRORS.
+         I can get such errors by making the driver negotiate offset 8 with 
+         a disk and setting the ncr chip to a lower offset value.
+         I got bunches of errors that have been gracefully recovered by 
+         the driver.
+         The driver now uses its timer handler in order to wait 2 sec. for 
+         devices to settle after SCSI reset and so does not uselessly freeze 
+         the system with interrupt masked for seconds.
+       - Enable 'burst op code fetch' and 'read line' for 815 chips.
+       - Use a 2 commands queue depth instead of 1 for devices that does 
+          not support tagged command queuing.
+       - The ULTRA timing flag setting was based on the output resulting 
+         period factor of the ncr and not on the negotiated one.
+         This flag setting was wrong only for 24 ns negotiated period factor.
+       - Some other minor changes and cleanups.
+
+Thu Feb 27 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h revision 1.18b
+       - 'On paper' support of the NCR53C895 Ultra-2 chip.
+         (Clock quadrupler + 7 clock divisors)
+       - Load the main part of the script into the on-board RAM.
+       - 810A rev. 0x11 PCI problem fixed.
+         This chip is now supported with all PCI features enabled and 
+         16 dwords burst transfers. 
+       - Align on 32 boundary some internal structures.
+         That fixes the 810A problem and allows cache line bursting when 
+         moving the global header (64 bytes) from/to CCBs to/from NCB.
+       - Synchronous parameters calculation rewritten. The driver 
+         now uses all available clock divisors and will be able to support 
+          clock frequencies that are not multiple of 40 Mhz if necessary.
+
+Sat Feb 8 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c - revision 1.17a
+       - IRQ mode set up from boot setup command.
+         irqm:0    open drain (default)
+         irqm:1    preserve initial setting (assumed from BIOS)
+         irqm:2    totem pole
+       - DIFF mode set up from boot setup command.
+         Suggested by Richard Waltham.
+         diff:0    never set up diff mode (default)
+         diff:1    set up diff mode according to initial setting (BIOS?)
+         diff:2    always set up diff mode
+         diff:3    set up diff mode if GPIO3 is zero (SYMBIOS boards)
+       - Change CONFIG option for LED support.
+          CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT allows LED support and 
+         DIFF support for SYMBIOS boards and compatibles (clones?).
+       - Set 16 DWORD bursts for 810A rev. >= 0x12 since my SC200 with 
+         such a chip have no problem with it (MB with Triton 2 HX).
+         810A rev. 0x11 are set to 8 DWORD bursts since they may give
+         problems with PCI read multiple and Triton 2 HX.
+         Thanks to Stefan for this information.
+
+Sat Jan 25 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c - revision 1.17
+       - Controller LED support.
+         Only works with LED pin wired to GPIO_FETCHN, so probably with 
+          all boards using SMDS BIOS.
+         This option can be enabled only if CONFIG_EXPERIMENTAL is set.
+       - Assume clock doubler for 875 chip when clock frequency measurement 
+         result is 40 MHz. May help when some old stuff as SDMS BIOS 3.0 
+         or some old driver has broken the normal BIOS settings.
+       - Add wide negotiation control from boot setup command.
+         May be usefull with systems using a 875 based board connected to 
+         a wide device through a 50 pins to 68 pins converter.
+       - Add a "boot fail safe option" to the boot setup command line.
+       - Rewrite the "reset_command" routine.
+         Low-level driver are responsible to keep the involved command 
+         alive. The new code seems to behave correctly.
+       - Change some variables used by the script from u_long to u_int32.
+       - Remove some useless code.
+
+Sun Jan 12 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c - revision 1.16e
+       - Add support of PCI burst length control from boot setup command.
+         burst:0     disable burst
+         burst:255   get burst from initial settings (BIOS settings?)
+         burst:#x    set burst transfers to 1<<#x
+       - Only check xfer direction for common op-codes.
+         For all device specific / vendor specific opcodes the driver 
+         now uses the xfer direction decided by the target.
+
+Sun Jan 05 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c - revision 1.16d
+       - The driver is now able to process scsi commands without 
+         knowledge of xfer data direction.
+         Stefan agreed with this change for Linux. This change is 
+         not needed under FreeBSD since low-level drivers receive 
+         the expected data direction for each scsi request.
+       - Save ctest5 features bits at start-up and restore them at 
+         module release step.
+         Avoid side effects when a ncr driver which trusts bios 
+         settings is reloaded (could be the ncr53c8xx itself).
+
+
+Wed Jan 01 23:30 1997 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c - revision 1.16c
+       - Bad decision about 20MHz for 13 ns period factor.
+         Was wrong, so I restore the previous algorithm.
+       - Burst length 128 not correctly set in dmode.
+
+Thu Dec 26 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16b
+       - Remove useless code.
+       - Try to improve error recovery in case of abort and reset.
+       - Remove DEBUG_NEGO by default.
+       - Add boot setup command support.
+         Now, all experimental config options can be removed.
+       - Update README file.
+
+
+Mon Dec 23 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h - revision 1.16a
+       New display for speed ##.# MB/s (From Stefan)
+       - I add "WIDE" qualifier after ULTRA and FAST
+       - I get "FAST WIDE SCSI-2 20 MB/s" with my Atlas. That's nice.
+
+       Richard Waltham reports SYMBIOS set the 875 to 20 MB/s for 13 ns 
+       period factor. I decide to trust SYMBIOS. 20 MB/s output speed 
+       instead of 19.2 MB/s should not cause problem. The ncr is only able 
+       to use 16.67 MB/s when 20 MB/s is not possible.
+
+       Fix from Markus Kossman: "Ultra SCSI enabled" wrongly printed 
+       when not enabled.
+
+       Set DEBUG_NEGO by default in order to get reports about sync nego. 
+       Will remove it in the next patch.
+
+Thu Dec 19 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16
+       Incorporate new definitions in ncr53c8xx.h (From Stefan).
+       Check changes against Stefan's current version of the driver.
+       All seems ok.
+
+Sat Nov 30 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h
+       Make changes in order to support: 
+       - Clock doubler and so 80 Mhz scsi clock for 875 chips.
+       - Sync transfers below 7.5 MB/sec.
+          Use Clock/2 between 5 and 10 Mega-transfers/s and Clock/4 below 5.
+       - Ultra SCSI data transfers.
+       - Offset 16.
+
+       Works with my configuration. However I cannot test Ultra transfers,
+       since my disks are only fast scsi-2.
+
+Tue Nov 28 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c
+       I received yesterday my Promise SCSI Ultra board.
+       NCR53C875 rev. 3 with clock doubler.
+       Add the code to support some bus features, the large 536 dma fifo and 
+       burst 128. Works.
+
+Mon Nov 4 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h - revision 1.14c
+       Severall control command improvements:
+
+       - Allow to specify "all" to commands that apply to #target.
+         For example: "setsync all 255" sets asynchronous data 
+                      transfers for all targets on a bus.
+
+       - Allow to control disconnection privilege per device, as follow:
+         "setflag #target no_sync" disables disconnection for #target.
+         "setflag #target" with no flag specified reenables it.
+
+         Obviously #target may be specified as "all" in order to control 
+         disconnection for all targets with a single control command.
+
+       - README file updated and some hints about SCSI problems solving added.
+
+Sun Oct 27 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h - revision 1.14b
+       Add the following config parameters:
+
+       - CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+         Max number of queued tagged commands.
+         Allow from 2 to 12, default 4.
+
+       - CONFIG_SCSI_NCR53C8XX_SYNC
+         Synchronous transfers frequency in MHz.
+         Allow from 5 to 10, default 5, 0 means asynchronous.
+         (And so remove CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS)
+
+Sun Oct 20 16:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c
+       ncr_scatter() rewritten.
+       remove "ncr dead" detection.
+Sun Oct 13 19:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h - revision 1.14a
+       Enabling some special features makes problems with some hardware.
+       So, disable them by default.
+       Add SCSI_NCR_SPECIAL_FEATURES define to play with.
+Sun Oct 13 14:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c ncr53c8xx.h
+       Incorporate Stefan's patch for clock frequency detection.
+       (Committed in FreeBSD/ncr.c rev. 1.81).
+       The driver then does about the following:
+           Assume 40 MHz clock for all ncr chips except:
+           - NCR53C860 chips:
+               Assume 80 Mhz clock.
+           - NCR53C875 chips:
+               If clock doubler enabled, disable it and assume 40 Mhz clock.
+               Else if (scntl3&7)=0 measure scsi clock frequency.
+               Else trust bios setting of scntl3&7 (3=40 Mhz, 5=80Mhz).
+
+Wed Oct 9 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c - release 1.14
+       For now, just change the clock detection as follow:
+       - If clock doubler selected by BIOS, assume 40 MHz clock since 
+         clock doubler will be disabled by chip reset.
+       - Else if NCR53C860 assume 80 MHz clock.
+       - Else trust BIOS setting if (scntl3&7 >= 3)
+       - Else assume 40 MHz clock.
+
+Sat Oct 05  17:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c
+       Stefan sent me a patch that improves the clock frequency detection 
+       of the driver. Stefan uses the general timer register stime1 in 
+       order to measure as accurately as possible the scsi clock.
+       Works ok with my 825, but needs still testing. So will be 
+       released later.
+
+Sun Sep 29  17:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c
+       Preserve dcntl/dmode/ctest3/ctest4 features bits at start-up.
+       Add the define option SCSI_NCR_TRUST_BIOS_SETTING.
+       - If this option is defined, the driver will preserve the 
+         corresponding bits of io registers.
+       - Else, the driver will set features bits according to chip 
+         and revision ids.
+
+Sun Sep 22  17:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.c
+       Remove useless fields and code and so spare cpu:
+       - profile data are accumulated in jiffies ticks and converted 
+         to milli-seconds when read through proc fs.
+       - when IOMAPPED is not defined, try only MMIO.
+         (avoid testing a value in order to choose between IO and MMIO)
+
+Sun Sep 01  20:00 1996 Gerard Roudier (groudier@club-internet.fr)
+       * ncr53c8xx.h, ncr53c8xx.c - Version 1.13
+       Adaptation of the tagged command queuing depth control of the 
+       FreeBSD driver to Linux. Now, tagged command queueing can be 
+       disabled at run time by a "settags N 0" control command.
+       Add the following heuristic in order to manage intelligently (perhaps) 
+       QUEUE_FULL status:
+       - Each time a QUEUE FULL status is returned by a device, disable tagged 
+         command queuing for that device.
+       - Every 100 successfully complete commands, increment the maximum 
+         queuable commands (up to the allowed limit).
+
 Fri Aug 30  10:00 1996 Gerard Roudier (groudier@club-internet.fr)
        * ncr53c8xx.c - Version 1.12c
        Incorporate the changes of FreeBSD/ncr.c revision 1.76.
index 1c6328fc99d3d7c328b7a35dcb0837de47ee36f2..7f65b6314e515bee9709bb23eae2764337457eb5 100644 (file)
@@ -21,10 +21,13 @@ dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
     bool '   Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
-    int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+    dep_tristate '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
+    if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
+      int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+    fi
     bool '   Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N
     bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
-    int  '   delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
+    int  '   Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
 fi
 dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
@@ -62,15 +65,17 @@ fi
 if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
   dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
   if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then
+    bool '  detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
     bool '  enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
-    bool '  force normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED
-    bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
-    bool '  force asynchronous transfer mode' CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS
-    bool '  force synchronous negotiation' CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-  fi
-  if [ "$CONFIG_SCSI_NCR53C8XX" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    bool '  disable master parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-    bool '  disable scsi parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
+    bool '  use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED
+    int  '  maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 4
+    int  '  synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 5
+    if [ "$CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE" != "y" ]; then
+      bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
+    fi
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      bool '  assume boards are SYMBIOS compatible' CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+    fi
   fi
 fi
 dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI
@@ -80,6 +85,9 @@ if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI
 fi
 dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI
+if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_AM53C974" != "y" ]; then
+  dep_tristate 'Tekram DC-390(T) SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI
+fi
 dep_tristate 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 $CONFIG_SCSI
 dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
   if [ "$CONFIG_SCSI_U14_34F" != "n" ]; then
index e2cfe6793fee1b1e0493e661c4cccfda3c9e73aa..0b1559d3c1e2bdf18fe9342018659cd2a26edb0c 100644 (file)
@@ -178,6 +178,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_DC390T),y)
+L_OBJS += tmscsim.o
+else
+  ifeq ($(CONFIG_SCSI_DC390T),m)
+  M_OBJS += tmscsim.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_AM53C974),y)
 L_OBJS += AM53C974.o
 else
@@ -380,6 +388,9 @@ aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
 seagate.o: seagate.c
        $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c 
 
+tmscsim.o : tmscsim.c
+       $(CC) $(CFLAGS) -c tmscsim.c
+
 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl
        ln -sf 53c7,8xx.scr fake.c
        $(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl 
index 34ba6360680ca13b84606a073d52a4a30e0f0542..1acf9887f377644d8be4b7f07810c9923a8e7eb3 100644 (file)
@@ -1,10 +1,10 @@
-The linux NCR53C8XX driver README file
+The Linux NCR53C8XX driver README file
 
 Written by Gerard Roudier <groudier@club-internet.fr>
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-12 June 1995
+19 June 1997
 ===============================================================================
 
 1.  Introduction
@@ -21,15 +21,31 @@ Written by Gerard Roudier <groudier@club-internet.fr>
       8.4  Set order type for tagged command
       8.5  Set debug mode
       8.6  Clear profile counters
+      8.7  Set flag (no_sync)
+      8.8  Debug error recovery
 9.  Configuration parameters
-10. Some constants and flags of the ncr53c8xx.h header files
-11. Provided files
-12. Installation procedure for Linux version 1
-13. Installation procedure for Linux version 2
-14. Control commands under linux-1.2.13
-15. Known problems
-      15.1 Tagged commands with Iomega Jaz device
-      15.2 Tagged command queueing cannot be disabled at run time
+10. Boot setup commands
+      10.1 Syntax
+      10.2 Available arguments
+      10.3 Advised boot setup commands
+      10.4 PCI configuration fix-up boot option
+      10.5 Serial NVRAM support boot option
+11. Some constants and flags of the ncr53c8xx.h header file
+12. Installation
+      12.1 Provided files
+      12.2 Installation procedure
+13. Control commands under linux-1.2.13
+14. Known problems
+      14.1 Tagged commands with Iomega Jaz device
+      14.2 Device names change when another controller is added
+15. SCSI problem troubleshooting
+16. Synchonous transfer negotiation tables
+      16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+      16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+17. Serial NVRAM support (by Richard Waltham)
+      17.1 Features
+      17.2 Symbios NVRAM layout
+      17.3 Tekram  NVRAM layout
 
 ===============================================================================
 
@@ -43,21 +59,37 @@ The original driver has been written for 386bsd and FreeBSD by:
           Wolfgang Stanglmeier        <wolf@cologne.de>
           Stefan Esser                <se@mi.Uni-Koeln.de>
 
-You can find technical information about the NCR 8xx family in the PCI-HOWTO 
-written by Michael Will and in the SCSI-HOWTO written by Drew Eckhardt.
+You can find technical information about the NCR 8xx family in the
+PCI-HOWTO written by Michael Will and in the SCSI-HOWTO written by
+Drew Eckhardt.
 
 Information about new chips is available at SYMBIOS web server:
-          http://www.symbios.com
 
-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.
+          http://www.symbios.com/
 
-This driver has been tested OK with linux/i386  and is currently untested
-under linux/Alpha. If you intend to use this driver under linux/Alpha, just 
-try it first with read-only or mounted read-only devices.
+SCSI standard documentations are available at SYMBIOS ftp server:
 
-I am not a native speaker of English and there are probably lots of 
+          ftp://ftp.symbios.com/
+
+Usefull SCSI tools written by Eric Youngdale are available at tsx-11:
+
+          ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsiinfo-X.Y.tar.gz
+          ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsidev-X.Y.tar.gz
+
+These tools are not ALPHA but quite clean and work quite well.
+It is essential you have the 'scsiinfo' package.
+
+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.
+
+Latest driver version and patches are available at:
+
+          ftp://linux.wauug.org/pub/roudier
+
+I am not a native speaker of English and there are probably lots of
 mistakes in this README file. Any help will be welcome.
 
 
@@ -71,8 +103,8 @@ The following features are supported for all chips:
        SCSI parity checking
        Master parity checking
 
-"Wide negotiation" is supported for chips that allow it.
-The following table shows some characteristics of NCR 8xx family chips:
+"Wide negotiation" is supported for chips that allow it.  The
+following table shows some characteristics of NCR 8xx family chips:
 
        On board                        Supported by    Tested with
 Chip   SDMS BIOS   Wide   Ultra SCSI   the driver      the driver
@@ -81,12 +113,12 @@ Chip   SDMS BIOS   Wide   Ultra SCSI   the driver      the driver
 810A       N         N        N            Y             Y
 815        Y         N        N            Y             Y
 825        Y         Y        N            Y             Y
-825A       Y         Y        N            Y             Not yet
-875        Y         Y        Y(1)         Y             Not yet
-
-(1) Ultra SCSI extensions will be supported in a future release of the 
-    driver.
+825A       Y         Y        N            Y             Y
+860        N         N        Y            Y             Y
+875        Y         Y        Y            Y             Y
+895        Y         Y        Y(1)         Y             not yet 
 
+(1) The 895 chip is supported 'on paper'.
 
 3. Summary of other supported features.
 
@@ -97,45 +129,58 @@ Chip   SDMS BIOS   Wide   Ultra SCSI   the driver      the driver
         Debugging information: written to syslog (expert only)
        Scatter / gather
        Shared interrupt
+        Boot setup commands
+        Serial NVRAM:          Symbios and Tekram formats
 
 
 4. Memory mapped I/O versus normal I/O
 
-Memory mapped I/O has less latency than normal I/O.
-Since linux-1.3.x, memory mapped I/O is used rather than normal I/O.
-Memory mapped I/O seems to work fine on most hardware configurations, but some 
-poorly designed motherboards may break this feature.
+Memory mapped I/O has less latency than normal I/O.  Since
+linux-1.3.x, memory mapped I/O is used rather than normal I/O.  Memory
+mapped I/O seems to work fine on most hardware configurations, but
+some poorly designed motherboards may break this feature.
 
-During the initialization phase, the driver first tries to use memory mapped
-I/O.  If nothing seems wrong, it will use memory mapped I/O.
-If a flaw is detected, it will use normal I/O.
-
-However, it's possible that memory mapped I/O does not work properly and the 
-driver has not detected the problem.
-
-The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the 
+The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the
 driver to use normal I/O in all cases.
 
 
 5. Tagged command queueing
 
-Some SCSI devices do not properly support tagged command queuing.
-A safe configuration is to not enable tagged command queuing support at 
-boot-up, and to enable support of it with the control command "settags" 
-described further in this text.
+Some SCSI devices do not properly support tagged command queuing.  A
+safe configuration is to not enable tagged command queuing support at
+boot-up, and to enable support of it with the control command
+"settags" described further in this text.
+
+Once you are sure that all your devices properly support tagged
+command queuing, you can enable it by default with the
+CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE configuration option.
+
+The maximum number of simultaneous tagged commands queued to a device
+is currently set to 4 by default. It is defined in the file
+ncr53c8xx.h by SCSI_NCR_MAX_TAGS. This value is suitable for most SCSI
+disks.  With large SCSI disks (> 2GB, cache > 512KB average seek time
+< 10 ms), 8 tagged commands may give better performance.
+
+In some special conditions, some SCSI disk firmwares may return a
+QUEUE FULL status for a SCSI command. This behaviour is managed by the
+driver by the following heuristic:
 
-Once you are sure that all your devices properly support tagged command queuing,
-you can enable it by default with the CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE 
-configuration option.
+- Each time a QUEUE FULL status is returned, tagged command queueing is 
+  temporarily disabled. 
+
+- Every 100 successfully completed SCSI commands, if allowed by the
+  current limit, the maximum number of queueable commands is
+  incremented and tagged command queueing is reenabled.
 
 
 6. Parity checking
 
-The driver supports SCSI parity checking and PCI bus master parity checking.
-These features must be enabled in order to ensure safe data transfers.
-However, some flawed devices or mother boards will have problems with 
-parity. You can disable parity by choosing first "CONFIG_EXPERIMENTAL".
-Then, "make config" will allow to set the following configuration options:
+The driver supports SCSI parity checking and PCI bus master parity
+checking.  These features must be enabled in order to ensure safe data
+transfers.  However, some flawed devices or mother boards will have
+problems with parity. You can disable parity by choosing first
+"CONFIG_EXPERIMENTAL".  Then, "make config" will allow to set the
+following configuration options:
 
    CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK  (disable SCSI parity checking)
    CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK (disable master parity checking)
@@ -145,16 +190,19 @@ Then, "make config" will allow to set the following configuration options:
 
 Profiling information is available through the proc SCSI file system.
 The device associated with a host has the following pathname:
+
           /proc/scsi/ncr53c8xx/N     (N=0,1,2 ....)
 
 Generally, only 1 board is used on hardware configuration, and that device is:
           /proc/scsi/ncr53c8xx/0
 
-However, if the driver has been made as module, the number of the hosts is 
-incremented each time the driver is loaded.
+However, if the driver has been made as module, the number of the
+hosts is incremented each time the driver is loaded.
 
 In order to display profiling information, just enter:
+
          cat /proc/scsi/ncr53c8xx/0
+
 and you will get something like the following text:
 
 -------------------------------------------------------
@@ -162,6 +210,7 @@ General information:
   Chip NCR53C810, device id 0x1, revision id 0x2
   IO port address 0x6000, IRQ number 10
   Using memory mapped IO at virtual address 0x282c000
+  Synchronous transfer period 25, max commands per lun 4
 Profiling information:
   num_trans    = 18014
   num_kbytes   = 671314
@@ -175,7 +224,7 @@ Profiling information:
   ms_post      = 1320
 -------------------------------------------------------
 
-General information is easy to understand. The device ID and the 
+General information is easy to understand. The device ID and the
 revision ID identify the SCSI chip as follows:
 
 Chip    Device id     Revision Id
@@ -187,15 +236,18 @@ Chip    Device id     Revision Id
 860       0x6
 825A      0x3            >= 0x10
 875       0xf
+895       0xc
 
 The profiling information is updated upon completion of SCSI commands.
-A data structure is allocated and zeroed when the host adapter is 
-attached. So, if the driver is a module, the profile counters are cleared each 
-time the driver is loaded.
-The "clearprof" command allows you to clear these counters at any time.
+A data structure is allocated and zeroed when the host adapter is
+attached. So, if the driver is a module, the profile counters are
+cleared each time the driver is loaded.  The "clearprof" command
+allows you to clear these counters at any time.
 
 The following counters are available:
-("num" prefix means "number of", "ms" means milli-seconds)
+
+("num" prefix means "number of",
+"ms" means milli-seconds)
 
 num_trans
        Number of completed commands
@@ -238,46 +290,43 @@ ms_post
        (time from SCSI status get to command completion call)
        Example above: 1.32 seconds spent for post processing
 
-Due to the 1/100 second tick of the system clock, "ms_post" time may be 
-wrong.
-
-In the example above, we got 18038 interrupts "on the fly" and only 1673 script 
-breaks probably due to disconnections inside a segment of the scatter list. 
-This is an excellent result due to the fact that the driver tries to use small 
-data segments (512) for the scatter list. The CPU load of this rescatter process 
-is acceptable. Unlike other SCSI processors, NCR53C8XX controllers do not need  
-large data chunks in order to get better performance, and it seems that it 
-is just the opposite.
-The scatter/gather algorithm of the middle SCSI driver is not optimal for 
-NCR SCSI processors and should be tunable according to host type.
-
-You can tune the "wished" segment size for the scatterlist by changing the 
-following "define" in the file ncr53c8xx.h.
-Use only power of 2 greater than 512 (1024, 2048 or 4096).
+Due to the 1/100 second tick of the system clock, "ms_post" time may
+be wrong.
 
-SCSI_NCR_SEGMENT_SIZE          (default: 512)
+In the example above, we got 18038 interrupts "on the fly" and only
+1673 script breaks generally due to disconnections inside a segment 
+of the scatter list.
 
 
 8. Control commands
 
-Control commands can be sent to the driver with write operations to the 
-proc SCSI file system. The generic command syntax is the following:
+Control commands can be sent to the driver with write operations to
+the proc SCSI file system. The generic command syntax is the
+following:
 
       echo "<verb> <parameters>" >/proc/scsi/ncr53c8xx/0
       (assumes controller number is 0)
 
+Using "all" for "<target>" parameter with the commands below will
+apply to all targets of the SCSI chain (except the controller).
+
 Available commands:
 
-8.1 Set minimum synchronous period
+8.1 Set minimum synchronous period factor
 
-    setsync <target> <period>
+    setsync <target> <period factor>
 
     target:    target number
-    period:    minimum synchronous period in nano-seconds.
-               Maximum speed = 1000/(4*period) MB/second
+    period:    minimum synchronous period.
+               Maximum speed = 1000/(4*period factor) except for special
+               cases below.
 
     Specify a period of 255, to force asynchronous transfer mode.
 
+      10 means 25 nano-seconds synchronous period
+      11 means 30 nano-seconds synchronous period
+      12 means 50 nano-seconds synchronous period
+
 8.2 Set wide size
 
     setwide <target> <size>
@@ -292,7 +341,6 @@ Available commands:
     target:    target number
     tags:      number of concurrent tagged commands
                must not be greater than SCSI_NCR_MAX_TAGS (default: 4)
-               must not be lower that 1 (see: known problems)
 
 8.4 Set order type for tagged command
 
@@ -321,23 +369,63 @@ Available commands:
        nego:    print information about SCSI negotiations
        phase:   print information on script interruptions
 
+    Use "setdebug" with no argument to reset debug flags.
+
 
 8.6 Clear profile counters
 
     clearprof
 
-    The profile counters are automatically cleared when the amount of data 
-    transfered reaches 1000 GB in order to avoid overflow.
+    The profile counters are automatically cleared when the amount of
+    data transfered reaches 1000 GB in order to avoid overflow.
     The "clearprof" command allows you to clear these counters at any time.
 
 
+8.7 Set flag (no_sync)
+    setflag <target> <flag>
+
+    target:    target number
+
+    For the moment, only one flag is available:
+
+        no_sync:   not allow target to disconnect.
+
+    Do not specify any flag in order to reset the flag. For example:
+    - setflag 4
+      will reset no_sync flag for target 4, so will allow it disconnections.
+    - setflag all
+      will allow disconnection for all devices on the SCSI bus.
+
+
+8.8 Debug error recovery
+
+    debug_error_recovery <error to trigger>
+
+    Available error type to trigger:
+        sge:     SCSI gross error
+        abort:   abort command from the middle-level driver
+        reset:   reset command from the middle-level driver
+        parity:  scsi parity detected in DATA IN phase
+        none:    restore driver normal behaviour
+
+    The code corresponding to this feature is normally not compiled.
+    Its purpose is driver testing only. In order to compile the code 
+    that allows to trigger error recovery you must define at compile time 
+    SCSI_NCR_DEBUG_ERROR_RECOVERY.
+    If you have compiled the driver with this option, nothing will happen 
+    as long as you donnot use the control command 'debug_error_recovery' 
+    with sge, abort, reset or parity as argument.
+    If you select an error type, it will be triggered by the driver every 
+    30 seconds.
+
 9. Configuration parameters
 
-If the firmware of all your devices is perfect enough, all the features 
-supported by the driver can be enabled at start-up.
-However, if only one has a flaw for some SCSI feature, you can disable the 
-support by the driver of this feature at linux start-up and enable this 
-feature after boot-up only for devices that support it safely.
+If the firmware of all your devices is perfect enough, all the
+features supported by the driver can be enabled at start-up.  However,
+if only one has a flaw for some SCSI feature, you can disable the
+support by the driver of this feature at linux start-up and enable
+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.
@@ -347,9 +435,16 @@ CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE    (default answer: n)
     Answer "y" if you are sure that all your SCSI devices that are able to 
     accept tagged commands will proceed safely.
 
-CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS (default answer: n)
-    This option forces asynchronous transfer mode for all SCSI devices. 
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS         (default answer: 4)
+    This option allows you to specify the maximum number of tagged commands 
+    that can be queued to a device.
+
+CONFIG_SCSI_NCR53C8XX_SYNC            (default answer: 5)
+    This option allows you to specify the frequency in MHz the driver 
+    will use at boot time for synchronous data transfer negotiations.
+    This frequency can be changed later with the "setsync" control command.
+    0 means "asynchronous data transfers".
+
 CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO (default answer: n)
     Force synchronous negotiation for all SCSI-2 devices.
     Some SCSI-2 devices do not report this feature in byte 7 of inquiry 
@@ -360,12 +455,331 @@ CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT   (default and only reasonnable answer: n)
     you can answer "y". Then, all SCSI devices will never disconnect the bus 
     even while performing long SCSI operations.
 
+CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+    Genuine SYMBIOS boards use GPIO0 in output for controller LED and GPIO3 
+    bit as a flag indicating singled-ended/differential interface.
+    If all the boards of your system are genuine SYMBIOS boards or use
+    BIOS and drivers from SYMBIOS, you would want to enable this option.
+    This option must NOT be enabled if your system has at least one 53C8XX 
+    based scsi board with a vendor-specific BIOS.
+    For example, Tekram DC-390/U, DC-390/W and DC-390/F scsi controllers 
+    use a vendor-specific BIOS and are known to not use SYMBIOS compatible 
+    GPIO wiring. So, this option must not be enabled if your system has 
+    such a board installed.
+
+CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+    Enable support for reading the serial NVRAM data on Symbios and
+    some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for
+    systems with more than one Symbios compatible controller where at least
+    one has a serial NVRAM, or for a system with a mixture of Symbios and
+    Tekram cards. Enables setting the boot order of host adaptors
+    to something other than the default order or "reverse probe" order. 
+    Also enables Symbios and Tekram cards to be distinguished so
+    CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a
+    mixture of Symbios and Tekram cards so the Symbios cards can make use of
+    the full range of Symbios features, differential, led pin, without
+    causing problems for the Tekram card(s).
+
+10. Boot setup commands
+
+10.1 Syntax
+
+Setup commands can be passed to the driver at boot time.
+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- 
+separated strings. Example of boot setup command under lilo prompt:
+
+lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200
+
+- enable tagged commands, up to 4 tagged commands queued.
+- 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. 
+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 
+characters and digits are allowed.
+
+10.2 Available arguments
+
+Master parity checking
+      mpar:y     enabled
+      mpar:n     disabled
+
+Scsi parity checking
+      spar:y     enabled
+      spar:n     disabled
+
+Scsi disconnections
+      disc:y     enabled
+      disc:n     disabled
+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
+
+Ultra SCSI support
+   Only apply to 860 and 875 controllers.
+   Have no effect with other ones.
+      ultra:y    enabled
+      ultra:n    disabled
+
+Number of tagged commands
+      tags:0     (or tags:1 ) tagged command queuing disabled
+      tags:#tags (#tags  > 1) tagged command queuing enabled
+  #tags will be truncated to the max queued commands configuration parameter.
+  If the driver is configured with a maximum of 4 queued commands, tags:4 is 
+  the right argument to specify.
+
+Default synchronous period factor
+      sync:255     disabled (asynchronous transfer mode)
+      sync:#factor
+  #factor = 10     Ultra-2 SCSI 40 Mega-transfers / second
+  #factor = 11     Ultra-2 SCSI 33 Mega-transfers / second
+  #factor < 25     Ultra   SCSI 20 Mega-transfers / second
+  #factor < 50     Fast    SCSI-2
+
+  In all cases, the driver will use the minimum transfer period supported by 
+  controllers according to NCR53C8XX chip type.
+
+Negotiate synchronous with all devices
+  (force sync nego)
+      fsn:y      enabled
+      fsn:n      disabled
+
+Verbosity level
+      verb:0     minimal
+      verb:1     normal
+      verb:2     too much
+
+Debug mode
+      debug:0   clear debug flags
+      debug:#x   set debug flags
+  #x is an integer value combining the following power-of-2 values:
+  DEBUG_ALLOC       0x1
+  DEBUG_PHASE       0x2
+  DEBUG_POLL        0x4
+  DEBUG_QUEUE       0x8
+  DEBUG_RESULT     0x10
+  DEBUG_SCATTER    0x20
+  DEBUG_SCRIPT     0x40
+  DEBUG_TINY       0x80
+  DEBUG_TIMING    0x100
+  DEBUG_NEGO      0x200
+  DEBUG_TAGS      0x400
+  DEBUG_FREEZE    0x800
+  DEBUG_RESTART  0x1000
+
+  You can play safely with DEBUG_NEGO. However, some of these flags may 
+  generate bunches of syslog messages. 
+
+Burst max
+      burst:0    burst disabled
+      burst:255  get burst length from initial IO register settings.
+      burst:#x   burst enabled (1<<#x burst transfers max)
+  #x is an integer value which is log base 2 of the burst transfers max.
+  The NCR53C875 and NCR53C825A support up to 128 burst transfers (#x = 7).
+  Other chips only support up to 16 (#x = 4).
+  This is a maximum value. The driver set the burst length according to chip 
+  and revision ids. By default the driver uses the maximum value supported 
+  by the chip.
+
+LED support
+     led:1      enable  LED support
+     led:0      disable LED support
+  Donnot enable LED support if your scsi board does not use SDMS BIOS.
+  (See 'Configuration parameters')
+
+Max wide
+    wide:1      wide scsi enabled
+    wide:0      wide scsi disabled
+  Some scsi boards use a 875 (ultra wide) and only supply narrow connectors.
+  If you have connected a wide device with a 50 pins to 68 pins cable 
+  converter, any accepted wide negotiation will break further data transfers.
+  In such a case, using "wide:0" in the bootup command will be helpfull. 
+
+Differential mode
+    diff:0     never set up diff mode
+    diff:1     set up diff mode if BIOS set it
+    diff:2     always set up diff mode
+    diff:3     set diff mode if GPIO3 is not set
+
+IRQ mode
+    irqm:0     always open drain
+    irqm:1     same as initial settings (assumed BIOS settings)
+    irqm:2     always totem pole
+
+Reverse probe
+    revprob:n   probe chip ids from the PCI configuration in this order:
+                810, 815, 820, 860, 875, 885, 895, 896
+    revprob:y   probe chip ids in the reverse order.
+
+Fix up PCI configuration space
+    pcifix:<option bits>
+
+    Available option bits:
+        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.
+
+    Use 'pcifix:7' in order to allow the driver to fix up all PCI features.
+
+Serial NVRAM
+    nvram:n     do not look for serial NVRAM
+    nvram:y     test controllers for onboard serial NVRAM
+
+Boot fail safe
+    safe:y     load the following assumed fail safe initial setup
+
+  master parity                        disabled                mpar:n
+  scsi parity                  enabled                 spar:y
+  disconnections               not allowed             disc:n
+  special features             disabled                specf:n
+  ultra scsi                   disabled                ultra:n
+  force sync negotiation       disabled                fsn:n
+  reverse probe                        disabled                revprob:n
+  PCI fix up                    disabled                pcifix:0
+  serial NVRAM                  enabled                 nvram:y
+  verbosity level              2                       verb:2
+  tagged command queuing       disabled                tags:0
+  synchronous negotiation      disabled                sync:255
+  debug flags                  none                    debug:0
+  burst length                 from BIOS settings      burst:255
+  LED support                  disabled                led:0
+  wide support                 disabled                wide:0
+  settle time                  10 seconds              settle:10
+  differential support         from BIOS settings      diff:1
+  irq mode                     from BIOS settings      irqm: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\
+             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,
+boot setup can be:
+
+    ncr53c8xx=safe:y,mpar:y,disc:y
+    ncr53c8xx=safe:y,disc:y
+    ncr53c8xx=safe:y,mpar:y
+    ncr53c8xx=safe:y
+
+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\
+             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 
+"ncr53c8xx=verb:2" to get the "static" setup of the driver, or add "verb:2" 
+to your boot setup command in order to check the actual setup the driver is 
+using.
+
+10.4 PCI configuration fix-up boot option
+
+pcifix:<option bits>
+
+Available option bits:
+    0x1:     Set PCI cache-line size register if not set.
+    0x2:     Set write and invalidate bit in PCI command register.
+
+Use 'pcifix:3' in order to allow the driver to fix both PCI features.
+
+These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875 
+and are only supported for Pentium and 486 class processors.
+Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple 
+and PCI write and invalidate commands. These features require the 
+cache line size register to be properly set in the PCI configuration 
+space of the chips. On the other hand, chips will use PCI write and 
+invalidate commands only if the corresponding bit is set to 1 in the 
+PCI command register.
+
+Not all PCI bioses set the PCI cache line register and the PCI write and 
+invalidate bit in the PCI configuration space of 53C8XX chips.
+Optimized PCI accesses may be broken for some PCI/memory controllers or 
+make problems with some PCI boards.
+
+This fix-up works flawlessly on my system.
+(MB Triton HX / 53C875 / 53C810A)
+I use these options at my own risks as you will do if you decide to 
+use them too.
+
+
+10.5 Serial NVRAM support boot option
+
+nvram:n     do not look for serial NVRAM
+nvram:y     test controllers for onboard serial NVRAM
+
+This option is described below (see 17. Serial NVRAM support).
+When this option is enabled, the driver tries to detect all boards using 
+a Serial NVRAM. This memory is used to hold user set up parameters.
+
+The parameters the driver is able to get from the NVRAM depend on the 
+data format used, as follow:
+
+                                 Tekram format      Symbios format
+General and host parameters
+    Boot order                         N                   Y
+    Host SCSI ID                       Y                   Y
+    SCSI parity checking               Y                   Y
+    Verbose boot messages              N                   Y
+SCSI devices parameters
+    Synchronous transfer speed         Y                   Y
+    Wide 16 / Narrow                   Y                   Y
+    Tagged Command Queuing enabled     Y                   Y
+    Disconnections enabled             Y                   Y
+    Scan at boot time                  N                   Y
+
+In order to speed up the system boot, for each device configured without 
+the "scan at boot time" option, the driver forces an error on the 
+first TEST UNIT READY command received for this device.
+
+Some SDMS BIOS revisions seem to be unable to boot cleanly with very fast 
+hard disks. In such a situation you cannot configure the NVRAM with 
+optimized parameters value.
+
+The 'nvram' boot option can be entered in hexadecimal form in order 
+to ignore some options configured in the NVRAM, as follow:
+
+mvram=<bits options>
+      0x01   look for NVRAM  (equivalent to nvram=y)
+      0x02   ignore NVRAM "Synchronous negotiation" parameters for all devices
+      0x04   ignore NVRAM "Wide negotiation"  parameter for all devices
+      0x08   ignore NVRAM "Scan at boot time" parameter for all devices
+
+My Atlas Wide only boots cleanly in 8 bits asynchronous data transfer 
+mode. However, it works flawlessly at 20 MB/second with the driver.
+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. Some constants and flags of the ncr53c8xx.h header files
 
-Some of these are defined from the configuration parameters.
-To change other "defines", you must edit the header file.
-Do that only if you know what you are doing.
+11. Some constants and flags of the ncr53c8xx.h header file
+
+Some of these are defined from the configuration parameters.  To
+change other "defines", you must edit the header file.  Do that only
+if you know what you are doing.
+
+SCSI_NCR_SETUP_ULTRA_SUPPORT   (default: defined)
+       Ultra SCSI support.
+       Can be changed by the following boot setup command:
+               ncr53c8xx=ultra:n
+
+SCSI_NCR_SETUP_SPECIAL_FEATURES        (default: defined)
+       If defined, the driver will enable some special features according  
+       to chip and revision id.
+        For 810A, 860, 825A and 875 scsi chips, this option enables support 
+        of features that reduce load of PCI bus and memory accesses during  
+        scsi transfer processing: burst op-code fetch, read multiple, 
+        read line, prefetch, cache line line, write and invalidate, 
+        burst 128 (875 only), large dma fifo (875 only), offset 16 (875 only).
+       Can be changed by the following boot setup command:
+               ncr53c8xx=specf:n
 
 SCSI_NCR_IOMAPPED              (default: not defined)
        If defined, normal I/O is forced.
@@ -377,26 +791,31 @@ SCSI_NCR_MAX_TAGS         (default: 4)
        Maximum number of simultaneous tagged commands to a device.
        Can be changed by "settags <target> <maxtags>"
 
+SCSI_NCR_SETUP_DEFAULT_SYNC     (default: 50)
+       Transfer period factor the driver will use at boot time for synchronous 
+       negotiation. 0 means asynchronous.
+       Can be changed by "setsync <target> <period factor>"
+
+SCSI_NCR_SETUP_DEFAULT_TAGS     (default: 4)
+       Default number of simultaneous tagged commands to a device.
+       < 1 means tagged command queuing disabled at start-up.
+
 SCSI_NCR_ALWAYS_SIMPLE_TAG     (default: defined)
        Use SIMPLE TAG for read and write commands.
        Can be changed by "setorder <ordered|simple|default>"
 
-SCSI_NCR_TAGGED_QUEUE_DISABLED (default: defined)
-       If defined, tagged command queuing is disable at start-up.
-       Can be changed by "settags <target> <maxtags>"
-
-SCSI_NCR_NO_DISCONNECT         (default: not defined)
-       If defined, targets are not allowed to disconnect.
+SCSI_NCR_SETUP_DISCONNECTION   (default: defined)
+       If defined, targets are allowed to disconnect.
 
-SCSI_NCR_FORCE_SYNC_NEGO       (default: not defined)
+SCSI_NCR_SETUP_FORCE_SYNC_NEGO (default: not defined)
        If defined, synchronous negotiation is tried for all SCSI-2 devices.
        Can be changed by "setsync <target> <period>"
 
-SCSI_NCR_DISABLE_MPARITY_CHECK (default: not defined)
-       If defined, master parity checking is disabled.
+SCSI_NCR_SETUP_MASTER_PARITY   (default: defined)
+       If defined, master parity checking is enabled.
 
-SCSI_NCR_DISABLE_PARITY_CHECK  (default: not defined)
-       If defined, SCSI parity checking is disabled.
+SCSI_NCR_SETUP_MASTER_PARITY   (default: defined)
+       If defined, SCSI parity checking is enabled.
 
 SCSI_NCR_PROFILE               (default: defined)
        If defined, profiling information is gathered.
@@ -404,10 +823,6 @@ SCSI_NCR_PROFILE           (default: defined)
 SCSI_NCR_MAX_SCATTER           (default: 128)
        Scatter list size of the driver ccb.
 
-SCSI_NCR_SEGMENT_SIZE          (default: 512)
-       If defined, the driver will try to use scatter segments of this size.
-       If not defined, the Linux scatter list is used as is.
-
 SCSI_NCR_MAX_TARGET            (default: 16)
        Max number of targets per host.
 
@@ -435,49 +850,38 @@ SCSI_NCR_MAX_LUN  (default: 8)
        Max number of LUNs per target.
 
 
-11. Provided files
+12. Installation
+
+12.1 Provided files
 
 Driver and common files:
 
        README.ncr53c8xx        : this file
        ChangeLog.ncr53c8xx     : change log
+       ConfigHelp.ncr53c8xx    : Part of Configure.help about the driver
        ncr53c8xx.h             : definitions
        ncr53c8xx.c             : the driver code
        scsitag.c               : command tool to enable tagged queue
        conf.modules            : sample of /etc/conf.modules
 
-Installation procedure 1 replacing the standard NCR53c7,8xx driver:
-
        Install.ncr53c8xx       : installation script
-       Uninstall.ncr53c8xx     : uninstallation script
-       53c7,8xx.h              : included by hosts.c, override the standard driver
-
-       Patch-1.2.13.ncr53c8xx     : patch for linux-1.2.13
-       Patch-1.3.45-49.ncr53c8xx  : patch for linux-1.3.45 to linux-1.3.49
-       Patch-1.3.50-100.ncr53c8xx : patch for linux-1.3.50 to linux-1.3.100
 
-Installation procedure 2 adding the driver to the kernel tree:
+       Patch-1.2.13.ncr53c8xx  : patch for linux-1.2.13
+       Patch-2.0.29.ncr53c8xx  : patch for linux-2.0.29
 
-       Install2.ncr53c8xx         : installation script
+You must untar the distribution with the following command:
 
-       Patch2-2.0.0-1.ncr53c8xx   : patch for linux-2.0.0 and linux-2.0.1
-       Patch2-Current.ncr53c8xx   : patch used for sub-levels > 1
+       tar zxvf ncrBsd2Linux-2.2b-src.tar.gz
 
-Prior to installing the driver, you must untar the distribution, as follow:
+The sub-directory ncr53c8xx-2.2b will be created. Change to this directory.
 
-       mkdir ncrB2L
-       cd ncrB2L
-       tar zxvf ncrBsd2Linux-1.12a-src.tar.gz
 
+12.2 Installation procedure
 
-12. Installation procedure for Linux version 1
+This install script has been tested with linux-1.2.13 and 2.0.29.
 
-This install script only supports linux-1.2.13 and linux-1.3.45 to 1.3.100.
-
-This procedure just moves the standard driver files to SAVE_53 sub-directory 
-of linux/drivers/scsi, copies the drivers file to that directory and patches 
-the SCSI Makefile.
-The standard driver can be restored with Uninstall.ncr53c8xx
+This procedure copies the new driver files to the kernel tree and
+applies a patch to some files of the kernel tree.
 
        If your linux directory is at the standard location
        "/usr/src/linux", just enter:
@@ -488,83 +892,546 @@ The standard driver can be restored with Uninstall.ncr53c8xx
 
        Make the kernel:
                Change to linux source directory
-               Configure with 53c7,8xx support (Y or m)  for Install.ncr53c8xx
+               Configure with NCR53C7,8XX support = N
+               Configure with NCR53C8XX   support = Y (or m)
                Make dependancies
                Make the kernel (use make zdisk first)
                Make and install modules if you have configured with 'm'
 
-       Notes:
-               Since release 1.3.90, additionnal configuation parameters 
-               have been added for the standard NCR driver.
-               Just reply Y or N as you want to these questions;
-               The NCR53C8XX driver ignores those parameters.
-
-       If you prefer the standard NCR driver of Linux:
-       just enter:
-               Uninstall.ncr53c8xx
-       or
-               Uninstall.ncr53c8xx <your_linux_directory>
-       if your linux root is not /usr/src/linux.
 
+13. Control commands under linux-1.2.13
 
-13. Installation procedure for Linux version 2
-
-This procedure adds the driver to the kernel tree.
-Using "make config" you can choose between the standard driver and the BSD one.
-It is possible to configure both drivers as modules and to switch from one 
-to the other at run time.
-Take care to unload the current driver module before loading the other one.
-
-       If your linux directory is at the standard location
-       "/usr/src/linux", just enter:
-               Install2.ncr53c8xx
-
-       Else enter:
-               Install2.ncr53c8xx <your_linux_directory>
-
-       Make the kernel:
-               Change to linux source directory
-               Configure with NCR53C8XX support (Y or m)
-               Make dependancies
-               Make the kernel (use make zdisk first)
-               Make and install modules if you have configured with 'm'
-
-
-14. Control commands under linux-1.2.13
-
-Profiling data and control commands using the proc SCSI file system are not 
-available for linux-1.2.13.
-The only control command available is "scsitag" which allows you to enable
-tagged command queuing support after linux boot-up.
+Profiling data and control commands using the proc SCSI file system
+are not available for linux-1.2.13.  The only control command
+available is "scsitag" which allows you to enable tagged command
+queuing support after linux boot-up.
 
 Tagged command queueing is disabled by default at system startup.
 
 You can enable tagged queue per device with the following command:
+
        scsitag device_name (ex: scsitag /dev/sda)
 
 Use "cc -o scsitag scsitag.c" to create the "scsitag" executable.
 
 
-15. Known problems
-
-15.1 Tagged commands with Iomega Jaz device
+14. Known problems
 
-I have not tried this device, however it has been reported to me the following:
-This device is capable of Tagged command queuing. However while spinning up,
-it rejects Tagged commands. This behaviour is conforms to 6.8.2 of SCSI-2 
-specifications. The current behaviour of the driver in that situation is not 
-satisfying. So do not enable Tagged command queuing for devices that are able 
-to spin down.
-The other problem that may appear is timeouts. The only way to avoid timeouts 
-seems to edit linux/drivers/scsi/sd.c and to increase the current timeout 
-values.
+14.1 Tagged commands with Iomega Jaz device
 
-15.2 Tagged command queuing cannot be disabled at run time
-
-Once Tagged command queuing has been enabled, the driver will not allow you to 
-disable this feature ("settags <target> 0" is not supported).
-This problem is due to some limitations of the code added to the Linux version 
-of the driver.
+I have not tried this device, however it has been reported to me the
+following: This device is capable of Tagged command queuing. However
+while spinning up, it rejects Tagged commands. This behaviour is
+conforms to 6.8.2 of SCSI-2 specifications. The current behaviour of
+the driver in that situation is not satisfying. So do not enable
+Tagged command queuing for devices that are able to spin down.  The
+other problem that may appear is timeouts. The only way to avoid
+timeouts seems to edit linux/drivers/scsi/sd.c and to increase the
+current timeout values.
+
+14.2 Device names change when another controller is added.
+
+When you add a new NCR53C8XX chip based controller to a system that already 
+has one or more controllers of this family, it may happen that the order 
+the driver registers them to the kernel causes problems due to device 
+name changes.
+When at least one controller uses NvRAM, SDMS BIOS version 4 allows you to 
+define the order the BIOS will scan the scsi boards. The driver attaches 
+controllers according to BIOS information if NvRAM detect option is set.
+
+If your controllers do not have NvRAM, you can:
+
+- Ask the driver to probe chip ids in reverse order from the boot command
+  line: ncr53c8xx=revprob:y
+- Make appropriate changes in the fstab.
+- Use the 'scsidev' tool from Eric Youngdale.
+
+15. SCSI problem troubleshooting
+
+Most SCSI problems are due to a non conformant SCSI bus or to buggy
+devices.  If infortunately you have SCSI problems, you can check the
+following things:
+
+- SCSI bus cables
+- terminations at both end of the SCSI chain
+- linux syslog messages (some of them may help you)
+
+If you donnot find the source of problems, you can configure the
+driver with no features enabled.
+
+- only asynchronous data transfers
+- tagged commands disabled
+- disconnections not allowed
+
+Now, if your SCSI bus is ok, your system have every chance to work
+with this safe configuration but performances will not be optimal.
+
+If it still fails, then you can send your problem description to
+appropriate mailing lists or news-groups.  Send me a copy in order to
+be sure I will receive it.  Obviously, a bug in the driver code is
+possible.
+
+     My email address: Gerard Roudier <groudier@club-internet.fr>
+
+Allowing disconnections is important if you use several devices on
+your SCSI bus but often causes problems with buggy devices.
+Synchronous data transfers increases throughput of fast devices like
+hard disks.  Good SCSI hard disks with a large cache gain advantage of
+tagged commands queuing.
+
+Try to enable one feature at a time with control commands.  For example:
+
+- echo "setsync all 25" >/proc/scsi/ncr53c8xx/0
+  Will enable fast synchronous data transfer negotiation for all targets.
+
+- echo "setflag 3" >/proc/scsi/ncr53c8xx/0
+  Will reset flags (no_sync) for target 3, and so will allow it to disconnect 
+  the SCSI Bus.
+
+- echo "settags 3 4" >/proc/scsi/ncr53c8xx/0
+  Will enable tagged command queuing for target 3 if that device supports it.
+
+Once you have found the device and the feature that cause problems, just 
+disable that feature for that device.
+
+
+16. Synchonous transfer negotiation tables
+
+Tables below have been created by calling the routine the driver uses
+for synchronisation negotiation timing calculation and chip setting.
+The first table corresponds to Ultra chips 53875 and 53C860 with 80 MHz 
+clock and 5 clock divisors.
+The second one has been calculated by setting the scsi clock to 40 Mhz 
+and using 4 clock divisors and so applies to all NCR53C8XX chips in fast 
+SCSI-2 mode.
+
+Periods are in nano-seconds and speeds are in Mega-transfers per second.
+1 Mega-transfers/second means 1 MB/s with 8 bits SCSI and 2 MB/s with 
+Wide16 SCSI.
+
+16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+
+ ----------------------------------------------
+ Negotiated                     NCR settings
+ Factor   Period   Speed        Period   Speed
+ ------   ------   ------       ------   ------
+ 12       50       20.000       50       20.000
+ 13       52       19.230       62       16.000
+ 14       56       17.857       62       16.000
+ 15       60       16.666       62       16.000
+ 16       64       15.625       75       13.333
+ 17       68       14.705       75       13.333
+ 18       72       13.888       75       13.333
+ 19       76       13.157       87       11.428
+ 20       80       12.500       87       11.428
+ 21       84       11.904       87       11.428
+ 22       88       11.363       93       10.666
+ 23       92       10.869       93       10.666
+ 24       96       10.416      100       10.000
+ 25      100       10.000      100       10.000
+ 26      104        9.615      112        8.888
+ 27      108        9.259      112        8.888
+ 28      112        8.928      112        8.888
+ 29      116        8.620      125        8.000
+ 30      120        8.333      125        8.000
+ 31      124        8.064      125        8.000
+ 32      128        7.812      131        7.619
+ 33      132        7.575      150        6.666
+ 34      136        7.352      150        6.666
+ 35      140        7.142      150        6.666
+ 36      144        6.944      150        6.666
+ 37      148        6.756      150        6.666
+ 38      152        6.578      175        5.714
+ 39      156        6.410      175        5.714
+ 40      160        6.250      175        5.714
+ 41      164        6.097      175        5.714
+ 42      168        5.952      175        5.714
+ 43      172        5.813      175        5.714
+ 44      176        5.681      187        5.333
+ 45      180        5.555      187        5.333
+ 46      184        5.434      187        5.333
+ 47      188        5.319      200        5.000
+ 48      192        5.208      200        5.000
+ 49      196        5.102      200        5.000
+
+
+16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+
+ ----------------------------------------------
+ Negotiated                     NCR settings
+ Factor   Period   Speed        Period   Speed
+ ------   ------   ------       ------   ------
+ 25      100       10.000      100       10.000
+ 26      104        9.615      125        8.000
+ 27      108        9.259      125        8.000
+ 28      112        8.928      125        8.000
+ 29      116        8.620      125        8.000
+ 30      120        8.333      125        8.000
+ 31      124        8.064      125        8.000
+ 32      128        7.812      131        7.619
+ 33      132        7.575      150        6.666
+ 34      136        7.352      150        6.666
+ 35      140        7.142      150        6.666
+ 36      144        6.944      150        6.666
+ 37      148        6.756      150        6.666
+ 38      152        6.578      175        5.714
+ 39      156        6.410      175        5.714
+ 40      160        6.250      175        5.714
+ 41      164        6.097      175        5.714
+ 42      168        5.952      175        5.714
+ 43      172        5.813      175        5.714
+ 44      176        5.681      187        5.333
+ 45      180        5.555      187        5.333
+ 46      184        5.434      187        5.333
+ 47      188        5.319      200        5.000
+ 48      192        5.208      200        5.000
+ 49      196        5.102      200        5.000
+
+
+17. Serial NVRAM (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+
+17.1 Features
+
+Enabling serial NVRAM support enables detection of the serial NVRAM included
+on Symbios and some Symbios compatible host adaptors, and Tekram boards. The 
+serial NVRAM is used by Symbios and Tekram to hold set up parameters for the 
+host adaptor and it's attached drives.
+
+The Symbios NVRAM also holds data on the boot order of host adaptors in a
+system with more than one host adaptor. This enables the order of scanning
+the cards for drives to be changed from the default used during host adaptor
+detection.
+
+This can be done to a limited extent at the moment using "reverse probe" but
+this only changes the order of detection of different types of cards. The
+NVRAM boot order settings can do this as well as change the order the same
+types of cards are scanned in, something "reverse probe" cannot do.
+
+Tekram boards using Symbios chips, DC390W/F/U, which have NVRAM are detected
+and this is used to distinguish between Symbios compatible and Tekram host 
+adaptors. This is used to disable the Symbios compatible "diff" setting
+incorrectly set on Tekram boards if the CONFIG_SCSI_53C8XX_SYMBIOS_COMPAT 
+configuration parameter is set enabling both Symbios and Tekram boards to be 
+used together with the Symbios cards using all their features, including
+"diff" support. ("led pin" support for Symbios compatible cards can remain
+enabled when using Tekram cards. It does nothing useful for Tekram host
+adaptors but does not cause problems either.)
+
+
+17.2 Symbios NVRAM layout
+
+typical data at NVRAM address 0x100 (53c810a NVRAM)
+-----------------------------------------------------------
+00 00
+64 01
+8e 0b
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00 
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 
+04 00 03 00 00 10 00 58 00 00 01 00 00 63 
+04 00 01 00 00 10 00 48 00 00 01 00 00 61 
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+fe fe
+00 00
+00 00
+-----------------------------------------------------------
+NVRAM layout details
+
+NVRAM Address 0x000-0x0ff not used
+              0x100-0x26f initialised data
+              0x270-0x7ff not used
+
+general layout
+
+        header  -   6 bytes,
+        data    - 356 bytes (checksum is byte sum of this data)
+        trailer -   6 bytes
+                  ---
+        total     368 bytes
+
+data area layout
+
+        controller set up  -  20 bytes
+        boot configuration -  56 bytes (4x14 bytes)
+        device set up      - 128 bytes (16x8 bytes)
+        unused (spare?)    - 152 bytes (19x8 bytes)
+                             ---
+        total                356 bytes
+
+-----------------------------------------------------------
+header
+
+00 00   - ?? start marker
+64 01   - byte count (lsb/msb excludes header/trailer)
+8e 0b   - checksum (lsb/msb excludes header/trailer)
+-----------------------------------------------------------
+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
+                   |      --flag bits 2
+                   |        0x00000001= scan order hi->low
+                   |            (default 0x00 - scan low->hi)
+                    --flag bits 1
+                       0x00000001 scam enable
+                       0x00000010 parity enable
+                       0x00000100 verbose boot msgs
+
+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
+-----------------------------------------------------------
+boot configuration
+
+boot order set by order of the devices in this table
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 -- 1st controller
+04 00 03 00 00 10 00 58 00 00 01 00 00 63    2nd controller
+04 00 01 00 00 10 00 48 00 00 01 00 00 61    3rd controller
+00 00 00 00 00 00 00 00 00 00 00 00 00 00    4th controller
+       |  |  |  |     |        |     |  |
+       |  |  |  |     |        |      ---- PCI io port adr
+       |  |  |  |     |         --0x01 init/scan at boot time
+       |  |  |  |      --PCI device/function number (0xdddddfff)
+       |  |   ----- ?? PCI vendor ID (lsb/msb)
+        ----PCI device ID (lsb/msb)
+
+?? use of this data is a guess but seems reasonable
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+device set up (up to 16 devices - includes controller)
+
+0f 00 08 08 64 00 0a 00 - id 0
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 - id 15
+ |     |  |  |     |  |
+ |     |  |  |      ----timeout (lsb/msb)
+ |     |  |   --synch period (0x?? 40 Mtrans/sec- fast 40) (probably 0x28)
+ |     |  |                  (0x30 20 Mtrans/sec- fast 20)
+ |     |  |                  (0x64 10 Mtrans/sec- fast )
+ |     |  |                  (0xc8  5 Mtrans/sec)
+ |     |  |                  (0x00  asynchronous)
+ |     |   -- ?? max sync offset (0x08 in NVRAM on 53c810a) 
+ |     |                         (0x10 in NVRAM on 53c875)
+ |      --device bus width (0x08 narrow)
+ |                         (0x10 16 bit wide)
+  --flag bits
+    0x00000001 - disconnect enabled
+    0x00000010 - scan at boot time
+    0x00000100 - scan luns
+    0x00001000 - queue tags enabled
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+?? use of this data is a guess but seems reasonable 
+(but it could be max bus width)
+
+default set up for 53c810a NVRAM
+default set up for 53c875 NVRAM - bus width     - 0x10
+                                - sync offset ? - 0x10
+                                - sync period   - 0x30
+-----------------------------------------------------------
+?? spare device space (32 bit bus ??)
+
+00 00 00 00 00 00 00 00  (19x8bytes)
+.
+.
+00 00 00 00 00 00 00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+trailer
+
+fe fe   - ? end marker ?
+00 00
+00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+
+
+
+17.3 Tekram NVRAM layout
+
+nvram 64x16 (1024 bit)
+
+Drive settings
+
+Drive ID 0-15 (addr 0x0yyyy0 = device setup, yyyy = ID)
+              (addr 0x0yyyy1 = 0x0000)
+
+    x x x x  x x x x  x x x x  x x x x
+               | | |      | |  | | | |
+               | | |      | |  | | |  ----- parity check   0 - off
+               | | |      | |  | | |                       1 - on
+               | | |      | |  | | |
+               | | |      | |  | |  ------- sync neg       0 - off
+               | | |      | |  | |                         1 - on
+               | | |      | |  | |
+               | | |      | |  |  --------- disconnect     0 - off
+               | | |      | |  |                           1 - on
+               | | |      | |  |
+               | | |      | |   ----------- start cmd      0 - off
+               | | |      | |                              1 - on
+               | | |      | |
+               | | |      |  -------------- tagged cmds    0 - off
+               | | |      |                                1 - on
+               | | |      | 
+               | | |       ---------------- wide neg       0 - off
+               | | |                                       1 - on
+               | | |
+                --------------------------- sync rate      0 - 10.0 Mtrans/sec
+                                                           1 -  8.0
+                                                           2 -  6.6
+                                                           3 -  5.7
+                                                           4 -  5.0
+                                                           5 -  4.0
+                                                           6 -  3.0
+                                                           7 -  2.0
+                                                           7 -  2.0
+                                                           8 - 20.0
+                                                           9 - 16.7
+                                                           a - 13.9
+                                                           b - 11.9
+
+Global settings
+
+Host flags 0 (addr 0x100000, 32) 
+
+    x x x x  x x x x  x x x x  x x x x
+    | | | |  | | | |           | | | |
+    | | | |  | | | |            ----------- host ID    0x00 - 0x0f
+    | | | |  | | | |
+    | | | |  | | |  ----------------------- support for    0 - off
+    | | | |  | | |                          > 2 drives     1 - on
+    | | | |  | | | 
+    | | | |  | |  ------------------------- support drives 0 - off
+    | | | |  | |                            > 1Gbytes      1 - on
+    | | | |  | |
+    | | | |  |  --------------------------- bus reset on   0 - off
+    | | | |  |                                power on     1 - on
+    | | | |  |
+    | | | |   ----------------------------- active neg     0 - off
+    | | | |                                                1 - on
+    | | | |
+    | | |  -------------------------------- imm seek       0 - off
+    | | |                                                  1 - on
+    | | |
+    | |  ---------------------------------- scan luns      0 - off
+    | |                                                    1 - on
+    | |
+     -------------------------------------- removable      0 - disable
+                                            as BIOS dev    1 - boot device
+                                                           2 - all
+
+Host flags 1 (addr 0x100001, 33)
+
+    x x x x  x x x x  x x x x  x x x x
+               | | |             | | | 
+               | | |              --------- boot delay     0 -   3 sec
+               | | |                                       1 -   5
+               | | |                                       2 -  10
+               | | |                                       3 -  20
+               | | |                                       4 -  30
+               | | |                                       5 -  60
+               | | |                                       6 - 120
+               | | |
+                --------------------------- max tag cmds   0 -  2
+                                                           1 -  4
+                                                           2 -  8
+                                                           3 - 16
+                                                           4 - 32
+
+Host flags 2 (addr 0x100010, 34)
+
+    x x x x  x x x x  x x x x  x x x x
+                                     |
+                                      ----- F2/F6 enable   0 - off ???
+                                                           1 - on  ???
+
+checksum (addr 0x111111)
+
+checksum = 0x1234 - (sum addr 0-63)
+
+----------------------------------------------------------------------------
+
+default nvram data:
+
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 
+
+0x0f07 0x0400 0x0001 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
 
 ===============================================================================
 End of NCR53C8XX driver README file
+
+
+
index 005c889d7503a8e1d7f4bc49abeb4213d0067070..5551cbad836dc50a7ee2afc88fdd2688ed218698 100644 (file)
@@ -829,6 +829,7 @@ struct aic7xxx_host {
   unsigned char            pause;            /* pause value for HCNTRL */
   unsigned char            qcntmask;
   unsigned char            qfullcount;
+  unsigned char            cmdoutcnt;
   unsigned char            curqincnt;
   struct Scsi_Host        *next;             /* allow for multiple IRQs */
   unsigned char            activescbs;       /* active scbs */
@@ -3880,6 +3881,19 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
 #endif
     while (qoutcnt > 0)
     {
+      if ((p->flags & PAGE_ENABLED) != 0)
+      {
+        p->cmdoutcnt += qoutcnt;
+        if (p->cmdoutcnt >= p->qfullcount)
+        {
+          /*
+           * Since paging only occurs on aic78x0 chips, we can use
+           * Auto Access Pause to clear the command count.
+           */
+          outb(0, p->base + CMDOUTCNT);
+          p->cmdoutcnt = 0;
+        }
+      }
       for (i = 0; i < qoutcnt; i++)
       {
         scb_index = inb(p->base + QOUTFIFO);
@@ -4638,9 +4652,9 @@ configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
     {
       old_verbose = aic7xxx_verbose;
       printk(KERN_INFO "aic7xxx: Warning - detected auto-termination.  Please "
-                       "verify driver");
+                       "verify driver\n");
       printk(KERN_INFO "         detected settings and use manual termination "
-                       "if necessary."); 
+                       "if necessary.\n"); 
 
       /* Configure auto termination. */
       outb(SEECS | SEEMS, p->base + SEECTL);
@@ -5291,6 +5305,9 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
     */
   outb(p->qcntmask, p->base + QCNTMASK);
 
+  outb(p->qfullcount, p->base + FIFODEPTH);
+  outb(0, p->base + CMDOUTCNT);
+
   /*
    * We don't have any waiting selections or disconnected SCBs.
    */
index fcde1ca8d950f3a3148cb60612340b89ac63c5ed..6ecae3bcdefd52f734059572da68f0a9113edc27 100644 (file)
@@ -1079,6 +1079,21 @@ scratch_ram {
        CUR_SCBID {
                size            1
        }
+       /*
+        * Running count of commands placed in
+        * the QOUTFIFO.  This is cleared by the
+        * kernel driver every FIFODEPTH commands.
+        */
+       CMDOUTCNT {
+               size            1
+       }
+       /*
+        * Maximum number of entries allowed in
+        * the QOUT/INFIFO.
+        */
+       FIFODEPTH {
+               size            1
+       }
        ARG_1 {
                size            1
                mask    SEND_MSG        0x80
index a5722f1fc8d6df0ca48bc9811df69b5fcecf9379..201a99c0e0469853d7a7c431f553a7df6f3adc45 100644 (file)
@@ -643,6 +643,15 @@ status_ok:
 
 complete:
        /* Post the SCB and issue an interrupt */
+.if ( SCB_PAGING )
+       /*
+        * Spin loop until there is space
+        * in the QOUTFIFO.
+        */
+       mov     A, FIFODEPTH;
+       cmp     CMDOUTCNT, A    je .;
+       inc     CMDOUTCNT;
+.endif
        mov     QOUTFIFO,SCB_TAG;
        mvi     INTSTAT,CMDCMPLT;
        test    SCB_CONTROL, ABORT_SCB jz dma_next_scb;
index f3b8c349ced4e6264b4eb411923a8bbb45dcf6e1..8fbf84fa630c24bf4f890176d4c529203457f58d 100644 (file)
 
 #define        CUR_SCBID                       0x58
 
-#define        ARG_1                           0x59
-#define        RETURN_1                        0x59
-#define                SEND_MSG                0x80
-#define                SEND_SENSE              0x40
-#define                SEND_REJ                0x20
+#define        CMDOUTCNT                       0x59
 
 #define        SCSICONF                        0x5a
 #define                RESET_SCSI              0x40
 
+#define        FIFODEPTH                       0x5a
+
+#define        ARG_1                           0x5b
+#define        RETURN_1                        0x5b
+#define                SEND_MSG                0x80
+#define                SEND_SENSE              0x40
+#define                SEND_REJ                0x20
+
 #define        HOSTCONF                        0x5d
 
 #define        HA_274_BIOSCTRL                 0x5f
index dda2f7a639431aefadf93f8e140a0de0524019bc..c90bad94dcc2059910711e4ac4d15a9f27c60190 100644 (file)
@@ -16,28 +16,28 @@ static u_int8_t seqprog[] = {
        0xff, 0x48, 0x2c, 0x18,
        0xff, 0x40, 0x64, 0x02,
        0x00, 0x9c, 0x03, 0x1e,
-       0x00, 0x6a, 0xaa, 0x17,
+       0x00, 0x6a, 0xad, 0x17,
        0xff, 0x65, 0x03, 0x1c,
        0xff, 0x9b, 0x58, 0x02,
        0xff, 0x58, 0x90, 0x02,
        0x0d, 0x6a, 0x3d, 0x00,
-       0x00, 0x58, 0x74, 0x17,
+       0x00, 0x58, 0x77, 0x17,
        0x28, 0xa0, 0x2a, 0x1a,
        0x50, 0x6a, 0x60, 0x00,
        0xff, 0x90, 0x4a, 0x02,
-       0x00, 0xa1, 0x9e, 0x17,
-       0xff, 0x6c, 0x59, 0x02,
-       0xff, 0x59, 0x27, 0x1c,
+       0x00, 0xa1, 0xa1, 0x17,
+       0xff, 0x6c, 0x5b, 0x02,
+       0xff, 0x5b, 0x27, 0x1c,
        0xff, 0x4a, 0x90, 0x02,
-       0x00, 0x65, 0xa7, 0x17,
-       0x00, 0x6a, 0x4f, 0x17,
+       0x00, 0x65, 0xaa, 0x17,
+       0x00, 0x6a, 0x52, 0x17,
        0xff, 0x65, 0x1f, 0x18,
        0x51, 0x6a, 0x91, 0x00,
        0xff, 0x58, 0xb3, 0x02,
-       0x00, 0x65, 0xb8, 0x17,
+       0x00, 0x65, 0xbb, 0x17,
        0x10, 0x6a, 0x60, 0x00,
        0x00, 0x65, 0x03, 0x10,
-       0xff, 0x59, 0x90, 0x02,
+       0xff, 0x5b, 0x90, 0x02,
        0xff, 0x58, 0xb3, 0x02,
        0x10, 0x6a, 0x60, 0x00,
        0x00, 0x65, 0x03, 0x10,
@@ -121,26 +121,26 @@ static u_int8_t seqprog[] = {
        0x7d, 0x6a, 0x3d, 0x00,
        0x00, 0x65, 0x7a, 0x10,
        0x08, 0x6a, 0x66, 0x00,
-       0xa9, 0x6a, 0x71, 0x17,
+       0xa9, 0x6a, 0x74, 0x17,
        0x00, 0x65, 0x82, 0x10,
        0x79, 0x6a, 0x3d, 0x00,
-       0x00, 0x65, 0x4c, 0x17,
+       0x00, 0x65, 0x4f, 0x17,
        0x10, 0x41, 0x76, 0x1a,
        0x88, 0x6a, 0x66, 0x00,
-       0xac, 0x6a, 0x6d, 0x17,
-       0x00, 0x65, 0x6a, 0x17,
+       0xac, 0x6a, 0x70, 0x17,
+       0x00, 0x65, 0x6d, 0x17,
        0xff, 0xa3, 0x43, 0x02,
        0x44, 0x6a, 0x66, 0x00,
-       0xa4, 0x6a, 0x70, 0x17,
+       0xa4, 0x6a, 0x73, 0x17,
        0xff, 0x43, 0x88, 0x1a,
        0x80, 0x02, 0x02, 0x00,
        0xff, 0x6a, 0x8c, 0x00,
        0xff, 0x6a, 0x8d, 0x00,
        0xff, 0x6a, 0x8e, 0x00,
-       0x00, 0x65, 0x6a, 0x17,
+       0x00, 0x65, 0x6d, 0x17,
        0x01, 0x43, 0x8a, 0x18,
        0xbf, 0x3d, 0x3d, 0x02,
-       0x00, 0x3d, 0x41, 0x17,
+       0x00, 0x3d, 0x44, 0x17,
        0x80, 0x02, 0xa2, 0x1a,
        0xff, 0x65, 0x9c, 0x1e,
        0xff, 0x43, 0x43, 0x06,
@@ -149,14 +149,14 @@ static u_int8_t seqprog[] = {
        0x08, 0x44, 0x44, 0x06,
        0x00, 0x45, 0x45, 0x08,
        0x88, 0x6a, 0x66, 0x00,
-       0x44, 0x6a, 0x70, 0x17,
+       0x44, 0x6a, 0x73, 0x17,
        0x08, 0x6a, 0x8c, 0x00,
        0xff, 0x6a, 0x8d, 0x02,
        0xff, 0x6a, 0x8e, 0x02,
        0x0d, 0x93, 0x93, 0x00,
-       0x00, 0x65, 0x9a, 0x17,
-       0x88, 0x6a, 0x92, 0x17,
-       0x00, 0x65, 0x6a, 0x17,
+       0x00, 0x65, 0x9d, 0x17,
+       0x88, 0x6a, 0x95, 0x17,
+       0x00, 0x65, 0x6d, 0x17,
        0x10, 0x0c, 0x82, 0x1e,
        0xff, 0x08, 0xa9, 0x02,
        0xff, 0x09, 0xaa, 0x02,
@@ -167,19 +167,19 @@ static u_int8_t seqprog[] = {
        0x7f, 0x02, 0x02, 0x02,
        0xe1, 0x6a, 0x91, 0x00,
        0x00, 0x65, 0x5c, 0x10,
-       0x00, 0x65, 0x4c, 0x17,
+       0x00, 0x65, 0x4f, 0x17,
        0x88, 0x6a, 0x66, 0x00,
-       0xb4, 0x6a, 0x6f, 0x17,
+       0xb4, 0x6a, 0x72, 0x17,
        0xff, 0x6a, 0x8d, 0x02,
        0xff, 0x6a, 0x8e, 0x02,
-       0x00, 0x65, 0x6a, 0x17,
-       0x3d, 0x6a, 0x41, 0x17,
+       0x00, 0x65, 0x6d, 0x17,
+       0x3d, 0x6a, 0x44, 0x17,
        0x00, 0x65, 0x5c, 0x10,
-       0x00, 0x65, 0x4c, 0x17,
+       0x00, 0x65, 0x4f, 0x17,
        0xff, 0x06, 0xa2, 0x02,
        0x00, 0x65, 0x5c, 0x10,
        0xff, 0x34, 0xb2, 0x1a,
-       0x08, 0x6a, 0x2f, 0x17,
+       0x08, 0x6a, 0x32, 0x17,
        0x35, 0x6a, 0x65, 0x00,
        0xff, 0x34, 0x66, 0x02,
        0x01, 0x0c, 0xb4, 0x1e,
@@ -197,116 +197,119 @@ static u_int8_t seqprog[] = {
        0x40, 0x6a, 0x0c, 0x00,
        0xff, 0x6a, 0x34, 0x02,
        0x00, 0x65, 0x5c, 0x10,
-       0x64, 0x6a, 0x3c, 0x17,
+       0x64, 0x6a, 0x3f, 0x17,
        0xff, 0x64, 0x4b, 0x02,
-       0x80, 0x64, 0x0b, 0x1b,
-       0x04, 0x64, 0xfe, 0x1c,
-       0x02, 0x64, 0x01, 0x1d,
+       0x80, 0x64, 0x0e, 0x1b,
+       0x04, 0x64, 0x01, 0x1d,
+       0x02, 0x64, 0x04, 0x1d,
        0x00, 0x6a, 0xd1, 0x1c,
-       0x03, 0x64, 0x09, 0x1d,
-       0x01, 0x64, 0xf2, 0x1c,
-       0x07, 0x64, 0x2d, 0x1d,
+       0x03, 0x64, 0x0c, 0x1d,
+       0x01, 0x64, 0xf5, 0x1c,
+       0x07, 0x64, 0x30, 0x1d,
        0x08, 0x64, 0xcf, 0x1c,
        0x11, 0x6a, 0x91, 0x00,
-       0x07, 0x6a, 0x2f, 0x17,
+       0x07, 0x6a, 0x32, 0x17,
        0xff, 0x06, 0x6a, 0x02,
        0x00, 0x65, 0x5c, 0x10,
        0xff, 0xa8, 0xd3, 0x1a,
        0xff, 0xa2, 0xda, 0x1e,
        0x01, 0x6a, 0x3d, 0x00,
-       0x00, 0xb9, 0x74, 0x17,
+       0x00, 0xb9, 0x77, 0x17,
        0xff, 0xa2, 0xda, 0x1e,
        0x71, 0x6a, 0x91, 0x00,
-       0x40, 0x59, 0xda, 0x18,
+       0x40, 0x5b, 0xda, 0x18,
        0xff, 0xb9, 0xb3, 0x02,
-       0x00, 0x65, 0xe4, 0x10,
+       0x00, 0x65, 0xe7, 0x10,
        0x20, 0xa0, 0xe0, 0x1a,
        0xff, 0x90, 0x4a, 0x02,
        0xff, 0xb3, 0x49, 0x02,
-       0x00, 0xa1, 0x9e, 0x17,
+       0x00, 0xa1, 0xa1, 0x17,
        0xff, 0x49, 0x6d, 0x02,
        0xff, 0x4a, 0x90, 0x02,
+       0xff, 0x5a, 0x64, 0x02,
+       0x00, 0x59, 0xe1, 0x1c,
+       0x01, 0x59, 0x59, 0x06,
        0xff, 0xb9, 0x9d, 0x02,
        0x02, 0x6a, 0x91, 0x00,
-       0x08, 0xa0, 0xe4, 0x1e,
+       0x08, 0xa0, 0xe7, 0x1e,
        0x91, 0x6a, 0x91, 0x00,
-       0xff, 0xb3, 0xf0, 0x1c,
+       0xff, 0xb3, 0xf3, 0x1c,
        0xff, 0xb3, 0x64, 0x02,
-       0x00, 0xb9, 0xea, 0x1c,
-       0x00, 0x65, 0xa7, 0x17,
+       0x00, 0xb9, 0xed, 0x1c,
+       0x00, 0x65, 0xaa, 0x17,
        0xff, 0x64, 0x90, 0x02,
-       0x00, 0x65, 0xec, 0x10,
+       0x00, 0x65, 0xef, 0x10,
        0x0d, 0x6a, 0x3d, 0x00,
-       0x00, 0xb3, 0x74, 0x17,
+       0x00, 0xb3, 0x77, 0x17,
        0xff, 0x48, 0xba, 0x02,
        0xff, 0x90, 0x48, 0x02,
        0x00, 0x65, 0x2f, 0x16,
        0x00, 0x65, 0x69, 0x10,
-       0x00, 0x65, 0xa7, 0x17,
+       0x00, 0x65, 0xaa, 0x17,
        0x00, 0x65, 0x69, 0x10,
-       0x4d, 0x6a, 0x37, 0x17,
+       0x4d, 0x6a, 0x3a, 0x17,
        0xff, 0x4d, 0x64, 0x02,
-       0x00, 0x66, 0x37, 0x17,
+       0x00, 0x66, 0x3a, 0x17,
        0xff, 0x64, 0x64, 0x06,
-       0x52, 0x66, 0xf8, 0x18,
+       0x52, 0x66, 0xfb, 0x18,
        0xff, 0x66, 0x66, 0x06,
-       0xff, 0x64, 0xf4, 0x1a,
+       0xff, 0x64, 0xf7, 0x1a,
        0x41, 0x6a, 0x91, 0x00,
-       0x20, 0x59, 0xcd, 0x1c,
-       0x80, 0x59, 0xcf, 0x18,
+       0x20, 0x5b, 0xcd, 0x1c,
+       0x80, 0x5b, 0xcf, 0x18,
        0x10, 0x4c, 0x03, 0x00,
        0x00, 0x65, 0xcf, 0x10,
        0x04, 0xa0, 0xa0, 0x00,
-       0x00, 0x65, 0xb8, 0x17,
+       0x00, 0x65, 0xbb, 0x17,
        0x00, 0x65, 0x69, 0x10,
        0x10, 0x41, 0xcf, 0x1e,
        0xff, 0x43, 0xa3, 0x02,
        0xa4, 0x6a, 0x66, 0x00,
-       0x44, 0x6a, 0x70, 0x17,
+       0x44, 0x6a, 0x73, 0x17,
        0xac, 0x6a, 0x66, 0x00,
-       0x14, 0x6a, 0x70, 0x17,
-       0xa9, 0x6a, 0x71, 0x17,
+       0x14, 0x6a, 0x73, 0x17,
+       0xa9, 0x6a, 0x74, 0x17,
        0x00, 0x65, 0xcf, 0x10,
        0xef, 0x41, 0x41, 0x02,
        0x00, 0x65, 0xcf, 0x10,
        0x78, 0x64, 0xcd, 0x1a,
        0x07, 0x64, 0x64, 0x02,
        0x00, 0x42, 0x42, 0x00,
-       0x00, 0x42, 0x9e, 0x17,
-       0xff, 0x6c, 0x59, 0x02,
-       0xff, 0x59, 0x25, 0x19,
-       0xff, 0x59, 0x15, 0x1d,
-       0xff, 0x59, 0x90, 0x02,
-       0x04, 0xa0, 0x2a, 0x1f,
-       0x00, 0x65, 0x27, 0x11,
+       0x00, 0x42, 0xa1, 0x17,
+       0xff, 0x6c, 0x5b, 0x02,
+       0xff, 0x5b, 0x28, 0x19,
+       0xff, 0x5b, 0x18, 0x1d,
+       0xff, 0x5b, 0x90, 0x02,
+       0x04, 0xa0, 0x2d, 0x1f,
+       0x00, 0x65, 0x2a, 0x11,
        0xff, 0x06, 0x6a, 0x02,
-       0x01, 0x0c, 0x16, 0x1f,
-       0x04, 0x0c, 0x16, 0x1b,
+       0x01, 0x0c, 0x19, 0x1f,
+       0x04, 0x0c, 0x19, 0x1b,
        0xe0, 0x03, 0x4c, 0x02,
-       0xe0, 0x4c, 0x2a, 0x19,
-       0x20, 0x12, 0x2a, 0x19,
+       0xe0, 0x4c, 0x2d, 0x19,
+       0x20, 0x12, 0x2d, 0x19,
        0x20, 0x41, 0x41, 0x00,
-       0x59, 0x6a, 0x37, 0x17,
+       0x5b, 0x6a, 0x3a, 0x17,
        0xff, 0x3f, 0x64, 0x02,
-       0x00, 0x59, 0x65, 0x06,
-       0x00, 0x65, 0x2a, 0x13,
-       0xff, 0x59, 0x90, 0x02,
+       0x00, 0x5b, 0x65, 0x06,
+       0x00, 0x65, 0x2d, 0x13,
+       0xff, 0x5b, 0x90, 0x02,
        0xff, 0x42, 0x64, 0x02,
-       0x00, 0xa1, 0x2a, 0x19,
-       0x20, 0xa0, 0x2a, 0x1f,
-       0x04, 0xa0, 0x2a, 0x1f,
-       0x00, 0x6a, 0x4f, 0x17,
-       0xff, 0x65, 0x2a, 0x1d,
+       0x00, 0xa1, 0x2d, 0x19,
+       0x20, 0xa0, 0x2d, 0x1f,
+       0x04, 0xa0, 0x2d, 0x1f,
+       0x00, 0x6a, 0x52, 0x17,
+       0xff, 0x65, 0x2d, 0x1d,
        0xfb, 0xa0, 0xa0, 0x02,
        0x40, 0x41, 0x41, 0x00,
        0x00, 0x65, 0xcf, 0x10,
        0x31, 0x6a, 0x91, 0x00,
-       0x0c, 0x6a, 0x2f, 0x17,
+       0x0c, 0x6a, 0x32, 0x17,
        0x00, 0x65, 0xcf, 0x10,
        0x61, 0x6a, 0x91, 0x00,
        0x00, 0x65, 0xcf, 0x10,
        0x50, 0x6a, 0x60, 0x00,
-       0xff, 0x34, 0x33, 0x1f,
+       0xff, 0x34, 0x36, 0x1f,
        0x10, 0x6a, 0x60, 0x00,
        0xc1, 0x6a, 0x91, 0x00,
        0x10, 0x4c, 0x03, 0x00,
@@ -314,50 +317,50 @@ static u_int8_t seqprog[] = {
        0xff, 0x65, 0x35, 0x02,
        0x10, 0x6a, 0x60, 0x01,
        0xff, 0x06, 0x6a, 0x02,
-       0x01, 0x0c, 0x38, 0x1f,
-       0x04, 0x0c, 0x38, 0x1b,
+       0x01, 0x0c, 0x3b, 0x1f,
+       0x04, 0x0c, 0x3b, 0x1b,
        0xe0, 0x03, 0x4c, 0x02,
-       0xe0, 0x4c, 0x3f, 0x19,
+       0xe0, 0x4c, 0x42, 0x19,
        0xff, 0x65, 0x66, 0x02,
        0xff, 0x12, 0x6d, 0x03,
        0xff, 0x06, 0x6a, 0x03,
        0xd1, 0x6a, 0x91, 0x00,
        0x00, 0x65, 0x5c, 0x10,
        0xff, 0x65, 0x93, 0x02,
-       0x01, 0x0b, 0x49, 0x1b,
-       0x10, 0x0c, 0x42, 0x1f,
-       0x04, 0x0b, 0x46, 0x1b,
+       0x01, 0x0b, 0x4c, 0x1b,
+       0x10, 0x0c, 0x45, 0x1f,
+       0x04, 0x0b, 0x49, 0x1b,
        0xff, 0x6a, 0x65, 0x02,
-       0x04, 0x93, 0x48, 0x1b,
-       0x01, 0x94, 0x47, 0x1f,
-       0x10, 0x94, 0x48, 0x1b,
+       0x04, 0x93, 0x4b, 0x1b,
+       0x01, 0x94, 0x4a, 0x1f,
+       0x10, 0x94, 0x4b, 0x1b,
        0xc7, 0x93, 0x93, 0x02,
-       0x38, 0x93, 0x4a, 0x1b,
+       0x38, 0x93, 0x4d, 0x1b,
        0xff, 0x6a, 0x6a, 0x03,
-       0x80, 0x41, 0x4b, 0x1f,
-       0x40, 0x41, 0x4b, 0x1b,
+       0x80, 0x41, 0x4e, 0x1f,
+       0x40, 0x41, 0x4e, 0x1b,
        0x21, 0x6a, 0x91, 0x01,
        0xff, 0x65, 0x90, 0x02,
-       0xff, 0x59, 0x64, 0x02,
-       0x00, 0xb9, 0x53, 0x19,
-       0x04, 0xa0, 0x5d, 0x1b,
+       0xff, 0x5b, 0x64, 0x02,
+       0x00, 0xb9, 0x56, 0x19,
+       0x04, 0xa0, 0x60, 0x1b,
        0x01, 0x65, 0x65, 0x06,
        0xff, 0x3e, 0x64, 0x02,
-       0x00, 0x65, 0x4f, 0x19,
-       0x00, 0x6a, 0xaa, 0x17,
+       0x00, 0x65, 0x52, 0x19,
+       0x00, 0x6a, 0xad, 0x17,
        0x0d, 0x6a, 0x3d, 0x00,
-       0x00, 0x59, 0x74, 0x17,
-       0xff, 0xa8, 0x5b, 0x1f,
+       0x00, 0x5b, 0x77, 0x17,
+       0xff, 0xa8, 0x5e, 0x1f,
        0x10, 0xa0, 0xa0, 0x00,
-       0x08, 0xa0, 0x4b, 0x1f,
+       0x08, 0xa0, 0x4e, 0x1f,
        0xff, 0x6a, 0x65, 0x01,
-       0x08, 0xa0, 0x5c, 0x1b,
-       0xff, 0xba, 0x63, 0x1d,
+       0x08, 0xa0, 0x5f, 0x1b,
+       0xff, 0xba, 0x66, 0x1d,
        0xff, 0xbb, 0x49, 0x02,
        0xff, 0xba, 0x90, 0x02,
        0xff, 0x49, 0xbb, 0x02,
        0xff, 0x65, 0x90, 0x02,
-       0xff, 0xbb, 0x68, 0x1d,
+       0xff, 0xbb, 0x6b, 0x1d,
        0xff, 0xba, 0x49, 0x02,
        0xff, 0xbb, 0x90, 0x02,
        0xff, 0x49, 0xba, 0x02,
@@ -386,7 +389,7 @@ static u_int8_t seqprog[] = {
        0xff, 0x6a, 0x8d, 0x02,
        0xff, 0x6a, 0x8e, 0x02,
        0xff, 0x3d, 0x93, 0x02,
-       0x04, 0x3d, 0x8c, 0x1b,
+       0x04, 0x3d, 0x8f, 0x1b,
        0xa0, 0x6a, 0x65, 0x00,
        0x1c, 0x65, 0x64, 0x06,
        0xff, 0x6c, 0x99, 0x02,
@@ -396,14 +399,14 @@ static u_int8_t seqprog[] = {
        0xff, 0x6c, 0x99, 0x02,
        0xff, 0x6c, 0x99, 0x02,
        0xff, 0x6c, 0x99, 0x02,
-       0x00, 0x65, 0x83, 0x19,
+       0x00, 0x65, 0x86, 0x19,
        0x0a, 0x93, 0x93, 0x00,
-       0x00, 0x65, 0x9a, 0x17,
-       0x04, 0x3d, 0x4b, 0x1f,
-       0xa0, 0x6a, 0x92, 0x17,
-       0x00, 0x65, 0x93, 0x17,
-       0x00, 0x65, 0x93, 0x17,
-       0x00, 0x65, 0x93, 0x11,
+       0x00, 0x65, 0x9d, 0x17,
+       0x04, 0x3d, 0x4e, 0x1f,
+       0xa0, 0x6a, 0x95, 0x17,
+       0x00, 0x65, 0x96, 0x17,
+       0x00, 0x65, 0x96, 0x17,
+       0x00, 0x65, 0x96, 0x11,
        0xff, 0x65, 0x66, 0x02,
        0xff, 0x99, 0x6d, 0x02,
        0xff, 0x99, 0x6d, 0x02,
@@ -412,40 +415,40 @@ static u_int8_t seqprog[] = {
        0xff, 0x99, 0x6d, 0x02,
        0xff, 0x99, 0x6d, 0x02,
        0xff, 0x99, 0x6d, 0x03,
-       0x08, 0x94, 0x9a, 0x1f,
+       0x08, 0x94, 0x9d, 0x1f,
        0xf7, 0x93, 0x93, 0x02,
-       0x08, 0x93, 0x9c, 0x1b,
+       0x08, 0x93, 0x9f, 0x1b,
        0xff, 0x6a, 0x6a, 0x03,
        0xff, 0x65, 0x66, 0x02,
        0x4c, 0x66, 0x66, 0x0a,
        0x03, 0x66, 0x66, 0x02,
        0xbc, 0x66, 0x66, 0x06,
        0x6a, 0x65, 0x64, 0x0a,
-       0x08, 0x65, 0xa5, 0x1f,
+       0x08, 0x65, 0xa8, 0x1f,
        0x02, 0x64, 0x64, 0x06,
        0xff, 0x64, 0x90, 0x02,
        0xff, 0x66, 0x65, 0x03,
        0xff, 0x53, 0xba, 0x02,
        0xff, 0x6a, 0xb9, 0x00,
        0xff, 0x90, 0x53, 0x03,
-       0xff, 0x53, 0xb6, 0x19,
-       0xff, 0x52, 0xad, 0x19,
+       0xff, 0x53, 0xb9, 0x19,
+       0xff, 0x52, 0xb0, 0x19,
        0xff, 0x6a, 0x65, 0x01,
        0xff, 0x52, 0x90, 0x02,
-       0x10, 0xa0, 0xb1, 0x1f,
+       0x10, 0xa0, 0xb4, 0x1f,
        0xef, 0xa0, 0xa0, 0x02,
-       0x00, 0x65, 0xb3, 0x11,
-       0xff, 0xa8, 0xb3, 0x1b,
-       0xff, 0xb3, 0xb5, 0x1d,
+       0x00, 0x65, 0xb6, 0x11,
+       0xff, 0xa8, 0xb6, 0x1b,
+       0xff, 0xb3, 0xb8, 0x1d,
        0x01, 0x6a, 0x3d, 0x00,
-       0x00, 0xb9, 0x74, 0x17,
-       0x00, 0x90, 0x5e, 0x11,
+       0x00, 0xb9, 0x77, 0x17,
+       0x00, 0x90, 0x61, 0x11,
        0xff, 0x53, 0x90, 0x02,
        0xff, 0xba, 0x53, 0x03,
        0xff, 0x6a, 0xbb, 0x00,
        0xff, 0x52, 0xba, 0x02,
        0xff, 0x90, 0x52, 0x02,
-       0xff, 0xba, 0x4b, 0x1d,
+       0xff, 0xba, 0x4e, 0x1d,
        0xff, 0xba, 0x90, 0x02,
        0xff, 0x52, 0xbb, 0x02,
        0xff, 0x52, 0x90, 0x03,
@@ -466,14 +469,15 @@ struct patch {
        { 0x00000004, 1, 0x023, 0x027 },
        { 0x00000002, 0, 0x02f, 0x033 },
        { 0x00000008, 0, 0x04f, 0x056 },
-       { 0x00000004, 1, 0x0e5, 0x0ea },
-       { 0x00000004, 0, 0x0ff, 0x100 },
-       { 0x00000004, 0, 0x110, 0x111 },
-       { 0x00000004, 1, 0x111, 0x115 },
-       { 0x00000004, 1, 0x120, 0x125 },
-       { 0x00000004, 0, 0x125, 0x127 },
-       { 0x00000004, 0, 0x14f, 0x169 },
-       { 0x00000004, 1, 0x169, 0x16a },
-       { 0x00000004, 0, 0x1aa, 0x1bf },
+       { 0x00000004, 0, 0x0e0, 0x0e3 },
+       { 0x00000004, 1, 0x0e8, 0x0ed },
+       { 0x00000004, 0, 0x102, 0x103 },
+       { 0x00000004, 0, 0x113, 0x114 },
+       { 0x00000004, 1, 0x114, 0x118 },
+       { 0x00000004, 1, 0x123, 0x128 },
+       { 0x00000004, 0, 0x128, 0x12a },
+       { 0x00000004, 0, 0x152, 0x16c },
+       { 0x00000004, 1, 0x16c, 0x16d },
+       { 0x00000004, 0, 0x1ad, 0x1c2 },
        { 0x00000000, 0, 0x000, 0x000 }
 };
diff --git a/drivers/scsi/dc390.h b/drivers/scsi/dc390.h
new file mode 100644 (file)
index 0000000..bfe20c9
--- /dev/null
@@ -0,0 +1,147 @@
+/***********************************************************************
+ *     FILE NAME : DC390.H                                            *
+ *          BY   : C.L. Huang                                         *
+ *     Description: Device Driver for Tekram DC-390(T) PCI SCSI       *
+ *                  Bus Master Host Adapter                           *
+ ***********************************************************************/
+
+/* Kernel version autodetection */
+
+#include <linux/version.h>
+/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */
+#define ASC_LINUX_VERSION(V, P, S)     (((V) * 65536) + ((P) * 256) + (S))
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,50)
+#define VERSION_ELF_1_2_13
+#elseif LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,95)
+#define VERSION_1_3_85
+#else
+#define VERSION_2_0_0
+#endif
+
+/*
+ * AMD 53C974 driver, header file
+ */
+
+#ifndef DC390_H
+#define DC390_H
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#ifdef VERSION_2_0_0
+#include <scsi/scsicam.h>
+#else
+#include <linux/scsicam.h>
+#endif
+
+extern int DC390_detect(Scsi_Host_Template *psht);
+extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+extern int DC390_abort(Scsi_Cmnd *cmd);
+
+#ifdef VERSION_2_0_0
+extern int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags);
+#else
+extern int DC390_reset(Scsi_Cmnd *cmd);
+#endif
+
+#ifdef VERSION_ELF_1_2_13
+extern int DC390_bios_param(Disk *disk, int devno, int geom[]);
+#else
+extern int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]);
+#endif
+
+#ifdef MODULE
+static int DC390_release(struct Scsi_Host *);
+#else
+#define DC390_release NULL
+#endif
+
+#ifndef VERSION_ELF_1_2_13
+extern struct proc_dir_entry proc_scsi_tmscsim;
+extern int tmscsim_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
+#endif
+
+#ifdef VERSION_2_0_0
+
+#define DC390_T    {                   \
+       NULL,   /* *next */             \
+       NULL,   /* *usage_count */      \
+       &proc_scsi_tmscsim,     /* *proc_dir */         \
+       tmscsim_proc_info,      /* (*proc_info)() */    \
+       "Tekram DC390(T) V1.10 Dec-05-1996",  /* *name */ \
+       DC390_detect,                   \
+       DC390_release,  /* (*release)() */      \
+       NULL,   /* *(*info)() */        \
+       NULL,   /* (*command)() */      \
+       DC390_queue_command,    \
+       DC390_abort,            \
+       DC390_reset,            \
+       NULL, /* slave attach */\
+       DC390_bios_param,       \
+       10,/* can queue(-1) */  \
+       7, /* id(-1) */         \
+       SG_ALL,                 \
+       2, /* cmd per lun(2) */ \
+       0, /* present */        \
+       0, /* unchecked isa dma */ \
+       DISABLE_CLUSTERING      \
+       }
+#endif
+
+
+#ifdef VERSION_1_3_85
+
+#define DC390_T    {                   \
+       NULL,   /* *next */             \
+       NULL,   /* *usage_count */      \
+       &proc_scsi_tmscsim,     /* *proc_dir */         \
+       tmscsim_proc_info,      /* (*proc_info)() */    \
+       "Tekram DC390(T) V1.10 Dec-05-1996",  /* *name */ \
+       DC390_detect,                   \
+       DC390_release,  /* (*release)() */      \
+       NULL,   /* *(*info)() */        \
+       NULL,   /* (*command)() */      \
+       DC390_queue_command,    \
+       DC390_abort,            \
+       DC390_reset,            \
+       NULL, /* slave attach */\
+       DC390_bios_param,       \
+       10,/* can queue(-1) */  \
+       7, /* id(-1) */         \
+       SG_ALL,                 \
+       2, /* cmd per lun(2) */ \
+       0, /* present */        \
+       0, /* unchecked isa dma */ \
+       DISABLE_CLUSTERING      \
+       }
+#endif
+
+
+#ifdef VERSION_ELF_1_2_13
+
+#define DC390_T     {          \
+       NULL,                   \
+       NULL,                   \
+       "Tekram DC390(T) V1.10 Dec-05-1996",\
+       DC390_detect,           \
+       DC390_release,                  \
+       NULL, /* info */        \
+       NULL, /* command, deprecated */ \
+       DC390_queue_command,    \
+       DC390_abort,            \
+       DC390_reset,            \
+       NULL, /* slave attach */\
+       DC390_bios_param,       \
+       10,/* can queue(-1) */  \
+       7, /* id(-1) */         \
+       16,/* old (SG_ALL) */   \
+       2, /* cmd per lun(2) */ \
+       0, /* present */        \
+       0, /* unchecked isa dma */ \
+       DISABLE_CLUSTERING      \
+       }
+#endif
+
+#endif /* defined(HOSTS_C) || defined(MODULE) */
+
+#endif /* DC390_H */
index 287344ea9eebfe63698501093a2fb0a60c6ed093..93bc46c2f33a010804629eb2c3ba83d095d12c1d 100644 (file)
 #include "NCR53c406a.h"
 #endif
 
+#ifdef CONFIG_SCSI_DC390T
+#include "dc390.h"
+#endif
+
 #ifdef CONFIG_SCSI_AM53C974
 #include "AM53C974.h"
 #endif
@@ -284,6 +288,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #ifdef CONFIG_SCSI_DTC3280
         DTC3x80,
 #endif
+#ifdef CONFIG_SCSI_DC390T
+    DC390_T,
+#endif
 #ifdef CONFIG_SCSI_NCR53C7xx
     NCR53c7xx,
 #endif
index 5e9eb77372c721fe3568ff8cd52a087a35608d5f..997e658ee2259d7b3c439d4c7c6df2b38266ee04 100644 (file)
 **  And has been ported to NetBSD by
 **          Charles M. Hannum           <mycroft@gnu.ai.mit.edu>
 **
+**-----------------------------------------------------------------------------
+**
+**                     Brief history
+**
+**  December 10 1995 by Gerard Roudier:
+**     Initial port to Linux.
+**
+**  June 23 1996 by Gerard Roudier:
+**     Support for 64 bits architectures (Alpha).
+**
+**  November 30 1996 by Gerard Roudier:
+**     Support for Fast-20 scsi.
+**     Support for large DMA fifo and 128 dwords bursting.
+**
+**  February 27 1997 by Gerard Roudier:
+**     Support for Fast-40 scsi.
+**     Support for on-Board RAM.
+**
+**  May 3 1997 by Gerard Roudier:
+**     Full support for scsi scripts instructions pre-fetching.
+**
+**  May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
+**     Support for NvRAM detection and reading.
+**
 *******************************************************************************
 */
 
 /*
-**     30 August 1996, version 1.12c
+**     26 July 1997, version 2.4
 **
 **     Supported SCSI-II features:
 **         Synchronous negotiation
 **         Etc...
 **
 **     Supported NCR chips:
-**             53C810          (NCR BIOS in flash-bios required) 
-**             53C815          (~53C810 with on board rom BIOS)
-**             53C820          (Wide, NCR BIOS in flash bios required)
-**             53C825          (Wide, ~53C820 with on board rom BIOS)
-**             53C860          (not yet tested)
-**             53C875          (not yet tested)
+**             53C810          (8 bits, Fast SCSI-2, no rom BIOS) 
+**             53C815          (8 bits, Fast SCSI-2, on board rom BIOS)
+**             53C820          (Wide,   Fast SCSI-2, no rom BIOS)
+**             53C825          (Wide,   Fast SCSI-2, on board rom BIOS)
+**             53C860          (8 bits, Fast 20,     no rom BIOS)
+**             53C875          (Wide,   Fast 20,     on board rom BIOS)
+**             53C895          (Wide,   Fast 40,     on board rom BIOS)
 **
 **     Other features:
-**             Memory mapped IO (linux-1.3.X only)
+**             Memory mapped IO (linux-1.3.X and above only)
 **             Module
 **             Shared IRQ (since linux-1.3.72)
 */
 
-#define SCSI_NCR_DEBUG
 #define SCSI_NCR_DEBUG_FLAGS   (0)             
 
-#define NCR_DATE "pl23 95/09/07"
-
-#define NCR_VERSION    (2)
-
 #define NCR_GETCC_WITHMSG
 
 /*==========================================================
 
 #include <linux/version.h>
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-#include "linux/blk.h"
+#include <linux/blk.h>
 #else
 #include "../block/blk.h"
 #endif
 
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)
+#include <linux/init.h>
+#else
+#ifndef        __initdata
+#define        __initdata
+#endif
+#ifndef        __initfunc
+#define        __initfunc(__arginit) __arginit
+#endif
+#endif
+
 #include "scsi.h"
 #include "hosts.h"
 #include "constants.h"
@@ -131,15 +162,6 @@ typedef u32 u_int32;
 **==========================================================
 */
 
-/*
-**     Proc info and user command support
-*/
-
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-#define SCSI_NCR_PROFILE
-#define SCSI_NCR_USER_COMMAND
-#endif
-
 /*
 **    SCSI address of this device.
 **    The boot routines should have set it.
@@ -150,24 +172,6 @@ typedef u32 u_int32;
 #define SCSI_NCR_MYADDR      (7)
 #endif
 
-/*
-**    The maximal synchronous frequency in kHz.
-**    (0=asynchronous)
-*/
-
-#ifndef SCSI_NCR_MAX_SYNC
-#define SCSI_NCR_MAX_SYNC   (10000)
-#endif
-
-/*
-**    The maximal bus with (in log2 byte)
-**    (0=8 bit, 1=16 bit)
-*/
-
-#ifndef SCSI_NCR_MAX_WIDE
-#define SCSI_NCR_MAX_WIDE   (1)
-#endif
-
 /*
 **    The maximum number of tags per logic unit.
 **    Used only for disk devices that support tags.
@@ -177,13 +181,6 @@ typedef u32 u_int32;
 #define SCSI_NCR_MAX_TAGS    (4)
 #endif
 
-/*==========================================================
-**
-**      Configuration and Debugging
-**
-**==========================================================
-*/
-
 /*
 **    Number of targets supported by the driver.
 **    n permits target numbers 0..n-1.
@@ -209,7 +206,14 @@ typedef u32 u_int32;
 #else
 #define MAX_LUN    (1)
 #endif
+
+/*
+**    Asynchronous pre-scaler (ns). Shall be 40
+*/
  
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
 
 /*
 **    The maximum number of jobs scheduled for starting.
@@ -231,12 +235,11 @@ typedef u32 u_int32;
 #define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)
 
 /*
-**    The maximum transfer length (should be >= 64k).
-**    MUST NOT be greater than (MAX_SCATTER-1) * NBPG.
+**    Io mapped or memory mapped.
 */
 
-#if 0
-#define MAX_SIZE  ((MAX_SCATTER-1) * (long) NBPG)
+#if defined(SCSI_NCR_IOMAPPED)
+#define NCR_IOMAPPED
 #endif
 
 /*
@@ -245,10 +248,6 @@ typedef u32 u_int32;
 
 #define NCR_SNOOP_TIMEOUT (1000000)
 
-#if defined(SCSI_NCR_IOMAPPED) || defined(__alpha__)
-#define NCR_IOMAPPED
-#endif
-
 /*==========================================================
 **
 **     Defines for Linux.
@@ -287,7 +286,7 @@ typedef     int             vm_size_t;
 **     virtual memory addresses of the kernel data segment into
 **     IO bus adresses.
 **     On i386 architecture, IO bus addresses match the physical
-**     addresses. But on Alpha architecture they are different.
+**     addresses. But on other architectures they can be different.
 **     In the original Bsd driver, vtophys() is called to translate
 **     data addresses to IO bus addresses. In order to minimize
 **     change, I decide to define vtophys() as virt_to_bus().
@@ -299,27 +298,42 @@ typedef   int             vm_size_t;
 /*
 **     Memory mapped IO
 **
-**     Linux 1.3.X allow to remap physical pages addresses greater than
-**     the highest physical memory address to kernel virtual pages.
-**     We must use vremap() to map the page and vfree() to unmap it.
-**     The memory base of ncr chips is set by the bios at a high physical
-**     address. Also we can map it, and MMIO is possible.
+**     Since linux-2.1, we must use ioremap() to map the io memory space.
+**     iounmap() to unmap it. That allows portability.
+**     Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater 
+**     than the highest physical memory address to kernel virtual pages with 
+**     vremap() / vfree(). That was not portable but worked with i386 
+**     architecture.
 */
 
-static inline vm_offset_t remap_pci_mem(u_long base, u_long size)
+__initfunc(
+static vm_offset_t remap_pci_mem(u_long base, u_long size)
+)
 {
        u_long page_base        = ((u_long) base) & PAGE_MASK;
        u_long page_offs        = ((u_long) base) - page_base;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+       u_long page_remapped    = (u_long) ioremap(page_base, page_offs+size);
+#else
        u_long page_remapped    = (u_long) vremap(page_base, page_offs+size);
+#endif
 
        return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL);
 }
-static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+
+__initfunc(
+static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+)
 {
-       if (vaddr) vfree((void *) (vaddr & PAGE_MASK));
+       if (vaddr)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+               iounmap((void *) (vaddr & PAGE_MASK));
+#else
+               vfree((void *) (vaddr & PAGE_MASK));
+#endif
 }
 
-#else
+#else /* linux-1.2.13 */
 
 /*
 **     Linux 1.2.X assumes that addresses (virtual, physical, bus)
@@ -332,6 +346,10 @@ static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size)
 #define vtophys(p)     ((u_long) (p))
 #endif
 
+/*
+**     Insert a delay in micro-seconds.
+*/
+
 static void DELAY(long us)
 {
        for (;us>1000;us-=1000) udelay(1000);
@@ -348,31 +366,81 @@ static void DELAY(long us)
 **     I notice that kmalloc() returns NULL during host attach under
 **     Linux 1.2.13. But this ncr driver is reliable enough to
 **     accomodate with this joke.
-**/
+**
+**     kmalloc() only ensure 8 bytes boundary alignment.
+**     The NCR need better alignment for cache line bursting.
+**     The global header is moved betewen the NCB and CCBs and need 
+**     origin and destination addresses to have same lower four bits.
+**
+**     We use 32 boundary alignment for NCB and CCBs and offset multiple 
+**     of 32 for global header fields. That's too much but at least enough.
+*/
+
+#define ALIGN_SIZE(shift)      (1UL << shift)
+#define ALIGN_MASK(shift)      (~(ALIGN_SIZE(shift)-1))
 
-static inline void *m_alloc(int size)
+#define NCB_ALIGN_SHIFT                5
+#define CCB_ALIGN_SHIFT                5
+#define LCB_ALIGN_SHIFT                5
+#define SCR_ALIGN_SHIFT                5
+
+#define NCB_ALIGN_SIZE         ALIGN_SIZE(NCB_ALIGN_SHIFT)
+#define NCB_ALIGN_MASK         ALIGN_MASK(NCB_ALIGN_SHIFT)
+#define CCB_ALIGN_SIZE         ALIGN_SIZE(CCB_ALIGN_SHIFT)
+#define CCB_ALIGN_MASK         ALIGN_MASK(CCB_ALIGN_SHIFT)
+#define SCR_ALIGN_SIZE         ALIGN_SIZE(SCR_ALIGN_SHIFT)
+#define SCR_ALIGN_MASK         ALIGN_MASK(SCR_ALIGN_SHIFT)
+
+static void *m_alloc(int size, int a_shift)
 {
-       void *ptr = (void *) kmalloc(size, GFP_ATOMIC);
-       if (((unsigned long) ptr) & 3)
-               panic("ncr53c8xx: kmalloc returns misaligned address %lx\n", (unsigned long) ptr);
+       u_long addr;
+       void *ptr;
+       u_long a_size, a_mask;
+
+       if (a_shift < 3)
+               a_shift = 3;
+
+       a_size  = ALIGN_SIZE(a_shift);
+       a_mask  = ALIGN_MASK(a_shift);
+
+       ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC);
+       if (ptr) {
+               addr    = (((u_long) ptr) + a_size) & a_mask;
+               *((void **) (addr - sizeof(void *))) = ptr;
+               ptr     = (void *) addr;
+       }
+
        return ptr;
 }
 
-static inline void m_free(void *ptr, int size)
-       { kfree(ptr); }
+#ifdef MODULE
+static void m_free(void *ptr, int size)
+{
+       u_long addr;
+
+       if (ptr) {
+               addr    = (u_long) ptr;
+               ptr     = *((void **) (addr - sizeof(void *)));
+
+               kfree(ptr);
+       }
+}
+#endif
 
 /*
 **     Transfer direction
 **
-**     The middle scsi driver of Linux does not provide the transfer
-**     direction in the command structure.
-**     FreeBsd ncr driver require this information.
-**
-**     I spent some hours to read the scsi2 documentation to see if
-**     it was possible to deduce the direction of transfer from the opcode
-**     of the command. It seems that it's OK.
-**     guess_xfer_direction() seems to work. If it's wrong we will
-**     get a phase mismatch on some opcode.
+**     Low-level scsi drivers under Linux do not receive the expected 
+**     data transfer direction from upper scsi drivers.
+**     The driver will only check actual data direction for common 
+**     scsi opcodes. Other ones may cause problem, since they may 
+**     depend on device type or be vendor specific.
+**     I would prefer to never trust the device for data direction, 
+**     but that is not possible.
+**
+**     The original driver requires the expected direction to be known.
+**     The Linux version of the driver has been enhanced in order to 
+**     be able to transfer data in the direction choosen by the target. 
 */
 
 #define XferNone       0
@@ -385,6 +453,8 @@ 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.
 */
 
 static struct Scsi_Host                *first_host     = NULL;
@@ -419,6 +489,41 @@ static struct {
        unsigned char and_map[MAX_TARGET];
 } target_capabilities[SCSI_NCR_MAX_HOST] = { NCR53C8XX_TARGET_CAPABILITIES };
 
+/*
+**     Driver setup.
+**
+**     This structure is initialized from linux config options.
+**     It can be overridden at boot-up by the boot command line.
+*/
+struct ncr_driver_setup {
+       unsigned master_parity  : 1;
+       unsigned scsi_parity    : 1;
+       unsigned disconnection  : 1;
+       unsigned special_features : 1;
+       unsigned ultra_scsi     : 2;
+       unsigned force_sync_nego: 1;
+       unsigned reverse_probe: 1;
+       unsigned pci_fix_up: 4;
+       u_char  use_nvram;
+       u_char  verbose;
+       u_char  default_tags;
+       u_short default_sync;
+       u_short debug;
+       u_char  burst_max;
+       u_char  led_pin;
+       u_char  max_wide;
+       u_char  settle_delay;
+       u_char  diff_support;
+       u_char  irqm;
+};
+
+static struct ncr_driver_setup
+       driver_setup                    = SCSI_NCR_DRIVER_SETUP;
+
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+       driver_safe_setup __initdata    = SCSI_NCR_DRIVER_SAFE_SETUP;
+#endif
 
 /*
 **     Other Linux definitions
@@ -438,7 +543,149 @@ static void ncr53c8xx_intr(int irq, struct pt_regs * regs);
 
 static void ncr53c8xx_timeout(unsigned long np);
 
-#define bootverbose 1
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+**     Symbios NvRAM data format
+*/
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+       u_short start_marker;   /* 0x0000 */
+       u_short byte_count;     /* excluding header/trailer */
+       u_short checksum;
+
+/* Controller set up 20 bytes */
+       u_short word0;          /* 0x3000 */
+       u_short word2;          /* 0x0000 */
+       u_short word4;          /* 0x0000 */
+       u_short flags;
+#define SYMBIOS_SCAM_ENABLE    (1)
+#define SYMBIOS_PARITY_ENABLE  (1<<1)
+#define SYMBIOS_VERBOSE_MSGS   (1<<2)
+       u_short flags1;
+#define SYMBIOS_SCAN_HI_LO     (1)
+       u_short word10;         /* 0x00 */
+       u_short word12;         /* 0x00 */
+       u_char  host_id;
+       u_char  byte15;         /* 0x04 */
+       u_short word16;         /* 0x0410 */
+       u_short word18;         /* 0x0000 */
+
+/* Boot order 14 bytes * 4 */
+       struct Symbios_host{
+               u_char  word0;          /* 0x0004:ok / 0x0000:nok */
+               u_short device_id;      /* PCI device id */
+               u_short vendor_id;      /* PCI vendor id */
+               u_char  byte6;          /* 0x00 */
+               u_char  device_fn;      /* PCI device/function number << 3*/
+               u_short word8;
+               u_short flags;
+#define        SYMBIOS_INIT_SCAN_AT_BOOT       (1)
+               u_short io_port;        /* PCI io_port address */
+       } host[4];
+
+/* Targets 8 bytes * 16 */
+       struct Symbios_target {
+               u_short flags;
+#define SYMBIOS_DISCONNECT_ENABLE      (1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME      (1<<1)
+#define SYMBIOS_SCAN_LUNS              (1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED     (1<<3)
+               u_char  bus_width;      /* 0x08/0x10 */
+               u_char  sync_offset;
+               u_char  sync_period;    /* 4*period factor */
+               u_char  byte6;          /* 0x00 */
+               u_short timeout;
+       } target[16];
+       u_char  spare_devices[19*8];
+       u_char  trailer[6];             /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram   Symbios_nvram;
+typedef struct Symbios_host    Symbios_host;
+typedef struct Symbios_target  Symbios_target;
+
+/*
+**     Tekram NvRAM data format.
+*/
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_NVRAM_ADDRESS 0
+
+struct Tekram_nvram {
+       struct Tekram_target {
+               u_char  flags;
+#define        TEKRAM_PARITY_CHECK             (1)
+#define TEKRAM_SYNC_NEGO               (1<<1)
+#define TEKRAM_DISCONNECT_ENABLE       (1<<2)
+#define        TEKRAM_START_CMD                (1<<3)
+#define TEKRAM_TAGGED_COMMANDS         (1<<4)
+#define TEKRAM_WIDE_NEGO               (1<<5)
+               u_char  sync_index;
+               u_short word2;
+       } target[16];
+       u_char  host_id;
+       u_char  flags;
+#define TEKRAM_MORE_THAN_2_DRIVES      (1)
+#define TEKRAM_DRIVES_SUP_1GB          (1<<1)
+#define        TEKRAM_RESET_ON_POWER_ON        (1<<2)
+#define TEKRAM_ACTIVE_NEGATION         (1<<3)
+#define TEKRAM_IMMEDIATE_SEEK          (1<<4)
+#define        TEKRAM_SCAN_LUNS                (1<<5)
+#define        TEKRAM_REMOVABLE_FLAGS          (3<<6)  /* 0: disable; 1: boot device; 2:all */
+       u_char  boot_delay_index;
+       u_char  max_tags_index;
+       u_short flags1;
+#define TEKRAM_F2_F6_ENABLED           (1)
+       u_short spare[29];
+};
+typedef struct Tekram_nvram    Tekram_nvram;
+typedef struct Tekram_target   Tekram_target;
+
+static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*
+**     Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to 
+**     transmit device configuration to the ncr_attach() function.
+*/
+typedef struct {
+       int     bus;
+       u_char  device_fn;
+       u_int   base;
+       u_int   io_port;
+       int     irq;
+/* port and reg fields to use INB, OUTB macros */
+       u_int   port;
+       volatile struct ncr_reg *reg;
+} ncr_slot;
+
+typedef struct {
+       int type;
+#define        SCSI_NCR_SYMBIOS_NVRAM  (1)
+#define        SCSI_NCR_TEKRAM_NVRAM   (2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       union {
+               Symbios_nvram Symbios;
+               Tekram_nvram Tekram;
+       } data;
+#endif
+} ncr_nvram;
+
+/*
+**     Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
+**     to save data on each detected board for ncr_attach().
+*/
+typedef struct {
+       ncr_slot  slot;
+       ncr_chip  chip;
+       ncr_nvram *nvram;
+       int attached;
+} ncr_device;
 
 /*==========================================================
 **
@@ -466,7 +713,7 @@ static void ncr53c8xx_timeout(unsigned long np);
 **    Can be changed at runtime too.
 */
 
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
        #define DEBUG_FLAGS ncr_debug
 #else
        #define DEBUG_FLAGS     SCSI_NCR_DEBUG_FLAGS
@@ -499,49 +746,45 @@ static void ncr53c8xx_timeout(unsigned long np);
 **     Access to the controller chip.
 **
 **     If NCR_IOMAPPED is defined, only IO are used by the driver.
-**     Else, we begins initialisations by using MMIO.
-**             If cache test fails, we retry using IO mapped.
-**     The flag "use_mmio" in the ncb structure is set to 1 if
-**     mmio is possible.
 **
 **==========================================================
 */
 
 /*
-**     IO mapped input / ouput
+**     IO mapped only input / ouput
 */
 
-#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))
+#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))
 
-#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))
+#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))
 
 /*
 **     MEMORY mapped IO input / output
 */
 
-#define MMIO_INB(r)        readb(&np->reg_remapped->r)
-#define MMIO_INB_OFF(o)    readb((char *)np->reg_remapped + (o))
-#define MMIO_INW(r)        readw(&np->reg_remapped->r)
-#define MMIO_INL(r)        readl(&np->reg_remapped->r)
-#define MMIO_INL_OFF(o)    readl((char *)np->reg_remapped + (o))
+#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))
 
-#define MMIO_OUTB(r, val)     writeb((val), &np->reg_remapped->r)
-#define MMIO_OUTW(r, val)     writew((val), &np->reg_remapped->r)
-#define MMIO_OUTL(r, val)     writel((val), &np->reg_remapped->r)
-#define MMIO_OUTL_OFF(o, val) writel((val), (char *)np->reg_remapped + (o))
+#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))
 
 /*
-**     IO mapped only input / output
+**     IO mapped input / output
 */
 
-#ifdef NCR_IOMAPPED
+#if defined(NCR_IOMAPPED)
 
 #define INB(r)             IOM_INB(r)
 #define INB_OFF(o)         IOM_INB_OFF(o)
@@ -555,24 +798,35 @@ static void ncr53c8xx_timeout(unsigned long np);
 #define OUTL_OFF(o, val)   IOM_OUTL_OFF(o, val)
 
 /*
-**     IO mapped or MEMORY mapped depending on flag "use_mmio"
+**     MEMORY mapped only input / output
 */
 
 #else
 
-#define        INB(r)             (np->use_mmio ? MMIO_INB(r) : IOM_INB(r))
-#define        INB_OFF(o)         (np->use_mmio ? MMIO_INB_OFF(o) : IOM_INB_OFF(o))
-#define        INW(r)             (np->use_mmio ? MMIO_INW(r) : IOM_INW(r))
-#define        INL(r)             (np->use_mmio ? MMIO_INL(r) : IOM_INL(r))
-#define        INL_OFF(o)         (np->use_mmio ? MMIO_INL_OFF(o) : IOM_INL_OFF(o))
+#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        OUTB(r, val)       (np->use_mmio ? MMIO_OUTB(r, val) : IOM_OUTB(r, val))
-#define        OUTW(r, val)       (np->use_mmio ? MMIO_OUTW(r, val) : IOM_OUTW(r, val))
-#define        OUTL(r, val)       (np->use_mmio ? MMIO_OUTL(r, val) : IOM_OUTL(r, val))
-#define        OUTL_OFF(o, val)   (np->use_mmio ? MMIO_OUTL_OFF(o, val) : IOM_OUTL_OFF(o, val))
+#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)
 
 #endif
 
+/*
+**     Set bit field ON, OFF 
+*/
+
+#define OUTONB(r, m)   OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m)  OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m)   OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m)  OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m)   OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m)  OUTL(r, INL(r) & ~(m))
+
 /*==========================================================
 **
 **     Command control block states.
@@ -614,7 +868,9 @@ static void ncr53c8xx_timeout(unsigned long np);
 #define        SIR_REJECT_SENT         (10)
 #define        SIR_IGN_RESIDUE         (11)
 #define        SIR_MISSING_SAVE        (12)
-#define        SIR_MAX                 (12)
+#define        SIR_DATA_IO_IS_OUT      (13)
+#define        SIR_DATA_IO_IS_IN       (14)
+#define        SIR_MAX                 (14)
 
 /*==========================================================
 **
@@ -673,7 +929,6 @@ static void ncr53c8xx_timeout(unsigned long np);
 */
 
 #define CCB_MAGIC      (0xf2691ad2)
-#define        MAX_TAGS        (16)            /* hard limit */
 
 /*==========================================================
 **
@@ -713,7 +968,13 @@ struct     usrcmd {
 #define UC_SETFLAG     15
 #define UC_CLEARPROF   16
 
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+#define UC_DEBUG_ERROR_RECOVERY 17
+#endif
+
 #define        UF_TRACE        (0x01)
+#define        UF_NODISC       (0x02)
+#define        UF_NOSCAN       (0x04)
 
 /*---------------------------------------
 **
@@ -727,7 +988,6 @@ struct tstamp {
        u_long end;
        u_long select;
        u_long command;
-       u_long data;
        u_long status;
        u_long disconnect;
        u_long reselect;
@@ -811,6 +1071,14 @@ struct tcb {
 
        ccb_p   hold_cp;
 
+       /*
+       **      pointer to ccb used for negotiating.
+       **      Avoid to start a nego for all queued commands 
+       **      when tagged command queuing is enabled.
+       */
+
+       ccb_p   nego_cp;
+
        /*
        **      statistical data
        */
@@ -821,13 +1089,18 @@ struct tcb {
        /*
        **      user settable limits for sync transfer
        **      and tagged commands.
+       **      These limits are read from the NVRAM if present.
        */
 
        u_char  usrsync;
-       u_char  usrtags;
        u_char  usrwide;
+       u_char  usrtags;
        u_char  usrflag;
 
+       u_char  numtags;
+       u_char  maxtags;
+       u_short num_good;
+
        /*
        **      negotiation of wide and synch transfer.
        **      device quirks.
@@ -911,6 +1184,15 @@ struct lcb {
        u_char          usetags;
        u_char          lasttag;
 
+       /*
+       **      Linux specific fields:
+       **      Number of active commands and current credit.
+       **      Should be managed by the generic scsi driver
+       */
+
+       u_char          active;
+       u_char          opennings;
+
        /*-----------------------------------------------
        **      Flag to force M_ORDERED_TAG on next command
        **      in order to avoid spurious timeout when
@@ -960,9 +1242,9 @@ struct head {
        **      the last transfer command.
        */
 
-       u_long          savep;
-       u_long          lastp;
-       u_long          goalp;
+       u_int32         savep;
+       u_int32         lastp;
+       u_int32         goalp;
 
        /*
        **      The virtual address of the ccb
@@ -1092,6 +1374,14 @@ struct dsb {
 
 
 struct ccb {
+       /*
+       **      This field forces 32 bytes alignement for phys.header,
+       **      in order to use cache line bursting when copying it 
+       **      to the ncb.
+       */
+
+       struct link             filler[2];
+
        /*
        **      during reselection the ncr jumps to this point.
        **      If a "SIMPLE_TAG" message was received,
@@ -1199,6 +1489,14 @@ struct ccb {
        */
 
        u_char                  tag;
+
+       /*
+       **      Number of segments of the scatter list.
+       **      Used for recalculation of savep/goalp/lastp on 
+       **      SIR_DATA_IO_IS_OUT interrupt.
+       */
+       
+       u_char                  segments;
 };
 
 #define CCB_PHYS(cp,lbl)       (cp->p_ccb + offsetof(struct ccb, lbl))
@@ -1211,32 +1509,69 @@ struct ccb {
 */
 
 struct ncb {
+       /*
+       **      The global header.
+       **      Accessible to both the host and the
+       **      script-processor.
+       **      Is 32 bytes aligned since ncb is, in order to 
+       **      allow cache line bursting when copying it from or 
+       **      to ccbs.
+       */
+       struct head     header;
+
        /*-----------------------------------------------
        **      Specific Linux fields
        **-----------------------------------------------
        */
        int    unit;                    /* Unit number                       */
-       int    chip;                    /* Chip number                       */
+       char   chip_name[8];            /* Chip name                         */
+       char   inst_name[16];           /* Instance name                     */
        struct timer_list timer;        /* Timer link header                 */
        int     ncr_cache;              /* Cache test variable               */
-       int     release_stage;          /* Synchronisation stage on release  */
        Scsi_Cmnd *waiting_list;        /* Waiting list header for commands  */
                                        /* that we can't put into the squeue */
-#ifndef NCR_IOMAPPED
-       volatile struct ncr_reg*
-               reg_remapped;           /* Virtual address of the memory     */
-                                       /* base of the ncr chip              */
-       int     use_mmio;               /* Indicate mmio is OK               */
+       u_long  settle_time;            /* Reset in progess                  */
+       u_char  release_stage;          /* Synchronisation stage on release  */
+       u_char  verbose;                /* Boot verbosity for this controller*/
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+       u_char  debug_error_recovery;
+       u_char  stalling;
+       u_char  assert_atn;
 #endif
+
        /*-----------------------------------------------
        **      Added field to support differences
        **      between ncr chips.
+       **      sv_xxx are some io register bit value at start-up and
+       **      so assumed to have been set by the sdms bios.
+       **      rv_xxx are the bit fields of io register that will keep 
+       **      the features used by the driver.
        **-----------------------------------------------
        */
        u_short device_id;
        u_char  revision_id;
-#define ChipDevice     ((np)->device_id)
-#define ChipVersion    ((np)->revision_id & 0xf0)
+
+       u_char  sv_scntl0;
+       u_char  sv_scntl3;
+       u_char  sv_dmode;
+       u_char  sv_dcntl;
+       u_char  sv_ctest3;
+       u_char  sv_ctest4;
+       u_char  sv_ctest5;
+       u_char  sv_gpcntl;
+       u_char  sv_stest2;
+       u_char  sv_stest4;
+
+       u_char  rv_scntl0;
+       u_char  rv_scntl3;
+       u_char  rv_dmode;
+       u_char  rv_dcntl;
+       u_char  rv_ctest3;
+       u_char  rv_ctest4;
+       u_char  rv_ctest5;
+       u_char  rv_stest2;
+
+       u_char  scsi_mode;
 
        /*-----------------------------------------------
        **      Scripts ..
@@ -1262,6 +1597,9 @@ struct ncb {
        vm_offset_t     vaddr;
        vm_offset_t     paddr;
 
+       vm_offset_t     vaddr2;
+       vm_offset_t     paddr2;
+
        /*
        **      pointer to the chip's registers.
        */
@@ -1269,26 +1607,44 @@ struct ncb {
        struct ncr_reg* reg;
 
        /*
-       **      A copy of the script, relocated for this ncb.
+       **      A copy of the scripts, relocated for this ncb.
+       */
+       struct script   *script0;
+       struct scripth  *scripth0;
+
+       /*
+       **      Scripts instance virtual address.
        */
        struct script   *script;
+       struct scripth  *scripth;
 
        /*
-       **      Physical address of this instance of ncb->script
+       **      Scripts instance physical address.
        */
        u_long          p_script;
+       u_long          p_scripth;
 
        /*
        **      The SCSI address of the host adapter.
        */
        u_char    myaddr;
 
+       /*
+       **      Max dwords burst supported by the adapter.
+       */
+       u_char          maxburst;       /* log base 2 of dwords burst   */
+
        /*
        **      timing parameters
        */
-       u_char          ns_async;
-       u_char          ns_sync;
-       u_char          rv_scntl3;
+       u_char          minsync;        /* Minimum sync period factor   */
+       u_char          maxsync;        /* Maximum sync period factor   */
+       u_char          maxoffs;        /* Max scsi offset              */
+       u_char          multiplier;     /* Clock multiplier (1,2,4)     */
+       u_char          clock_divn;     /* Number of clock divisors     */
+       u_long          clock_khz;      /* SCSI clock frequency in KHz  */
+       u_int           features;       /* Chip features map            */
+
 
        /*-----------------------------------------------
        **      Link to the generic SCSI driver
@@ -1314,7 +1670,7 @@ struct ncb {
        /*
        **      Start queue.
        */
-       u_long          squeue [MAX_START];
+       u_int32         squeue [MAX_START];
        u_short         squeueput;
        u_short         actccbs;
 
@@ -1342,20 +1698,13 @@ struct ncb {
        u_long          disc_phys;
        u_long          disc_ref;
 
-       /*
-       **      The global header.
-       **      Accessible to both the host and the
-       **      script-processor.
-       */
-       struct head     header;
-
        /*
        **      The global control block.
        **      It's used only during the configuration phase.
        **      A target control block will be created
        **      after the first successful transfer.
        */
-       struct ccb      ccb;
+       struct ccb      *ccb;
 
        /*
        **      message buffers.
@@ -1365,7 +1714,7 @@ struct ncb {
        */
        u_char          msgout[8];
        u_char          msgin [8];
-       u_long          lastmsg;
+       u_int32         lastmsg;
 
        /*
        **      Buffer for STATUS_IN phase.
@@ -1393,7 +1742,8 @@ struct ncb {
        u_short         irq;
 };
 
-#define NCB_SCRIPT_PHYS(np,lbl)        (np->p_script + offsetof (struct script, lbl))
+#define NCB_SCRIPT_PHYS(np,lbl)         (np->p_script  + offsetof (struct script, lbl))
+#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth, lbl))
 
 /*==========================================================
 **
@@ -1416,12 +1766,15 @@ struct ncb {
 **----------------------------------------------------------
 */
 
+/*
+**     Script fragments which are loaded into the on-board RAM 
+**     of 825A, 875 and 895 chips.
+*/
 struct script {
        ncrcmd  start           [  7];
        ncrcmd  start0          [  2];
        ncrcmd  start1          [  3];
        ncrcmd  startpos        [  1];
-       ncrcmd  tryloop         [MAX_START*5+2];
        ncrcmd  trysel          [  8];
        ncrcmd  skip            [  8];
        ncrcmd  skip2           [  3];
@@ -1439,14 +1792,6 @@ struct script {
        ncrcmd  status          [ 27];
        ncrcmd  msg_in          [ 26];
        ncrcmd  msg_bad         [  6];
-       ncrcmd  msg_parity      [  6];
-       ncrcmd  msg_reject      [  8];
-       ncrcmd  msg_ign_residue [ 32];
-       ncrcmd  msg_extended    [ 18];
-       ncrcmd  msg_ext_2       [ 18];
-       ncrcmd  msg_wdtr        [ 27];
-       ncrcmd  msg_ext_3       [ 18];
-       ncrcmd  msg_sdtr        [ 27];
        ncrcmd  complete        [ 13];
        ncrcmd  cleanup         [ 12];
        ncrcmd  cleanup0        [ 11];
@@ -1458,6 +1803,30 @@ struct script {
        ncrcmd  disconnect1     [ 23];
        ncrcmd  msg_out         [  9];
        ncrcmd  msg_out_done    [  7];
+       ncrcmd  badgetcc        [  6];
+       ncrcmd  reselect        [  8];
+       ncrcmd  reselect1       [  8];
+       ncrcmd  reselect2       [  8];
+       ncrcmd  resel_tmp       [  5];
+       ncrcmd  resel_lun       [ 18];
+       ncrcmd  resel_tag       [ 24];
+       ncrcmd  data_io         [  6];
+       ncrcmd  data_in         [MAX_SCATTER * 4 + 4];
+};
+
+/*
+**     Script fragments which stay in main memory for all chips.
+*/
+struct scripth {
+       ncrcmd  tryloop         [MAX_START*5+2];
+       ncrcmd  msg_parity      [  6];
+       ncrcmd  msg_reject      [  8];
+       ncrcmd  msg_ign_residue [ 32];
+       ncrcmd  msg_extended    [ 18];
+       ncrcmd  msg_ext_2       [ 18];
+       ncrcmd  msg_wdtr        [ 27];
+       ncrcmd  msg_ext_3       [ 18];
+       ncrcmd  msg_sdtr        [ 27];
        ncrcmd  msg_out_abort   [ 10];
        ncrcmd  getcc           [  4];
        ncrcmd  getcc1          [  5];
@@ -1467,14 +1836,7 @@ struct script {
        ncrcmd  getcc2          [ 14];
 #endif
        ncrcmd  getcc3          [ 10];
-       ncrcmd  badgetcc        [  6];
-       ncrcmd  reselect        [ 12];
-       ncrcmd  reselect2       [  6];
-       ncrcmd  resel_tmp       [  5];
-       ncrcmd  resel_lun       [ 18];
-       ncrcmd  resel_tag       [ 24];
-       ncrcmd  data_in         [MAX_SCATTER * 4 + 7];
-       ncrcmd  data_out        [MAX_SCATTER * 4 + 7];
+       ncrcmd  data_out        [MAX_SCATTER * 4 + 4];
        ncrcmd  aborttag        [  4];
        ncrcmd  abort           [ 22];
        ncrcmd  snooptest       [  9];
@@ -1493,51 +1855,59 @@ struct script {
 static void    ncr_alloc_ccb   (ncb_p np, u_long t, u_long l);
 static void    ncr_complete    (ncb_p np, ccb_p cp);
 static void    ncr_exception   (ncb_p np);
-static void    ncr_free_ccb    (ncb_p np, ccb_p cp);
-static void    ncr_getclock    (ncb_p np, u_char scntl3);
+static void    ncr_free_ccb    (ncb_p np, ccb_p cp, u_long t, u_long l);
+static void    ncr_getclock    (ncb_p np, int mult);
+static void    ncr_selectclock (ncb_p np, u_char scntl3);
 static ccb_p   ncr_get_ccb     (ncb_p np, u_long t,u_long l);
 static void    ncr_init        (ncb_p np, char * msg, u_long code);
-static int     ncr_intr        (ncb_p np);
+static int     ncr_int_sbmc    (ncb_p np);
+static int     ncr_int_par     (ncb_p np);
 static void    ncr_int_ma      (ncb_p np);
 static void    ncr_int_sir     (ncb_p np);
 static  void    ncr_int_sto     (ncb_p np);
 static u_long  ncr_lookup      (char* id);
 static void    ncr_negotiate   (struct ncb* np, struct tcb* tp);
+static void    ncr_opennings   (ncb_p np, lcb_p lp, Scsi_Cmnd * xp);
 
-#ifdef SCSI_NCR_PROFILE
-static int     ncr_delta       (u_long from, u_long to);
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 static void    ncb_profile     (ncb_p np, ccb_p cp);
 #endif
 
 static void    ncr_script_copy_and_bind
-                               (struct script * script, ncb_p np);
-static  void    ncr_script_fill (struct script * scr);
+                               (ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
+static  void    ncr_script_fill (struct script * scr, struct scripth * scripth);
 static int     ncr_scatter     (ccb_p cp, Scsi_Cmnd *cmd);
-static void    ncr_setmaxtags  (ncb_p np, tcb_p tp, u_long usrtags);
-static void    ncr_setsync     (ncb_p np, ccb_p cp, u_char sxfer);
+static void    ncr_setmaxtags  (ncb_p np, tcb_p tp, u_long numtags);
+static void    ncr_getsync     (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
+static void    ncr_setsync     (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
 static void    ncr_settags     (tcb_p tp, lcb_p lp);
-static void    ncr_setwide     (ncb_p np, ccb_p cp, u_char wide);
+static void    ncr_setwide     (ncb_p np, ccb_p cp, u_char wide, u_char ack);
 static int     ncr_show_msg    (u_char * msg);
 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);
 
-#ifdef SCSI_NCR_USER_COMMAND
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
 static void    ncr_usercmd     (ncb_p np);
 #endif
 
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, u_short device_id,
-                      u_char revision_id, int chip, u_int base, u_int io_port, 
-                      int irq, int bus, u_char device_fn);
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
 
 static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
-static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
 static void process_waiting_list(ncb_p np, int sts);
 
+#define remove_from_waiting_list(np, cmd) \
+               retrieve_from_waiting_list(1, (np), (cmd))
 #define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
-#define abort_waiting_list(np) process_waiting_list((np), DID_ABORT)
 #define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
 
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static int     ncr_get_Symbios_nvram   (ncr_slot *np, Symbios_nvram *nvram);
+static int     ncr_get_Tekram_nvram    (ncr_slot *np, Tekram_nvram *nvram);
+#endif
+
 /*==========================================================
 **
 **
@@ -1557,24 +1927,13 @@ static u_long   ncr_version = NCR_VERSION       * 11
        + (u_long) sizeof (struct tcb)  *  2;
 #endif
 
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
 static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
 #endif
 
-/*==========================================================
-**
-**
-**      Global static data:    auto configure
-**
-**
-**==========================================================
-*/
-
-static char *ncr_name (ncb_p np)
+static inline char *ncr_name (ncb_p np)
 {
-       static char name[10];
-       sprintf(name, "ncr53c%d-%d", np->chip, np->unit);
-       return (name);
+       return np->inst_name;
 }
 
 
@@ -1601,10 +1960,12 @@ static char *ncr_name (ncb_p np)
 #define        RELOC_LABEL     0x50000000
 #define        RELOC_REGISTER  0x60000000
 #define        RELOC_KVAR      0x70000000
+#define        RELOC_LABELH    0x80000000
 #define        RELOC_MASK      0xf0000000
 
 #define        NADDR(label)    (RELOC_SOFTC | offsetof(struct ncb, label))
 #define PADDR(label)    (RELOC_LABEL | offsetof(struct script, label))
+#define PADDRH(label)   (RELOC_LABELH | offsetof(struct scripth, label))
 #define        RADDR(label)    (RELOC_REGISTER | REG(label))
 #define        FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
 #define        KVAR(which)     (RELOC_KVAR | (which))
@@ -1618,10 +1979,10 @@ static char *ncr_name (ncb_p np)
  * Kernel variables referenced in the scripts.
  * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
  */
-static void *script_kvars[] =
+static void *script_kvars[] __initdata =
        { (void *)&jiffies };
 
-static struct script script0 = {
+static struct script script0 __initdata = {
 /*--------------------------< START >-----------------------*/ {
        /*
        **      Claim to be still alive ...
@@ -1661,33 +2022,7 @@ static   struct script script0 = {
        */
        SCR_JUMP,
 }/*-------------------------< STARTPOS >--------------------*/,{
-               PADDR(tryloop),
-}/*-------------------------< TRYLOOP >---------------------*/,{
-/*
-**     Load an entry of the start queue into dsa
-**     and try to start it by jumping to TRYSEL.
-**
-**     Because the size depends on the
-**     #define MAX_START parameter, it is filled
-**     in at runtime.
-**
-**-----------------------------------------------------------
-**
-**  ##===========< I=0; i<MAX_START >===========
-**  || SCR_COPY (4),
-**  ||         NADDR (squeue[i]),
-**  ||         RADDR (dsa),
-**  || SCR_CALL,
-**  ||         PADDR (trysel),
-**  ##==========================================
-**
-**     SCR_JUMP,
-**             PADDR(tryloop),
-**
-**-----------------------------------------------------------
-*/
-0
-
+               PADDRH(tryloop),
 }/*-------------------------< TRYSEL >----------------------*/,{
        /*
        **      Now:
@@ -1742,7 +2077,7 @@ static    struct script script0 = {
        **      patch the launch field.
        **      should look like an idle process.
        */
-       SCR_COPY (4),
+       SCR_COPY_F (4),
                RADDR (dsa),
                PADDR (skip2),
        SCR_COPY (8),
@@ -1848,7 +2183,7 @@ static    struct script script0 = {
        **      We patch the address part of a
        **      COPY command with the DSA-register.
        */
-       SCR_COPY (4),
+       SCR_COPY_F (4),
                RADDR (dsa),
                PADDR (loadpos),
        /*
@@ -1925,7 +2260,7 @@ static    struct script script0 = {
        SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
                PADDR (msg_in),
        SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
-               PADDR (msg_reject),
+               PADDRH (msg_reject),
        /*
        **      normal processing
        */
@@ -2142,7 +2477,7 @@ static    struct script script0 = {
        SCR_FROM_REG (socl),
                0,
        SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-               PADDR (msg_parity),
+               PADDRH (msg_parity),
        SCR_FROM_REG (scratcha),
                0,
        /*
@@ -2157,13 +2492,13 @@ static  struct script script0 = {
        SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
                PADDR (disconnect),
        SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
-               PADDR (msg_extended),
+               PADDRH (msg_extended),
        SCR_JUMP ^ IFTRUE (DATA (M_NOOP)),
                PADDR (clrack),
        SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
-               PADDR (msg_reject),
+               PADDRH (msg_reject),
        SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)),
-               PADDR (msg_ign_residue),
+               PADDRH (msg_ign_residue),
        /*
        **      Rest of the messages left as
        **      an exercise ...
@@ -2182,33 +2517,527 @@ static struct script script0 = {
        SCR_JUMP,
                PADDR (setmsg),
 
-}/*-------------------------< MSG_PARITY >---------------*/,{
+}/*-------------------------< COMPLETE >-----------------*/,{
        /*
-       **      count it
+       **      Complete message.
+       **
+       **      If it's not the get condition code,
+       **      copy TEMP register to LASTP in header.
        */
-       SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+       SCR_FROM_REG (SS_REG),
                0,
-       /*
-       **      send a "message parity error" message.
+/*<<<*/        SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)),
+               12,
+       SCR_COPY (4),
+               RADDR (temp),
+               NADDR (header.lastp),
+/*>>>*/        /*
+       **      When we terminate the cycle by clearing ACK,
+       **      the target may disconnect immediately.
+       **
+       **      We don't want to be told of an
+       **      "unexpected disconnect",
+       **      so we disable this feature.
        */
-       SCR_LOAD_REG (scratcha, M_PARITY),
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
                0,
-       SCR_JUMP,
-               PADDR (setmsg),
-}/*-------------------------< MSG_REJECT >---------------*/,{
        /*
-       **      If a negotiation was in progress,
-       **      negotiation failed.
+       **      Terminate cycle ...
        */
-       SCR_FROM_REG (HS_REG),
+       SCR_CLR (SCR_ACK|SCR_ATN),
                0,
-       SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
-               SIR_NEGO_FAILED,
        /*
-       **      else make host log this message
+       **      ... and wait for the disconnect.
        */
-       SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
-               SIR_REJECT_RECEIVED,
+       SCR_WAIT_DISC,
+               0,
+}/*-------------------------< CLEANUP >-------------------*/,{
+       /*
+       **      dsa:    Pointer to ccb
+       **            or xxxxxxFF (no ccb)
+       **
+       **      HS_REG:   Host-Status (<>0!)
+       */
+       SCR_FROM_REG (dsa),
+               0,
+       SCR_JUMP ^ IFTRUE (DATA (0xff)),
+               PADDR (signal),
+       /*
+       **      dsa is valid.
+       **      save the status registers
+       */
+       SCR_COPY (4),
+               RADDR (scr0),
+               NADDR (header.status),
+       /*
+       **      and copy back the header to the ccb.
+       */
+       SCR_COPY_F (4),
+               RADDR (dsa),
+               PADDR (cleanup0),
+       SCR_COPY (sizeof (struct head)),
+               NADDR (header),
+}/*-------------------------< CLEANUP0 >--------------------*/,{
+               0,
+
+       /*
+       **      If command resulted in "check condition"
+       **      status and is not yet completed,
+       **      try to get the condition code.
+       */
+       SCR_FROM_REG (HS_REG),
+               0,
+/*<<<*/        SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
+               16,
+       SCR_FROM_REG (SS_REG),
+               0,
+       SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
+               PADDRH(getcc2),
+       /*
+       **      And make the DSA register invalid.
+       */
+/*>>>*/        SCR_LOAD_REG (dsa, 0xff), /* invalid */
+               0,
+}/*-------------------------< SIGNAL >----------------------*/,{
+       /*
+       **      if status = queue full,
+       **      reinsert in startqueue and stall queue.
+       */
+       SCR_FROM_REG (SS_REG),
+               0,
+       SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
+               SIR_STALL_QUEUE,
+       /*
+       **      if job completed ...
+       */
+       SCR_FROM_REG (HS_REG),
+               0,
+       /*
+       **      ... signal completion to the host
+       */
+       SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)),
+               0,
+       /*
+       **      Auf zu neuen Schandtaten!
+       */
+       SCR_JUMP,
+               PADDR(start),
+
+}/*-------------------------< SAVE_DP >------------------*/,{
+       /*
+       **      SAVE_DP message:
+       **      Copy TEMP register to SAVEP in header.
+       */
+       SCR_COPY (4),
+               RADDR (temp),
+               NADDR (header.savep),
+       SCR_JUMP,
+               PADDR (clrack),
+}/*-------------------------< RESTORE_DP >---------------*/,{
+       /*
+       **      RESTORE_DP message:
+       **      Copy SAVEP in header to TEMP register.
+       */
+       SCR_COPY (4),
+               NADDR (header.savep),
+               RADDR (temp),
+       SCR_JUMP,
+               PADDR (clrack),
+
+}/*-------------------------< DISCONNECT >---------------*/,{
+       /*
+       **      If QUIRK_AUTOSAVE is set,
+       **      do an "save pointer" operation.
+       */
+       SCR_FROM_REG (QU_REG),
+               0,
+/*<<<*/        SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
+               12,
+       /*
+       **      like SAVE_DP message:
+       **      Copy TEMP register to SAVEP in header.
+       */
+       SCR_COPY (4),
+               RADDR (temp),
+               NADDR (header.savep),
+/*>>>*/        /*
+       **      Check if temp==savep or temp==goalp:
+       **      if not, log a missing save pointer message.
+       **      In fact, it's a comparison mod 256.
+       **
+       **      Hmmm, I hadn't thought that I would be urged to
+       **      write this kind of ugly self modifying code.
+       **
+       **      It's unbelievable, but the ncr53c8xx isn't able
+       **      to subtract one register from another.
+       */
+       SCR_FROM_REG (temp),
+               0,
+       /*
+       **      You are not expected to understand this ..
+       **
+       **      CAUTION: only little endian architectures supported! XXX
+       */
+       SCR_COPY_F (1),
+               NADDR (header.savep),
+               PADDR (disconnect0),
+}/*-------------------------< DISCONNECT0 >--------------*/,{
+/*<<<*/        SCR_JUMPR ^ IFTRUE (DATA (1)),
+               20,
+       /*
+       **      neither this
+       */
+       SCR_COPY_F (1),
+               NADDR (header.goalp),
+               PADDR (disconnect1),
+}/*-------------------------< DISCONNECT1 >--------------*/,{
+       SCR_INT ^ IFFALSE (DATA (1)),
+               SIR_MISSING_SAVE,
+/*>>>*/
+
+       /*
+       **      DISCONNECTing  ...
+       **
+       **      disable the "unexpected disconnect" feature,
+       **      and remove the ACK signal.
+       */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       /*
+       **      Wait for the disconnect.
+       */
+       SCR_WAIT_DISC,
+               0,
+       /*
+       **      Profiling:
+       **      Set a time stamp,
+       **      and count the disconnects.
+       */
+       SCR_COPY (sizeof (u_long)),
+               KVAR(SCRIPT_KVAR_JIFFIES),
+               NADDR (header.stamp.disconnect),
+       SCR_COPY (4),
+               NADDR (disc_phys),
+               RADDR (temp),
+       SCR_REG_REG (temp, SCR_ADD, 0x01),
+               0,
+       SCR_COPY (4),
+               RADDR (temp),
+               NADDR (disc_phys),
+       /*
+       **      Status is: DISCONNECTED.
+       */
+       SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+               0,
+       SCR_JUMP,
+               PADDR (cleanup),
+
+}/*-------------------------< MSG_OUT >-------------------*/,{
+       /*
+       **      The target requests a message.
+       */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+               NADDR (msgout),
+       SCR_COPY (1),
+               RADDR (sfbr),
+               NADDR (lastmsg),
+       /*
+       **      If it was no ABORT message ...
+       */
+       SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
+               PADDRH (msg_out_abort),
+       /*
+       **      ... wait for the next phase
+       **      if it's a message out, send it again, ...
+       */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+               PADDR (msg_out),
+}/*-------------------------< MSG_OUT_DONE >--------------*/,{
+       /*
+       **      ... else clear the message ...
+       */
+       SCR_LOAD_REG (scratcha, M_NOOP),
+               0,
+       SCR_COPY (4),
+               RADDR (scratcha),
+               NADDR (msgout),
+       /*
+       **      ... and process the next phase
+       */
+       SCR_JUMP,
+               PADDR (dispatch),
+}/*------------------------< BADGETCC >---------------------*/,{
+       /*
+       **      If SIGP was set, clear it and try again.
+       */
+       SCR_FROM_REG (ctest2),
+               0,
+       SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+               PADDRH (getcc2),
+       SCR_INT,
+               SIR_SENSE_FAILED,
+}/*-------------------------< RESELECT >--------------------*/,{
+       /*
+       **      This NOP will be patched with LED OFF
+       **      SCR_REG_REG (gpreg, SCR_OR, 0x01)
+       */
+       SCR_NO_OP,
+               0,
+       /*
+       **      make the DSA invalid.
+       */
+       SCR_LOAD_REG (dsa, 0xff),
+               0,
+       SCR_CLR (SCR_TRG),
+               0,
+       /*
+       **      Sleep waiting for a reselection.
+       **      If SIGP is set, special treatment.
+       **
+       **      Zu allem bereit ..
+       */
+       SCR_WAIT_RESEL,
+               PADDR(reselect2),
+}/*-------------------------< RESELECT1 >--------------------*/,{
+       /*
+       **      This NOP will be patched with LED ON
+       **      SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+       */
+       SCR_NO_OP,
+               0,
+       /*
+       **      ... zu nichts zu gebrauchen ?
+       **
+       **      load the target id into the SFBR
+       **      and jump to the control block.
+       **
+       **      Look at the declarations of
+       **      - struct ncb
+       **      - struct tcb
+       **      - struct lcb
+       **      - struct ccb
+       **      to understand what's going on.
+       */
+       SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+               0,
+       SCR_TO_REG (ctest0),
+               0,
+       SCR_JUMP,
+               NADDR (jump_tcb),
+}/*-------------------------< RESELECT2 >-------------------*/,{
+       /*
+       **      This NOP will be patched with LED ON
+       **      SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+       */
+       SCR_NO_OP,
+               0,
+       /*
+       **      If it's not connected :(
+       **      -> interrupted by SIGP bit.
+       **      Jump to start.
+       */
+       SCR_FROM_REG (ctest2),
+               0,
+       SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+               PADDR (start),
+       SCR_JUMP,
+               PADDR (reselect),
+
+}/*-------------------------< RESEL_TMP >-------------------*/,{
+       /*
+       **      The return address in TEMP
+       **      is in fact the data structure address,
+       **      so copy it to the DSA register.
+       */
+       SCR_COPY (4),
+               RADDR (temp),
+               RADDR (dsa),
+       SCR_JUMP,
+               PADDR (prepare),
+
+}/*-------------------------< RESEL_LUN >-------------------*/,{
+       /*
+       **      come back to this point
+       **      to get an IDENTIFY message
+       **      Wait for a msg_in phase.
+       */
+/*<<<*/        SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               48,
+       /*
+       **      message phase
+       **      It's not a sony, it's a trick:
+       **      read the data without acknowledging it.
+       */
+       SCR_FROM_REG (sbdl),
+               0,
+/*<<<*/        SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
+               32,
+       /*
+       **      It WAS an Identify message.
+       **      get it and ack it!
+       */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               NADDR (msgin),
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+       **      Mask out the lun.
+       */
+       SCR_REG_REG (sfbr, SCR_AND, 0x07),
+               0,
+       SCR_RETURN,
+               0,
+       /*
+       **      No message phase or no IDENTIFY message:
+       **      return 0.
+       */
+/*>>>*/        SCR_LOAD_SFBR (0),
+               0,
+       SCR_RETURN,
+               0,
+
+}/*-------------------------< RESEL_TAG >-------------------*/,{
+       /*
+       **      come back to this point
+       **      to get a SIMPLE_TAG message
+       **      Wait for a MSG_IN phase.
+       */
+/*<<<*/        SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               64,
+       /*
+       **      message phase
+       **      It's a trick - read the data
+       **      without acknowledging it.
+       */
+       SCR_FROM_REG (sbdl),
+               0,
+/*<<<*/        SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
+               48,
+       /*
+       **      It WAS a SIMPLE_TAG message.
+       **      get it and ack it!
+       */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               NADDR (msgin),
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+       **      Wait for the second byte (the tag)
+       */
+/*<<<*/        SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               24,
+       /*
+       **      Get it and ack it!
+       */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               NADDR (msgin),
+       SCR_CLR (SCR_ACK|SCR_CARRY),
+               0,
+       SCR_RETURN,
+               0,
+       /*
+       **      No message phase or no SIMPLE_TAG message
+       **      or no second byte: return 0.
+       */
+/*>>>*/        SCR_LOAD_SFBR (0),
+               0,
+       SCR_SET (SCR_CARRY),
+               0,
+       SCR_RETURN,
+               0,
+
+}/*-------------------------< DATA_IO >--------------------*/,{
+/*
+**     Because Linux does not provide xfer data direction 
+**     to low-level scsi drivers, we must trust the target 
+**     for actual data direction when we cannot guess it.
+**     The programmed interrupt patches savep, lastp, goalp,
+**     etc.., and restarts the scsi script at data_out/in.
+*/
+       SCR_INT ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+               SIR_DATA_IO_IS_OUT,
+       SCR_INT ^ IFTRUE (WHEN (SCR_DATA_IN)),
+               SIR_DATA_IO_IS_IN,
+       SCR_JUMP,
+               PADDR (no_data),
+
+}/*-------------------------< DATA_IN >--------------------*/,{
+/*
+**     Because the size depends on the
+**     #define MAX_SCATTER parameter,
+**     it is filled in at runtime.
+**
+**  ##===========< i=0; i<MAX_SCATTER >=========
+**  || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+**  ||         PADDR (checkatn),
+**  || SCR_MOVE_TBL ^ SCR_DATA_IN,
+**  ||         offsetof (struct dsb, data[ i]),
+**  ##==========================================
+**
+**     SCR_CALL,
+**             PADDR (checkatn),
+**     SCR_JUMP,
+**             PADDR (no_data),
+*/
+0
+}/*--------------------------------------------------------*/
+};
+
+static struct scripth scripth0 __initdata = {
+/*-------------------------< TRYLOOP >---------------------*/{
+/*
+**     Load an entry of the start queue into dsa
+**     and try to start it by jumping to TRYSEL.
+**
+**     Because the size depends on the
+**     #define MAX_START parameter, it is filled
+**     in at runtime.
+**
+**-----------------------------------------------------------
+**
+**  ##===========< I=0; i<MAX_START >===========
+**  || SCR_COPY (4),
+**  ||         NADDR (squeue[i]),
+**  ||         RADDR (dsa),
+**  || SCR_CALL,
+**  ||         PADDR (trysel),
+**  ##==========================================
+**
+**     SCR_JUMP,
+**             PADDRH(tryloop),
+**
+**-----------------------------------------------------------
+*/
+0
+},/*-------------------------< MSG_PARITY >---------------*/{
+       /*
+       **      count it
+       */
+       SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+               0,
+       /*
+       **      send a "message parity error" message.
+       */
+       SCR_LOAD_REG (scratcha, M_PARITY),
+               0,
+       SCR_JUMP,
+               PADDR (setmsg),
+}/*-------------------------< MSG_REJECT >---------------*/,{
+       /*
+       **      If a negotiation was in progress,
+       **      negotiation failed.
+       */
+       SCR_FROM_REG (HS_REG),
+               0,
+       SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+               SIR_NEGO_FAILED,
+       /*
+       **      else make host log this message
+       */
+       SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
+               SIR_REJECT_RECEIVED,
        SCR_JUMP,
                PADDR (clrack),
 
@@ -2233,7 +3062,7 @@ static    struct script script0 = {
        SCR_FROM_REG (socl),
                0,
        SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-               PADDR (msg_parity),
+               PADDRH (msg_parity),
        SCR_FROM_REG (scratcha),
                0,
        /*
@@ -2292,13 +3121,13 @@ static  struct script script0 = {
        SCR_FROM_REG (socl),
                0,
        SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-               PADDR (msg_parity),
+               PADDRH (msg_parity),
        SCR_FROM_REG (scratcha),
                0,
        /*
        */
        SCR_JUMP ^ IFTRUE (DATA (3)),
-               PADDR (msg_ext_3),
+               PADDRH (msg_ext_3),
        SCR_JUMP ^ IFFALSE (DATA (2)),
                PADDR (msg_bad),
 }/*-------------------------< MSG_EXT_2 >----------------*/,{
@@ -2319,11 +3148,11 @@ static  struct script script0 = {
        SCR_FROM_REG (socl),
                0,
        SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-               PADDR (msg_parity),
+               PADDRH (msg_parity),
        SCR_FROM_REG (scratcha),
                0,
        SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
-               PADDR (msg_wdtr),
+               PADDRH (msg_wdtr),
        /*
        **      unknown extended message
        */
@@ -2342,7 +3171,7 @@ static    struct script script0 = {
        SCR_FROM_REG (socl),
                0,
        SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-               PADDR (msg_parity),
+               PADDRH (msg_parity),
        /*
        **      let the host do the real work.
        */
@@ -2374,325 +3203,74 @@ static struct script script0 = {
 }/*-------------------------< MSG_EXT_3 >----------------*/,{
        SCR_CLR (SCR_ACK),
                0,
-       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-               PADDR (dispatch),
-       /*
-       **      get extended message code.
-       */
-       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-               NADDR (msgin[2]),
-       /*
-       **      Check for message parity error.
-       */
-       SCR_TO_REG (scratcha),
-               0,
-       SCR_FROM_REG (socl),
-               0,
-       SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-               PADDR (msg_parity),
-       SCR_FROM_REG (scratcha),
-               0,
-       SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
-               PADDR (msg_sdtr),
-       /*
-       **      unknown extended message
-       */
-       SCR_JUMP,
-               PADDR (msg_bad)
-
-}/*-------------------------< MSG_SDTR >-----------------*/,{
-       SCR_CLR (SCR_ACK),
-               0,
-       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-               PADDR (dispatch),
-       /*
-       **      get period and offset
-       */
-       SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
-               NADDR (msgin[3]),
-       SCR_FROM_REG (socl),
-               0,
-       SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-               PADDR (msg_parity),
-       /*
-       **      let the host do the real work.
-       */
-       SCR_INT,
-               SIR_NEGO_SYNC,
-       /*
-       **      let the target fetch our answer.
-       */
-       SCR_SET (SCR_ATN),
-               0,
-       SCR_CLR (SCR_ACK),
-               0,
-
-       SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
-               SIR_NEGO_PROTO,
-       /*
-       **      Send the M_X_SYNC_REQ
-       */
-       SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
-               NADDR (msgout),
-       SCR_CLR (SCR_ATN),
-               0,
-       SCR_COPY (1),
-               RADDR (sfbr),
-               NADDR (lastmsg),
-       SCR_JUMP,
-               PADDR (msg_out_done),
-
-}/*-------------------------< COMPLETE >-----------------*/,{
-       /*
-       **      Complete message.
-       **
-       **      If it's not the get condition code,
-       **      copy TEMP register to LASTP in header.
-       */
-       SCR_FROM_REG (SS_REG),
-               0,
-/*<<<*/        SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)),
-               12,
-       SCR_COPY (4),
-               RADDR (temp),
-               NADDR (header.lastp),
-/*>>>*/        /*
-       **      When we terminate the cycle by clearing ACK,
-       **      the target may disconnect immediately.
-       **
-       **      We don't want to be told of an
-       **      "unexpected disconnect",
-       **      so we disable this feature.
-       */
-       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
-               0,
-       /*
-       **      Terminate cycle ...
-       */
-       SCR_CLR (SCR_ACK|SCR_ATN),
-               0,
-       /*
-       **      ... and wait for the disconnect.
-       */
-       SCR_WAIT_DISC,
-               0,
-}/*-------------------------< CLEANUP >-------------------*/,{
-       /*
-       **      dsa:    Pointer to ccb
-       **            or xxxxxxFF (no ccb)
-       **
-       **      HS_REG:   Host-Status (<>0!)
-       */
-       SCR_FROM_REG (dsa),
-               0,
-       SCR_JUMP ^ IFTRUE (DATA (0xff)),
-               PADDR (signal),
-       /*
-       **      dsa is valid.
-       **      save the status registers
-       */
-       SCR_COPY (4),
-               RADDR (scr0),
-               NADDR (header.status),
-       /*
-       **      and copy back the header to the ccb.
-       */
-       SCR_COPY (4),
-               RADDR (dsa),
-               PADDR (cleanup0),
-       SCR_COPY (sizeof (struct head)),
-               NADDR (header),
-}/*-------------------------< CLEANUP0 >--------------------*/,{
-               0,
-
-       /*
-       **      If command resulted in "check condition"
-       **      status and is not yet completed,
-       **      try to get the condition code.
-       */
-       SCR_FROM_REG (HS_REG),
-               0,
-/*<<<*/        SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
-               16,
-       SCR_FROM_REG (SS_REG),
-               0,
-       SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
-               PADDR(getcc2),
-       /*
-       **      And make the DSA register invalid.
-       */
-/*>>>*/        SCR_LOAD_REG (dsa, 0xff), /* invalid */
-               0,
-}/*-------------------------< SIGNAL >----------------------*/,{
-       /*
-       **      if status = queue full,
-       **      reinsert in startqueue and stall queue.
-       */
-       SCR_FROM_REG (SS_REG),
-               0,
-       SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
-               SIR_STALL_QUEUE,
-       /*
-       **      if job completed ...
-       */
-       SCR_FROM_REG (HS_REG),
-               0,
-       /*
-       **      ... signal completion to the host
-       */
-       SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)),
-               0,
-       /*
-       **      Auf zu neuen Schandtaten!
-       */
-       SCR_JUMP,
-               PADDR(start),
-
-}/*-------------------------< SAVE_DP >------------------*/,{
-       /*
-       **      SAVE_DP message:
-       **      Copy TEMP register to SAVEP in header.
-       */
-       SCR_COPY (4),
-               RADDR (temp),
-               NADDR (header.savep),
-       SCR_JUMP,
-               PADDR (clrack),
-}/*-------------------------< RESTORE_DP >---------------*/,{
-       /*
-       **      RESTORE_DP message:
-       **      Copy SAVEP in header to TEMP register.
-       */
-       SCR_COPY (4),
-               NADDR (header.savep),
-               RADDR (temp),
-       SCR_JUMP,
-               PADDR (clrack),
-
-}/*-------------------------< DISCONNECT >---------------*/,{
-       /*
-       **      If QUIRK_AUTOSAVE is set,
-       **      do an "save pointer" operation.
-       */
-       SCR_FROM_REG (QU_REG),
-               0,
-/*<<<*/        SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
-               12,
-       /*
-       **      like SAVE_DP message:
-       **      Copy TEMP register to SAVEP in header.
-       */
-       SCR_COPY (4),
-               RADDR (temp),
-               NADDR (header.savep),
-/*>>>*/        /*
-       **      Check if temp==savep or temp==goalp:
-       **      if not, log a missing save pointer message.
-       **      In fact, it's a comparison mod 256.
-       **
-       **      Hmmm, I hadn't thought that I would be urged to
-       **      write this kind of ugly self modifying code.
-       **
-       **      It's unbelievable, but the ncr53c8xx isn't able
-       **      to subtract one register from another.
-       */
-       SCR_FROM_REG (temp),
-               0,
-       /*
-       **      You are not expected to understand this ..
-       **
-       **      CAUTION: only little endian architectures supported! XXX
-       */
-       SCR_COPY (1),
-               NADDR (header.savep),
-               PADDR (disconnect0),
-}/*-------------------------< DISCONNECT0 >--------------*/,{
-/*<<<*/        SCR_JUMPR ^ IFTRUE (DATA (1)),
-               20,
-       /*
-       **      neither this
-       */
-       SCR_COPY (1),
-               NADDR (header.goalp),
-               PADDR (disconnect1),
-}/*-------------------------< DISCONNECT1 >--------------*/,{
-       SCR_INT ^ IFFALSE (DATA (1)),
-               SIR_MISSING_SAVE,
-/*>>>*/
-
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               PADDR (dispatch),
        /*
-       **      DISCONNECTing  ...
-       **
-       **      disable the "unexpected disconnect" feature,
-       **      and remove the ACK signal.
+       **      get extended message code.
        */
-       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               NADDR (msgin[2]),
+       /*
+       **      Check for message parity error.
+       */
+       SCR_TO_REG (scratcha),
                0,
-       SCR_CLR (SCR_ACK|SCR_ATN),
+       SCR_FROM_REG (socl),
                0,
+       SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+               PADDRH (msg_parity),
+       SCR_FROM_REG (scratcha),
+               0,
+       SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
+               PADDRH (msg_sdtr),
        /*
-       **      Wait for the disconnect.
+       **      unknown extended message
        */
-       SCR_WAIT_DISC,
+       SCR_JUMP,
+               PADDR (msg_bad)
+
+}/*-------------------------< MSG_SDTR >-----------------*/,{
+       SCR_CLR (SCR_ACK),
                0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               PADDR (dispatch),
        /*
-       **      Profiling:
-       **      Set a time stamp,
-       **      and count the disconnects.
+       **      get period and offset
        */
-       SCR_COPY (sizeof (u_long)),
-               KVAR(SCRIPT_KVAR_JIFFIES),
-               NADDR (header.stamp.disconnect),
-       SCR_COPY (4),
-               NADDR (disc_phys),
-               RADDR (temp),
-       SCR_REG_REG (temp, SCR_ADD, 0x01),
+       SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+               NADDR (msgin[3]),
+       SCR_FROM_REG (socl),
                0,
-       SCR_COPY (4),
-               RADDR (temp),
-               NADDR (disc_phys),
+       SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+               PADDRH (msg_parity),
        /*
-       **      Status is: DISCONNECTED.
+       **      let the host do the real work.
        */
-       SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+       SCR_INT,
+               SIR_NEGO_SYNC,
+       /*
+       **      let the target fetch our answer.
+       */
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
                0,
-       SCR_JUMP,
-               PADDR (cleanup),
 
-}/*-------------------------< MSG_OUT >-------------------*/,{
+       SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               SIR_NEGO_PROTO,
        /*
-       **      The target requests a message.
+       **      Send the M_X_SYNC_REQ
        */
-       SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+       SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
                NADDR (msgout),
+       SCR_CLR (SCR_ATN),
+               0,
        SCR_COPY (1),
                RADDR (sfbr),
                NADDR (lastmsg),
-       /*
-       **      If it was no ABORT message ...
-       */
-       SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
-               PADDR (msg_out_abort),
-       /*
-       **      ... wait for the next phase
-       **      if it's a message out, send it again, ...
-       */
-       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
-               PADDR (msg_out),
-}/*-------------------------< MSG_OUT_DONE >--------------*/,{
-       /*
-       **      ... else clear the message ...
-       */
-       SCR_LOAD_REG (scratcha, M_NOOP),
-               0,
-       SCR_COPY (4),
-               RADDR (scratcha),
-               NADDR (msgout),
-       /*
-       **      ... and process the next phase
-       */
        SCR_JUMP,
-               PADDR (dispatch),
+               PADDR (msg_out_done),
+
 }/*-------------------------< MSG_OUT_ABORT >-------------*/,{
        /*
        **      After ABORT message,
@@ -2723,9 +3301,9 @@ static    struct script script0 = {
        **      We patch the address part of a COPY command
        **      with the address of the dsa register ...
        */
-       SCR_COPY (4),
+       SCR_COPY_F (4),
                RADDR (dsa),
-               PADDR (getcc1),
+               PADDRH (getcc1),
        /*
        **      ... then we do the actual copy.
        */
@@ -2791,7 +3369,7 @@ static    struct script script0 = {
        SCR_FROM_REG (QU_REG),
                0,
        SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)),
-               PADDR(getcc3),
+               PADDRH(getcc3),
        /*
        **      Then try to connect to the target.
        **      If we are reselected, special treatment
@@ -2831,233 +3409,37 @@ static struct script script0 = {
        **      If we are reselected, special treatment
        **      of the current job is required before
        **      accepting the reselection.
-       **
-       **      Silly target won't accept a message.
-       **      Select without ATN.
-       */
-       SCR_SEL_TBL ^ offsetof (struct dsb, select),
-               PADDR(badgetcc),
-       /*
-       **      save target id.
-       */
-       SCR_FROM_REG (sdid),
-               0,
-       SCR_TO_REG (ctest0),
-               0,
-       /*
-       **      Force error if selection timeout
-       */
-       SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
-               0,
-       /*
-       **      don't negotiate.
-       */
-       SCR_JUMP,
-               PADDR (prepare2),
-
-}/*------------------------< BADGETCC >---------------------*/,{
-       /*
-       **      If SIGP was set, clear it and try again.
-       */
-       SCR_FROM_REG (ctest2),
-               0,
-       SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
-               PADDR (getcc2),
-       SCR_INT,
-               SIR_SENSE_FAILED,
-}/*-------------------------< RESELECT >--------------------*/,{
-       /*
-       **      make the DSA invalid.
-       */
-       SCR_LOAD_REG (dsa, 0xff),
-               0,
-       SCR_CLR (SCR_TRG),
-               0,
-       /*
-       **      Sleep waiting for a reselection.
-       **      If SIGP is set, special treatment.
-       **
-       **      Zu allem bereit ..
-       */
-       SCR_WAIT_RESEL,
-               PADDR(reselect2),
-       /*
-       **      ... zu nichts zu gebrauchen ?
-       **
-       **      load the target id into the SFBR
-       **      and jump to the control block.
-       **
-       **      Look at the declarations of
-       **      - struct ncb
-       **      - struct tcb
-       **      - struct lcb
-       **      - struct ccb
-       **      to understand what's going on.
-       */
-       SCR_REG_SFBR (ssid, SCR_AND, 0x87),
-               0,
-       SCR_TO_REG (ctest0),
-               0,
-       SCR_JUMP,
-               NADDR (jump_tcb),
-}/*-------------------------< RESELECT2 >-------------------*/,{
-       /*
-       **      If it's not connected :(
-       **      -> interrupted by SIGP bit.
-       **      Jump to start.
-       */
-       SCR_FROM_REG (ctest2),
-               0,
-       SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
-               PADDR (start),
-       SCR_JUMP,
-               PADDR (reselect),
-
-}/*-------------------------< RESEL_TMP >-------------------*/,{
-       /*
-       **      The return address in TEMP
-       **      is in fact the data structure address,
-       **      so copy it to the DSA register.
-       */
-       SCR_COPY (4),
-               RADDR (temp),
-               RADDR (dsa),
-       SCR_JUMP,
-               PADDR (prepare),
-
-}/*-------------------------< RESEL_LUN >-------------------*/,{
-       /*
-       **      come back to this point
-       **      to get an IDENTIFY message
-       **      Wait for a msg_in phase.
-       */
-/*<<<*/        SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-               48,
-       /*
-       **      message phase
-       **      It's not a sony, it's a trick:
-       **      read the data without acknowledging it.
-       */
-       SCR_FROM_REG (sbdl),
-               0,
-/*<<<*/        SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
-               32,
-       /*
-       **      It WAS an Identify message.
-       **      get it and ack it!
-       */
-       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-               NADDR (msgin),
-       SCR_CLR (SCR_ACK),
-               0,
-       /*
-       **      Mask out the lun.
-       */
-       SCR_REG_REG (sfbr, SCR_AND, 0x07),
-               0,
-       SCR_RETURN,
-               0,
-       /*
-       **      No message phase or no IDENTIFY message:
-       **      return 0.
-       */
-/*>>>*/        SCR_LOAD_SFBR (0),
-               0,
-       SCR_RETURN,
-               0,
-
-}/*-------------------------< RESEL_TAG >-------------------*/,{
-       /*
-       **      come back to this point
-       **      to get a SIMPLE_TAG message
-       **      Wait for a MSG_IN phase.
-       */
-/*<<<*/        SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-               64,
-       /*
-       **      message phase
-       **      It's a trick - read the data
-       **      without acknowledging it.
-       */
-       SCR_FROM_REG (sbdl),
-               0,
-/*<<<*/        SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
-               48,
-       /*
-       **      It WAS a SIMPLE_TAG message.
-       **      get it and ack it!
-       */
-       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-               NADDR (msgin),
-       SCR_CLR (SCR_ACK),
-               0,
-       /*
-       **      Wait for the second byte (the tag)
+       **
+       **      Silly target won't accept a message.
+       **      Select without ATN.
        */
-/*<<<*/        SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-               24,
+       SCR_SEL_TBL ^ offsetof (struct dsb, select),
+               PADDR(badgetcc),
        /*
-       **      Get it and ack it!
+       **      save target id.
        */
-       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-               NADDR (msgin),
-       SCR_CLR (SCR_ACK|SCR_CARRY),
+       SCR_FROM_REG (sdid),
                0,
-       SCR_RETURN,
+       SCR_TO_REG (ctest0),
                0,
        /*
-       **      No message phase or no SIMPLE_TAG message
-       **      or no second byte: return 0.
+       **      Force error if selection timeout
        */
-/*>>>*/        SCR_LOAD_SFBR (0),
-               0,
-       SCR_SET (SCR_CARRY),
-               0,
-       SCR_RETURN,
+       SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
                0,
+       /*
+       **      don't negotiate.
+       */
+       SCR_JUMP,
+               PADDR (prepare2),
 
-}/*-------------------------< DATA_IN >--------------------*/,{
-/*
-**     Because the size depends on the
-**     #define MAX_SCATTER parameter,
-**     it is filled in at runtime.
-**
-**     SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**             PADDR (no_data),
-**     SCR_COPY (sizeof (u_long)),
-**             KVAR(SCRIPT_KVAR_JIFFIES),
-**             NADDR (header.stamp.data),
-**     SCR_MOVE_TBL ^ SCR_DATA_IN,
-**             offsetof (struct dsb, data[ 0]),
-**
-**  ##===========< i=1; i<MAX_SCATTER >=========
-**  || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**  ||         PADDR (checkatn),
-**  || SCR_MOVE_TBL ^ SCR_DATA_IN,
-**  ||         offsetof (struct dsb, data[ i]),
-**  ##==========================================
-**
-**     SCR_CALL,
-**             PADDR (checkatn),
-**     SCR_JUMP,
-**             PADDR (no_data),
-*/
-0
 }/*-------------------------< DATA_OUT >-------------------*/,{
 /*
 **     Because the size depends on the
 **     #define MAX_SCATTER parameter,
 **     it is filled in at runtime.
 **
-**     SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**             PADDR (no_data),
-**     SCR_COPY (sizeof (u_long)),
-**             KVAR(SCRIPT_KVAR_JIFFIES),
-**             NADDR (header.stamp.data),
-**     SCR_MOVE_TBL ^ SCR_DATA_OUT,
-**             offsetof (struct dsb, data[ 0]),
-**
-**  ##===========< i=1; i<MAX_SCATTER >=========
+**  ##===========< i=0; i<MAX_SCATTER >=========
 **  || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
 **  ||         PADDR (dispatch),
 **  || SCR_MOVE_TBL ^ SCR_DATA_OUT,
@@ -3071,8 +3453,7 @@ static    struct script script0 = {
 **
 **---------------------------------------------------------
 */
-0      /* was (u_long)&ident ? */
-
+0
 }/*-------------------------< ABORTTAG >-------------------*/,{
        /*
        **      Abort a bad reselection.
@@ -3146,12 +3527,14 @@ static  struct script script0 = {
 **==========================================================
 */
 
-void ncr_script_fill (struct script * scr)
+__initfunc(
+void ncr_script_fill (struct script * scr, struct scripth * scrh)
+)
 {
        int     i;
        ncrcmd  *p;
 
-       p = scr->tryloop;
+       p = scrh->tryloop;
        for (i=0; i<MAX_START; i++) {
                *p++ =SCR_COPY (4);
                *p++ =NADDR (squeue[i]);
@@ -3160,21 +3543,13 @@ void ncr_script_fill (struct script * scr)
                *p++ =PADDR (trysel);
        };
        *p++ =SCR_JUMP;
-       *p++ =PADDR(tryloop);
+       *p++ =PADDRH(tryloop);
 
-       assert ((u_long)p == (u_long)&scr->tryloop + sizeof (scr->tryloop));
+       assert ((u_long)p == (u_long)&scrh->tryloop + sizeof (scrh->tryloop));
 
        p = scr->data_in;
 
-       *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN));
-       *p++ =PADDR (no_data);
-       *p++ =SCR_COPY (sizeof (u_long));
-       *p++ =KVAR(SCRIPT_KVAR_JIFFIES);
-       *p++ =NADDR (header.stamp.data);
-       *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
-       *p++ =offsetof (struct dsb, data[ 0]);
-
-       for (i=1; i<MAX_SCATTER; i++) {
+       for (i=0; i<MAX_SCATTER; i++) {
                *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
                *p++ =PADDR (checkatn);
                *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
@@ -3188,17 +3563,9 @@ void ncr_script_fill (struct script * scr)
 
        assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
 
-       p = scr->data_out;
-
-       *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT));
-       *p++ =PADDR (no_data);
-       *p++ =SCR_COPY (sizeof (u_long));
-       *p++ =KVAR(SCRIPT_KVAR_JIFFIES);
-       *p++ =NADDR (header.stamp.data);
-       *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
-       *p++ =offsetof (struct dsb, data[ 0]);
+       p = scrh->data_out;
 
-       for (i=1; i<MAX_SCATTER; i++) {
+       for (i=0; i<MAX_SCATTER; i++) {
                *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
                *p++ =PADDR (dispatch);
                *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
@@ -3210,7 +3577,7 @@ void ncr_script_fill (struct script * scr)
        *p++ =SCR_JUMP;
        *p++ =PADDR (no_data);
 
-       assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
+       assert ((u_long)p == (u_long)&scrh->data_out + sizeof (scrh->data_out));
 }
 
 /*==========================================================
@@ -3222,19 +3589,17 @@ void ncr_script_fill (struct script * scr)
 **==========================================================
 */
 
-static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
+__initfunc(
+static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
+)
 {
        ncrcmd  opcode, new, old, tmp1, tmp2;
-       ncrcmd  *src, *dst, *start, *end;
+       ncrcmd  *start, *end;
        int relocs;
-
-       np->p_script = vtophys(np->script);
-
-       src = script->start;
-       dst = np->script->start;
+       int opchanged = 0;
 
        start = src;
-       end = src + (sizeof (struct script) / 4);
+       end = src + len/4;
 
        while (src < end) {
 
@@ -3278,6 +3643,14 @@ static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
                                        ncr_name(np), (int) (src-start-1));
                                DELAY (1000000);
                        }
+                       /*
+                       **      If PREFETCH feature not enabled, remove 
+                       **      the NO FLUSH bit if present.
+                       */
+                       if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
+                               dst[-1] = (opcode & ~SCR_NO_FLUSH);
+                               ++opchanged;
+                       }
                        break;
 
                case 0x0:
@@ -3321,6 +3694,9 @@ static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
                                case RELOC_LABEL:
                                        new = (old & ~RELOC_MASK) + np->p_script;
                                        break;
+                               case RELOC_LABELH:
+                                       new = (old & ~RELOC_MASK) + np->p_scripth;
+                                       break;
                                case RELOC_SOFTC:
                                        new = (old & ~RELOC_MASK) + vtophys(np);
                                        break;
@@ -3351,6 +3727,9 @@ static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
                        *dst++ = *src++;
 
        };
+       if (bootverbose > 1 && opchanged)
+               printf("%s: NO FLUSH bit removed from %d script instructions\n",
+                       ncr_name(np), opchanged); 
 }
 
 /*==========================================================
@@ -3362,10 +3741,6 @@ static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
 **==========================================================
 */
 
-#define        MIN_ASYNC_PD    40
-#define        MIN_SYNC_PD     20
-
-
 /*
 **     Linux host data structure
 **
@@ -3375,25 +3750,526 @@ static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
 */
 
 struct host_data {
-     struct ncb ncb_data;
+     struct ncb *ncb;
+
+     char ncb_align[NCB_ALIGN_SIZE-1]; /* Filler for alignment */
+     struct ncb _ncb_data;
+
+     char ccb_align[CCB_ALIGN_SIZE-1]; /* Filler for alignment */
+     struct ccb _ccb_data;
+
+     char scr_align[SCR_ALIGN_SIZE-1]; /* Filler for alignment */
      struct script script_data;
+
+     struct scripth scripth_data;
 };
 
 /*
-**     Print something which allow to retreive the controler type, unit,
+**     Print something which allow to retrieve the controler type, unit,
 **     target, lun concerned by a kernel message.
 */
 
 #define PRINT_LUN(np, target, lun) \
-printf("%s-<target %d, lun %d>: ", ncr_name(np), (int) (target), (int) (lun))
+printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
 
-static inline void PRINT_ADDR(Scsi_Cmnd *cmd)
+static void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
        struct host_data *host_data = (struct host_data *) cmd->host->hostdata;
-       ncb_p np                    = &host_data->ncb_data;
+       ncb_p np                    = host_data->ncb;
        if (np) PRINT_LUN(np, cmd->target, cmd->lun);
 }
 
+/*==========================================================
+**
+**     NCR chip clock divisor table.
+**     Divisors are multiplied by 10,000,000 in order to make 
+**     calculations more simple.
+**
+**==========================================================
+*/
+
+#define _5M 5000000
+static u_long div_10M[] =
+       {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+
+/*===============================================================
+**
+**     Prepare io register values used by ncr_init() according 
+**     to selected and supported features.
+**
+**     NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 
+**     transfers. 32,64,128 are only supported by 875 and 895 chips.
+**     We use log base 2 (burst length) as internal code, with 
+**     value 0 meaning "burst disabled".
+**
+**===============================================================
+*/
+
+/*
+ *     Burst length from burst code.
+ */
+#define burst_length(bc) (!(bc))? 0 : 1 << (bc)
+
+/*
+ *     Burst code from io register bits.
+ */
+#define burst_code(dmode, ctest4, ctest5) \
+       (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1
+
+/*
+ *     Set initial io register bits from burst code.
+ */
+static inline void ncr_init_burst(ncb_p np, u_char bc)
+{
+       np->rv_ctest4   &= ~0x80;
+       np->rv_dmode    &= ~(0x3 << 6);
+       np->rv_ctest5   &= ~0x4;
+
+       if (!bc) {
+               np->rv_ctest4   |= 0x80;
+       }
+       else {
+               --bc;
+               np->rv_dmode    |= ((bc & 0x3) << 6);
+               np->rv_ctest5   |= (bc & 0x4);
+       }
+}
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/*
+**     Get target set-up from Symbios format NVRAM.
+*/
+
+__initfunc(
+static void
+       ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
+)
+{
+       tcb_p tp = &np->target[target];
+       Symbios_target *tn = &nvram->target[target];
+
+       tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
+       tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
+       tp->usrtags =
+               (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+
+       if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+               tp->usrflag |= UF_NODISC;
+       if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+               tp->usrflag |= UF_NOSCAN;
+}
+
+/*
+**     Get target set-up from Tekram format NVRAM.
+*/
+
+__initfunc(
+static void
+       ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
+)
+{
+       tcb_p tp = &np->target[target];
+       struct Tekram_target *tn = &nvram->target[target];
+       int i;
+
+       if (tn->flags & TEKRAM_SYNC_NEGO) {
+               i = tn->sync_index & 0xf;
+               tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+       }
+
+       tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
+
+       if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
+               tp->usrtags = 2 << nvram->max_tags_index;
+               if (tp->usrtags > SCSI_NCR_MAX_TAGS)
+                       tp->usrtags = SCSI_NCR_MAX_TAGS;
+       }
+
+       if (!(tn->flags & TEKRAM_DISCONNECT_ENABLE))
+               tp->usrflag = UF_NODISC;
+       /* If any device does not support parity, we will not use this option */
+       if (!(tn->flags & TEKRAM_PARITY_CHECK))
+               np->rv_scntl0  &= ~0x0a; /* SCSI parity checking disabled */
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
+static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
+)
+{
+       u_char  burst_max;
+       u_long  period;
+       int i;
+
+       /*
+       **      Save assumed BIOS setting
+       */
+
+       np->sv_scntl0   = INB(nc_scntl0) & 0x0a;
+       np->sv_scntl3   = INB(nc_scntl3) & 0x07;
+       np->sv_dmode    = INB(nc_dmode)  & 0xce;
+       np->sv_dcntl    = INB(nc_dcntl)  & 0xa8;
+       np->sv_ctest3   = INB(nc_ctest3) & 0x01;
+       np->sv_ctest4   = INB(nc_ctest4) & 0x80;
+       np->sv_ctest5   = INB(nc_ctest5) & 0x24;
+       np->sv_gpcntl   = INB(nc_gpcntl);
+       np->sv_stest2   = INB(nc_stest2) & 0x20;
+       np->sv_stest4   = INB(nc_stest4);
+
+       /*
+       **      Wide ?
+       */
+
+       np->maxwide     = (np->features & FE_WIDE)? 1 : 0;
+
+       /*
+       **      Get the frequency of the chip's clock.
+       **      Find the right value for scntl3.
+       */
+
+       if      (np->features & FE_QUAD)
+               np->multiplier  = 4;
+       else if (np->features & FE_DBLR)
+               np->multiplier  = 2;
+       else
+               np->multiplier  = 1;
+
+       np->clock_khz   = (np->features & FE_CLK80)? 80000 : 40000;
+       np->clock_khz   *= np->multiplier;
+
+       if (np->clock_khz != 40000)
+               ncr_getclock(np, np->multiplier);
+
+       /*
+        * Divisor to be used for async (timer pre-scaler).
+        */
+       i = np->clock_divn - 1;
+       while (i >= 0) {
+               --i;
+               if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+                       ++i;
+                       break;
+               }
+       }
+       np->rv_scntl3 = i+1;
+
+       /*
+        * Minimum synchronous period factor supported by the chip.
+        * Btw, 'period' is in tenths of nanoseconds.
+        */
+
+       period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+       if      (period <= 250)         np->minsync = 10;
+       else if (period <= 303)         np->minsync = 11;
+       else if (period <= 500)         np->minsync = 12;
+       else                            np->minsync = (period + 40 - 1) / 40;
+
+       /*
+        * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+        */
+
+       if      (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2)))
+               np->minsync = 25;
+       else if (np->minsync < 12 && !(np->features & FE_ULTRA2))
+               np->minsync = 12;
+
+       /*
+        * Maximum synchronous period factor supported by the chip.
+        */
+
+       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
+       */
+#if defined SCSI_NCR_TRUST_BIOS_SETTING
+       np->rv_scntl0   = np->sv_scntl0;
+       np->rv_dmode    = np->sv_dmode;
+       np->rv_dcntl    = np->sv_dcntl;
+       np->rv_ctest3   = np->sv_ctest3;
+       np->rv_ctest4   = np->sv_ctest4;
+       np->rv_ctest5   = np->sv_ctest5;
+       burst_max       = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+#else
+
+       /*
+       **      Select burst length (dwords)
+       */
+       burst_max       = driver_setup.burst_max;
+       if (burst_max == 255)
+               burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+       if (burst_max > 7)
+               burst_max = 7;
+       if (burst_max > np->maxburst)
+               burst_max = np->maxburst;
+
+       /*
+       **      Select all supported special features
+       */
+       if (np->features & FE_ERL)
+               np->rv_dmode    |= ERL;         /* Enable Read Line */
+       if (np->features & FE_BOF)
+               np->rv_dmode    |= BOF;         /* Burst Opcode Fetch */
+       if (np->features & FE_ERMP)
+               np->rv_dmode    |= ERMP;        /* Enable Read Multiple */
+       if (np->features & FE_PFEN)
+               np->rv_dcntl    |= PFEN;        /* Prefetch Enable */
+       if (np->features & FE_CLSE)
+               np->rv_dcntl    |= CLSE;        /* Cache Line Size Enable */
+       if (np->features & FE_WRIE)
+               np->rv_ctest3   |= WRIE;        /* Write and Invalidate */
+       if (np->features & FE_DFS)
+               np->rv_ctest5   |= DFS;         /* Dma Fifo Size */
+
+       /*
+       **      Select some other
+       */
+       if (driver_setup.master_parity)
+               np->rv_ctest4   |= MPEE;        /* Master parity checking */
+       if (driver_setup.scsi_parity)
+               np->rv_scntl0   |= 0x0a;        /*  full arb., ena parity, par->ATN  */
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       /*
+       **      Get parity checking, host ID and verbose mode from NVRAM
+       **/
+       if (nvram) {
+               switch(nvram->type) {
+               case SCSI_NCR_TEKRAM_NVRAM:
+                       np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+                       break;
+               case SCSI_NCR_SYMBIOS_NVRAM:
+                       if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+                               np->rv_scntl0  &= ~0x0a;
+                       np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+                       if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+                               np->verbose += 1;
+                       break;
+               }
+       }
+#endif
+       /*
+       **  Get SCSI addr of host adapter (set by bios?).
+       */
+       if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07;
+       if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+
+
+#endif /* SCSI_NCR_TRUST_BIOS_SETTING */
+
+       /*
+        *      Prepare initial io register bits for burst length
+        */
+       ncr_init_burst(np, burst_max);
+
+       /*
+       **      Set differential mode and LED support.
+       **      Ignore these features for boards known to use a 
+       **      specific GPIO wiring (Tekram only for now).
+       **      Probe initial setting of GPREG and GPCNTL for 
+       **      other ones.
+       */
+       if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+               switch(driver_setup.diff_support) {
+               case 3:
+                       if (INB(nc_gpreg) & 0x08)
+                       break;
+               case 2:
+                       np->rv_stest2   |= 0x20;
+                       break;
+               case 1:
+                       np->rv_stest2   |= (np->sv_stest2 & 0x20);
+                       break;
+               default:
+                       break;
+               }
+       }
+       if ((driver_setup.led_pin ||
+            (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
+           !(np->sv_gpcntl & 0x01))
+               np->features |= FE_LED0;
+
+       /*
+       **      Set irq mode.
+       */
+       switch(driver_setup.irqm) {
+       case 2:
+               np->rv_dcntl    |= IRQM;
+               break;
+       case 1:
+               np->rv_dcntl    |= (np->sv_dcntl & IRQM);
+               break;
+       default:
+               break;
+       }
+
+       /*
+       **      Configure targets according to driver setup.
+       **      If NVRAM present get targets setup from NVRAM.
+       **      Allow to override sync, wide and NOSCAN from 
+       **      boot command line.
+       */
+       for (i = 0 ; i < MAX_TARGET ; i++) {
+               tcb_p tp = &np->target[i];
+
+               tp->usrsync = 255;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+               if (nvram) {
+                       switch(nvram->type) {
+                       case SCSI_NCR_TEKRAM_NVRAM:
+                               ncr_Tekram_setup_target(np, i, &nvram->data.Tekram);
+                               break;
+                       case SCSI_NCR_SYMBIOS_NVRAM:
+                               ncr_Symbios_setup_target(np, i, &nvram->data.Symbios);
+                               break;
+                       }
+                       if (driver_setup.use_nvram & 0x2)
+                               tp->usrsync = driver_setup.default_sync;
+                       if (driver_setup.use_nvram & 0x4)
+                               tp->usrwide = driver_setup.max_wide;
+                       if (driver_setup.use_nvram & 0x8)
+                               tp->usrflag &= ~UF_NOSCAN;
+               }
+               else {
+#else
+               if (1) {
+#endif
+                       tp->usrsync = driver_setup.default_sync;
+                       tp->usrwide = driver_setup.max_wide;
+                       tp->usrtags = driver_setup.default_tags;
+                       if (!driver_setup.disconnection)
+                               np->target[i].usrflag = UF_NODISC;
+               }
+       }
+
+       /*
+       **      Announce all that stuff to user.
+       */
+
+       i = nvram ? nvram->type : 0;
+       printf(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np),
+               i  == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " :
+               (i == SCSI_NCR_TEKRAM_NVRAM  ? "Tekram format NVRAM, " : ""),
+               np->myaddr,
+               np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+               (np->rv_scntl0 & 0xa)   ? ", Parity Checking"   : ", NO Parity",
+               (np->rv_stest2 & 0x20)  ? ", Differential"      : "");
+
+       if (bootverbose > 1) {
+               printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+                       "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+                       ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+                       np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+               printf ("%s: final   SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+                       "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+                       ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+                       np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
+       }
+
+       if (bootverbose && np->paddr2)
+               printf (KERN_INFO "%s: on-board RAM at 0x%lx\n",
+                       ncr_name(np), np->paddr2);
+
+       return 0;
+}
+
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+
+__initfunc(
+void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
+)
+{
+       int i;
+
+       /* display Symbios nvram host data */
+       printf("%s: HOST ID=%d%s%s%s%s\n",
+               ncr_name(np), nvram->host_id & 0x0f,
+               (nvram->flags  & SYMBIOS_SCAM_ENABLE)   ? " SCAM"       :"",
+               (nvram->flags  & SYMBIOS_PARITY_ENABLE) ? " PARITY"     :"",
+               (nvram->flags  & SYMBIOS_VERBOSE_MSGS)  ? " VERSBOSE"   :"", 
+               (nvram->flags1 & SYMBIOS_SCAN_HI_LO)    ? " HI_LO"      :"");
+
+       /* display Symbios nvram drive data */
+       for (i = 0 ; i < 15 ; i++) {
+               struct Symbios_target *tn = &nvram->target[i];
+               printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+               ncr_name(np), i,
+               (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC"       : "",
+               (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT"  : "",
+               (tn->flags & SYMBIOS_SCAN_LUNS)         ? " SCAN_LUNS"  : "",
+               (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"        : "",
+               tn->bus_width,
+               tn->sync_period / 4,
+               tn->timeout);
+       }
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+__initfunc(
+void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
+)
+{
+       int i, tags, boot_delay;
+       char *rem;
+
+       /* display Tekram nvram host data */
+       tags = 2 << nvram->max_tags_index;
+       boot_delay = 0;
+       if (nvram->boot_delay_index < 6)
+               boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+       switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+       default:
+       case 0: rem = "";                       break;
+       case 1: rem = " REMOVABLE=boot device"; break;
+       case 2: rem = " REMOVABLE=all";         break;
+       }
+
+       printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+               ncr_name(np), nvram->host_id & 0x0f,
+               (nvram->flags1 & SYMBIOS_SCAM_ENABLE)   ? " SCAM"       :"",
+               (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES"        :"",
+               (nvram->flags & TEKRAM_DRIVES_SUP_1GB)  ? " >1GB"       :"",
+               (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"    :"",
+               (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG"    :"",
+               (nvram->flags & TEKRAM_IMMEDIATE_SEEK)  ? " IMM_SEEK"   :"",
+               (nvram->flags & TEKRAM_SCAN_LUNS)       ? " SCAN_LUNS"  :"",
+               (nvram->flags1 & TEKRAM_F2_F6_ENABLED)  ? " F2_F6"      :"",
+               rem, boot_delay, tags);
+
+       /* display Tekram nvram drive data */
+       for (i = 0; i <= 15; i++) {
+               int sync, j;
+               struct Tekram_target *tn = &nvram->target[i];
+               j = tn->sync_index & 0xf;
+               sync = j < 12 ? Tekram_sync[j] : 255;
+               printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+               ncr_name(np), i,
+               (tn->flags & TEKRAM_PARITY_CHECK)       ? " PARITY"     : "",
+               (tn->flags & TEKRAM_SYNC_NEGO)          ? " SYNC"       : "",
+               (tn->flags & TEKRAM_DISCONNECT_ENABLE)  ? " DISC"       : "",
+               (tn->flags & TEKRAM_START_CMD)          ? " START"      : "",
+               (tn->flags & TEKRAM_TAGGED_COMMANDS)    ? " TCQ"        : "",
+               (tn->flags & TEKRAM_WIDE_NEGO)          ? " WIDE"       : "",
+               sync);
+       }
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
 
 /*
 **     Host attach and initialisations.
@@ -3401,24 +4277,23 @@ static inline void PRINT_ADDR(Scsi_Cmnd *cmd)
 **     Allocate host data and ncb structure.
 **     Request IO region and remap MMIO region.
 **     Do chip initialization.
-**     Try with mmio.
-**     If mmio not possible (misconfigured cache),
-**     retry with io mapped.
 **     If all is OK, install interrupt handling and
 **     start the timer daemon.
 */
 
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ushort device_id,
-                      u_char revision_id, int chip, u_int base, u_int io_port, 
-                      int irq, int bus, u_char device_fn)
-
+__initfunc(
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
+)
 {
         struct host_data *host_data;
        ncb_p np;
         struct Scsi_Host *instance = 0;
        u_long flags = 0;
+       ncr_nvram *nvram = device->nvram;
 
-printf("ncr_attach: unit=%d chip=%d base=%x, io_port=%x, irq=%d\n", unit, chip, base, io_port, irq);
+printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+       device->chip.name, unit, device->chip.revision_id, device->slot.base,
+       device->slot.io_port, device->slot.irq);
 
        /*
        **      Allocate host_data structure
@@ -3429,16 +4304,36 @@ printf("ncr_attach: unit=%d chip=%d base=%x, io_port=%x, irq=%d\n", unit, chip,
        /*
        **      Initialize structure.
        */
-       instance->irq = irq;
-       host_data = (struct host_data *) instance->hostdata;
+       host_data = (struct host_data *) instance->hostdata;
+
+       /*
+       **      Align np and first ccb to 32 boundary for cache line 
+       **      bursting when copying the global header.
+       */
+       np        = (ncb_p) (((u_long) &host_data->_ncb_data) & NCB_ALIGN_MASK);
+       host_data->ncb = np;
+       bzero (np, sizeof (*np));
+
+       np->ccb   = (ccb_p) (((u_long) &host_data->_ccb_data) & CCB_ALIGN_MASK);
+       bzero (np->ccb, sizeof (*np->ccb));
+
+       /*
+       **      Store input informations in the host data structure.
+       */
+       strncpy(np->chip_name, device->chip.name, sizeof(np->chip_name) - 1);
+       np->unit        = unit;
+       np->verbose     = driver_setup.verbose;
+       sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit);
+       np->device_id   = device->chip.device_id;
+       np->revision_id = device->chip.revision_id;
+       np->features    = device->chip.features;
+       np->clock_divn  = device->chip.nr_divisor;
+       np->maxoffs     = device->chip.offset_max;
+       np->maxburst    = device->chip.burst_max;
 
-       np        = &host_data->ncb_data;
-       bzero (np, sizeof (*np));
-       np->unit = unit;
-       np->chip = chip;
-       np->device_id   = device_id;
-       np->revision_id = revision_id;
-       np->script = &host_data->script_data;
+       np->script0  =
+       (struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
+       np->scripth0 = &host_data->scripth_data;
 
        /*
        **    Initialize timer structure
@@ -3453,38 +4348,74 @@ printf("ncr_attach: unit=%d chip=%d base=%x, io_port=%x, irq=%d\n", unit, chip,
        **      virtual and physical memory.
        */
 
-       np->paddr = base;
-       np->vaddr = base;
+       np->paddr = device->slot.base;
 
 #ifndef NCR_IOMAPPED
-       np->reg_remapped = (struct ncr_reg *) remap_pci_mem((u_long) base, (u_long) 128);
-       if (!np->reg_remapped) {
+       np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
+       if (!np->vaddr) {
                printf("%s: can't map memory mapped IO region\n", ncr_name(np));
-               np->use_mmio = 0;
+               goto attach_error;
        }
-       printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->reg_remapped);
-               np->use_mmio = 1;
-#endif
+       else
+               if (bootverbose > 1)
+                       printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+
+       /*
+       **      Make the controller's registers available.
+       **      Now the INB INW INL OUTB OUTW OUTL macros
+       **      can be used safely.
+       */
+
+       np->reg = (struct ncr_reg*) np->vaddr;
+
+#endif /* !defined NCR_IOMAPPED */
+
        /*
        **      Try to map the controller chip into iospace.
        */
 
-       request_region(io_port, 128, "ncr53c8xx");
-       np->port = io_port;
+       request_region(device->slot.io_port, 128, "ncr53c8xx");
+       np->port = device->slot.io_port;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       if (nvram) {
+               switch(nvram->type) {
+               case SCSI_NCR_SYMBIOS_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+                       ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+#endif
+                       break;
+               case SCSI_NCR_TEKRAM_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+                       ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+#endif
+                       break;
+               default:
+                       nvram = 0;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+                       printf("%s: NVRAM: None or invalid data.\n", ncr_name(np));
+#endif
+               }
+       }
+#endif
 
        /*
        **      Do chip dependent initialization.
        */
+       (void)ncr_prepare_setting(np, nvram);
 
-       switch (device_id) {
-       case PCI_DEVICE_ID_NCR_53C825:
-       case PCI_DEVICE_ID_NCR_53C875:
-               np->maxwide = 1;
-               break;
-       default:
-               np->maxwide = 0;
-               break;
+#ifndef NCR_IOMAPPED
+       if (np->paddr2 && sizeof(struct script) <= 4096) {
+               np->vaddr2 = remap_pci_mem((u_long) np->paddr2, (u_long) 4096);
+               if (!np->vaddr2) {
+                       printf("%s: can't map memory mapped IO region\n", ncr_name(np));
+                       goto attach_error;
+               }
+               else
+                       if (bootverbose > 1)
+                               printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr2);
        }
+#endif /* !defined NCR_IOMAPPED */
 
        /*
        **      Fill Linux host instance structure
@@ -3495,9 +4426,10 @@ printf("ncr_attach: unit=%d chip=%d base=%x, io_port=%x, irq=%d\n", unit, chip,
        instance->max_lun       = SCSI_NCR_MAX_LUN;
 #endif
 #ifndef NCR_IOMAPPED
-       instance->base          = (char *) np->reg_remapped;
+       instance->base          = (char *) np->reg;
 #endif
-       instance->io_port       = io_port;
+       instance->irq           = device->slot.irq;
+       instance->io_port       = device->slot.io_port;
        instance->n_io_port     = 128;
        instance->dma_channel   = 0;
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -3507,58 +4439,39 @@ printf("ncr_attach: unit=%d chip=%d base=%x, io_port=%x, irq=%d\n", unit, chip,
        /*
        **      Patch script to physical addresses
        */
-       ncr_script_fill (&script0);
-       ncr_script_copy_and_bind (&script0, np);
-       np->ccb.p_ccb           = vtophys (&np->ccb);
+       ncr_script_fill (&script0, &scripth0);
 
-       /*
-       **      init data structure
-       */
-
-       np->jump_tcb.l_cmd      = SCR_JUMP;
-       np->jump_tcb.l_paddr    = NCB_SCRIPT_PHYS (np, abort);
-
-       /*
-       **      Make the controller's registers available.
-       **      Now the INB INW INL OUTB OUTW OUTL macros
-       **      can be used safely.
-       */
+       np->scripth     = np->scripth0;
+       np->p_scripth   = vtophys(np->scripth);
 
-       np->reg = (struct ncr_reg*) np->vaddr;
+       np->script      = (np->vaddr2) ? (struct script *) np->vaddr2 : np->script0;
+       np->p_script    = (np->vaddr2) ? np->paddr2 : vtophys(np->script0);
 
-#ifndef NCR_IOMAPPED
-retry_chip_init:
-#endif
+       ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
+       ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
+       np->ccb->p_ccb          = vtophys (np->ccb);
 
        /*
-       **  Get SCSI addr of host adapter (set by bios?).
+       **    Patch the script for LED support.
        */
 
-       np->myaddr = INB(nc_scid) & 0x07;
-       if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+       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);
+       }
 
        /*
-       **      Get the value of the chip's clock.
-       **      Find the right value for scntl3.
+       **      init data structure
        */
 
-       ncr_getclock (np, INB(nc_scntl3));
+       np->jump_tcb.l_cmd      = SCR_JUMP;
+       np->jump_tcb.l_paddr    = NCB_SCRIPTH_PHYS (np, abort);
 
        /*
        **      Reset chip.
        */
 
-       OUTW (nc_sien , 0);     /* Disable scsi interrupts */
-       OUTB (nc_dien , 0);     /* Disable dma interrupts */
-
-       OUTB (nc_istat,  SRST);
-       DELAY (1000);
-       OUTB (nc_istat,  0   );
-
-       /*
-       **      Reset chip, once again.
-       */
-
        OUTB (nc_istat,  SRST);
        DELAY (1000);
        OUTB (nc_istat,  0   );
@@ -3568,14 +4481,6 @@ retry_chip_init:
        */
 
        if (ncr_snooptest (np)) {
-#ifndef NCR_IOMAPPED
-               if (np->use_mmio) {
-printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
-       ncr_name(np), (u_long) np->port);
-                       np->use_mmio = 0;
-                       goto retry_chip_init;
-               }
-#endif
                printf ("CACHE INCORRECTLY CONFIGURED.\n");
                goto attach_error;
        };
@@ -3584,55 +4489,50 @@ printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
        **      Install the interrupt handler.
        */
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-#   ifdef SCSI_NCR_SHARE_IRQ
-       printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
-               ncr_name(np), irq, (u_long) np);
-       if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) {
-#   else
-       if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) {
-#   endif
+#ifdef SCSI_NCR_SHARE_IRQ
+       if (bootverbose > 1)
+               printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
+                       ncr_name(np), device->slot.irq, (u_long) np);
+       if (request_irq(device->slot.irq, ncr53c8xx_intr,
+                       SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
+#else
+       if (request_irq(device->slot.irq, ncr53c8xx_intr,
+                       SA_INTERRUPT, "ncr53c8xx", NULL)) {
+#endif
 #else
-       if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx")) {
+       if (request_irq(device->slot.irq, ncr53c8xx_intr,
+                       SA_INTERRUPT, "ncr53c8xx")) {
 #endif
-               printf("%s: request irq %d failure\n", ncr_name(np), irq);
+               printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq);
                goto attach_error;
        }
-       np->irq = irq;
+       np->irq = device->slot.irq;
 
        /*
        **      After SCSI devices have been opened, we cannot
        **      reset the bus safely, so we do it here.
        **      Interrupt handler does the real work.
-       */
-
-       OUTB (nc_scntl1, CRST);
-       DELAY (1000);
-
-       /*
        **      Process the reset exception,
        **      if interrupts are not enabled yet.
        **      Then enable disconnects.
        */
        save_flags(flags); cli();
+       ncr_start_reset(np, driver_setup.settle_delay);
        ncr_exception (np);
        restore_flags(flags);
 
-#ifndef SCSI_NCR_NO_DISCONNECT
        np->disc = 1;
-#endif
 
        /*
        **      The middle-level SCSI driver does not
        **      wait devices to settle.
+       **      Wait synchronously if more than 2 seconds.
        */
-#ifdef SCSI_NCR_SETTLE_TIME
-#if    SCSI_NCR_SETTLE_TIME > 2
-       printf("%s: waiting for scsi devices to settle...\n", ncr_name(np));
-#endif
-#if    SCSI_NCR_SETTLE_TIME > 0
-       DELAY(SCSI_NCR_SETTLE_TIME*1000000);
-#endif
-#endif
+       if (driver_setup.settle_delay > 2) {
+               printf("%s: waiting %d seconds for scsi devices to settle...\n",
+                       ncr_name(np), driver_setup.settle_delay);
+               DELAY(1000000UL * driver_setup.settle_delay);
+       }
 
        /*
        **      Now let the generic SCSI driver
@@ -3642,8 +4542,8 @@ printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
        /*
        **      start the timeout daemon
        */
-       ncr_timeout (np);
        np->lasttime=0;
+       ncr_timeout (np);
 
        /*
        **  use SIMPLE TAG messages by default
@@ -3664,14 +4564,25 @@ printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
 
 attach_error:
        if (!instance) return -1;
+       printf("%s: detaching...\n", ncr_name(np));
 #ifndef NCR_IOMAPPED
-       if (np->reg_remapped) {
-               printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg_remapped, 128);
-               unmap_pci_mem((vm_offset_t) np->reg_remapped, (u_long) 128);
+       if (np->vaddr) {
+#ifdef DEBUG_NCR53C8XX
+               printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
+#endif
+               unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
+       }
+       if (np->vaddr2) {
+#ifdef DEBUG_NCR53C8XX
+               printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
+#endif
+               unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
        }
 #endif
        if (np->port) {
+#ifdef DEBUG_NCR53C8XX
                printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+#endif
                release_region(np->port, 128);
        }
        scsi_unregister(instance);
@@ -3679,47 +4590,6 @@ attach_error:
         return -1;
  }
 
-/*==========================================================
-**
-**
-**     Process pending device interrupts.
-**
-**
-**==========================================================
-*/
-int ncr_intr(np)
-       ncb_p np;
-{
-       int n = 0;
-       u_long flags;
-
-       save_flags(flags); cli();
-
-       if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
-
-#ifdef SCSI_NCR_PARANOIA
-       if (INB(nc_istat) & (INTF|SIP|DIP)) {
-               /*
-               **      Repeat until no outstanding ints
-               */
-               do {
-#endif
-                       ncr_exception (np);
-#ifdef SCSI_NCR_PARANOIA
-               } while (INB(nc_istat) & (INTF|SIP|DIP));
-
-               n=1;
-               np->ticks = 5 * HZ;
-       };
-#endif
-
-       if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
-
-       restore_flags(flags);
-
-       return (n);
-}
-
 /*==========================================================
 **
 **
@@ -3734,7 +4604,7 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
         struct Scsi_Host   *host      = cmd->host;
 /*     Scsi_Device        *device    = cmd->device; */
        struct host_data   *host_data = (struct host_data *) host->hostdata;
-       ncb_p np                      = &host_data->ncb_data;
+       ncb_p np                      = host_data->ncb;
        tcb_p tp                      = &np->target[cmd->target];
 
        ccb_p cp;
@@ -3742,7 +4612,7 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 
        int     segments;
        u_char  qidx, nego, idmsg, *msgptr;
-       u_long  msglen, msglen2;
+       u_int  msglen, msglen2;
        u_long  flags;
        int     xfer_direction;
 
@@ -3751,22 +4621,6 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        cmd->SCp.ptr       = NULL;
        cmd->SCp.buffer    = NULL;
 
-       /*---------------------------------------------
-       **
-       **   Reset SCSI bus
-       **
-       **      Interrupt handler does the real work.
-       **
-       **---------------------------------------------
-       */
-#if 0
-       if (flags & SCSI_RESET) {
-               OUTB (nc_scntl1, CRST);
-               DELAY (1000);
-               return(COMPLETE);
-       }
-#endif
-
        /*---------------------------------------------
        **
        **      Some shortcuts ...
@@ -3779,6 +4633,19 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
                return(DID_BAD_TARGET);
         }
 
+       /*---------------------------------------------
+       **
+       **      Complete the 1st TEST UNIT READY command
+       **      with error condition if the device is 
+       **      flagged NOSCAN, in order to speed up 
+       **      the boot.
+       **
+       **---------------------------------------------
+       */
+       if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) {
+               tp->usrflag &= ~UF_NOSCAN;
+               return DID_BAD_TARGET;
+       }
 
        if (DEBUG_FLAGS & DEBUG_TINY) {
                PRINT_ADDR(cmd);
@@ -3788,13 +4655,14 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        /*---------------------------------------------------
        **
        **      Assign a ccb / bind cmd
-       **      If no free ccb, insert cmd into the waiting list.
+       **      If resetting or no free ccb,
+       **      insert cmd into the waiting list.
        **
        **----------------------------------------------------
        */
        save_flags(flags); cli();
 
-        if (!(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
+        if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
                insert_into_waiting_list(np, cmd);
                restore_flags(flags);
                return(DID_OK);
@@ -3803,16 +4671,14 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 
        /*---------------------------------------------------
        **
-       **      Enable tagged queue if asked by user
+       **      Enable tagged queue if asked by scsi ioctl
        **
        **----------------------------------------------------
        */
-#ifdef SCSI_NCR_TAGGED_QUEUE_DISABLED
-       if (cmd->device && cmd->device->tagged_queue &&
-           (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
+       if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
+               tp->usrtags = SCSI_NCR_MAX_TAGS;
                ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
        }
-#endif
 
        /*---------------------------------------------------
        **
@@ -3820,9 +4686,10 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        **
        **----------------------------------------------------
        */
-
+#ifdef SCSI_NCR_PROFILE_SUPPORT
        bzero (&cp->phys.header.stamp, sizeof (struct tstamp));
        cp->phys.header.stamp.start = jiffies;
+#endif
 
        /*----------------------------------------------------
        **
@@ -3857,7 +4724,8 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 
        nego = 0;
 
-       if (cmd->lun == 0 && (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+       if (cmd->lun == 0 && !tp->nego_cp && 
+           (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
                /*
                **      negotiate wide transfers ?
                */
@@ -3874,7 +4742,7 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
                */
 
                if (!nego && !tp->period) {
-                       if (SCSI_NCR_MAX_SYNC 
+                       if ( 1
 #if defined (CDROM_ASYNC)
                            && ((tp->inqdata[0] & 0x1f) != 5)
 #endif
@@ -3887,6 +4755,15 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
                                printf ("asynchronous.\n");
                        };
                };
+
+               /*
+               **      remember nego is pending for the target.
+               **      Avoid to start a nego for all queued commands 
+               **      when tagged command queuing is enabled.
+               */
+
+               if (nego)
+                       tp->nego_cp = cp;
        };
 
        /*---------------------------------------------------
@@ -3925,7 +4802,7 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 
        idmsg = M_IDENTIFY | cmd->lun;
 
-       if ((cp!=&np->ccb) && (np->disc))
+       if (cp != np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag))
                idmsg |= 0x40;
 
        msgptr = cp->scsi_smsg;
@@ -4013,7 +4890,7 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        segments = ncr_scatter (cp, cp->cmd);
 
        if (segments < 0) {
-               ncr_free_ccb(np, cp);
+               ncr_free_ccb(np, cp, cmd->target, cmd->lun);
                restore_flags(flags);
                return(DID_ERROR);
        }
@@ -4028,10 +4905,12 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        switch((int) cmd->cmnd[0]) {
        case 0x08:  /*  READ(6)                         08 */
        case 0x28:  /*  READ(10)                        28 */
+       case 0xA8:  /*  READ(12)                        A8 */
                xfer_direction = XferIn;
                break;
        case 0x0A:  /*  WRITE(6)                        0A */
        case 0x2A:  /*  WRITE(10)                       2A */
+       case 0xAA:  /*  WRITE(12)                       AA */
                xfer_direction = XferOut;
                break;
        default:
@@ -4046,20 +4925,31 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        **----------------------------------------------------
        */
 
+       cp->segments = segments;
+       if (!cp->data_len)
+               xfer_direction = XferNone;
+
        switch (xfer_direction) {
+               u_long endp;
        default:
+       case XferBoth:
+               cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_io);
+               cp->phys.header.goalp = cp->phys.header.savep;
+               break;
        case XferIn:
-            cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_in);
-            cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16;
-            break;
+               endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
+               cp->phys.header.goalp = endp + 8;
+               cp->phys.header.savep = endp - segments*16;
+               break;
        case XferOut:
-            cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_out);
-            cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16;
-            break;
+               endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
+               cp->phys.header.goalp = endp + 8;
+               cp->phys.header.savep = endp - segments*16;
+               break;
        case XferNone:
-            cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
-            cp->phys.header.goalp = cp->phys.header.savep;
-            break;
+               cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
+               cp->phys.header.goalp = cp->phys.header.savep;
+               break;
        }
 
        cp->phys.header.lastp = cp->phys.header.savep;
@@ -4161,12 +5051,15 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
                printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
                np->squeueput,
                (unsigned)(np->script->startpos[0]- 
-                          (NCB_SCRIPT_PHYS (np, tryloop))));
+                          (NCB_SCRIPTH_PHYS (np, tryloop))));
 
        /*
        **      Script processor may be waiting for reselect.
        **      Wake it up.
        */
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+       if (!np->stalling)
+#endif
        OUTB (nc_istat, SIGP);
 
        /*
@@ -4181,6 +5074,40 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        return(DID_OK);
 }
 
+/*==========================================================
+**
+**
+**     Start reset process.
+**     If reset in progress do nothing.
+**     The interrupt handler will reinitialize the chip.
+**     The timeout handler will wait for settle_time before 
+**     clearing it and so resuming command processing.
+**
+**
+**==========================================================
+*/
+static void ncr_start_reset(ncb_p np, int settle_delay)
+{
+       u_long flags;
+
+       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);
+       }
+
+       restore_flags(flags);
+}
+
 /*==========================================================
 **
 **
@@ -4190,22 +5117,72 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 **
 **==========================================================
 */
-int ncr_reset_bus (Scsi_Cmnd *cmd)
+int ncr_reset_bus (Scsi_Cmnd *cmd, int sync_reset)
 {
         struct Scsi_Host   *host      = cmd->host;
 /*     Scsi_Device        *device    = cmd->device; */
        struct host_data   *host_data = (struct host_data *) host->hostdata;
-       ncb_p np                      = &host_data->ncb_data;
+       ncb_p np                      = host_data->ncb;
+       ccb_p cp;
        u_long flags;
+       int found;
 
-       save_flags(flags); cli();
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+       if (np->stalling)
+               np->stalling = 0;
+#endif
 
+       save_flags(flags); cli();
+/*
+ * Return immediately if reset is in progress.
+ */
+       if (np->settle_time) {
+               restore_flags(flags);
+               return SCSI_RESET_PUNT;
+       }
+/*
+ * Start the reset process.
+ * The script processor is then assumed to be stopped.
+ * Commands will now be queued in the waiting list until a settle 
+ * delay of 2 seconds will be completed.
+ */
+       ncr_start_reset(np, 2);
+/*
+ * First, look in the wakeup list
+ */
+       for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
+               /*
+               **      look for the ccb of this command.
+               */
+               if (cp->host_status == HS_IDLE) continue;
+               if (cp->cmd == cmd) {
+                       found = 1;
+                       break;
+               }
+       }
+/*
+ * Then, look in the waiting list
+ */
+       if (!found && retrieve_from_waiting_list(0, np, cmd))
+               found = 1;
+/*
+ * Wake-up all awaiting commands with DID_RESET.
+ */
        reset_waiting_list(np);
-       ncr_init(np, "scsi bus reset", HS_RESET);
-
-#ifndef SCSI_NCR_NO_DISCONNECT
-       np->disc = 1;
-#endif
+/*
+ * Wake-up all pending commands with HS_RESET -> DID_RESET.
+ */
+       ncr_wakeup(np, HS_RESET);
+/*
+ * If the involved command was not in a driver queue, and the 
+ * scsi driver told us reset is synchronous, and the command is not 
+ * currently in the waiting list, complete it with DID_RESET status,
+ * in order to keep it alive.
+ */
+       if (!found && sync_reset && !retrieve_from_waiting_list(0, np, cmd)) {
+               cmd->result = ScsiResult(DID_RESET, 0);
+               cmd->scsi_done(cmd);
+       }
 
        restore_flags(flags);
 
@@ -4221,17 +5198,22 @@ int ncr_reset_bus (Scsi_Cmnd *cmd)
 **
 **==========================================================
 */
-int ncr_abort_command (Scsi_Cmnd *cmd)
+static int ncr_abort_command (Scsi_Cmnd *cmd)
 {
         struct Scsi_Host   *host      = cmd->host;
 /*     Scsi_Device        *device    = cmd->device; */
        struct host_data   *host_data = (struct host_data *) host->hostdata;
-       ncb_p np                      = &host_data->ncb_data;
+       ncb_p np                      = host_data->ncb;
        ccb_p cp;
        u_long flags;
        int found;
        int retv;
 
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+       if (np->stalling == 2)
+               np->stalling = 0;
+#endif
+
        save_flags(flags); cli();
 /*
  * First, look for the scsi command in the waiting list
@@ -4246,7 +5228,7 @@ int ncr_abort_command (Scsi_Cmnd *cmd)
 /*
  * Then, look in the wakeup list
  */
-       for (found=0, cp=&np->ccb; cp; cp=cp->link_ccb) {
+       for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
                /*
                **      look for the ccb of this command.
                */
@@ -4256,14 +5238,23 @@ int ncr_abort_command (Scsi_Cmnd *cmd)
                        break;
                }
        }
+
        if (!found) {
                restore_flags(flags);
                return SCSI_ABORT_NOT_RUNNING;
        }
 
+       if (np->settle_time) {
+               restore_flags(flags);
+               return SCSI_ABORT_SNOOZE;
+       }
+
        /*
        **      Disable reselect.
        **      Remove it from startqueue.
+       **      Set cp->tlimit to 0. The ncr_timeout() handler will use 
+       **      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)) {
@@ -4271,35 +5262,18 @@ int ncr_abort_command (Scsi_Cmnd *cmd)
                cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip);
        }
 
-       switch (cp->host_status) {
-       case HS_BUSY:
-       case HS_NEGOTIATE:
-               /*
-               ** still in start queue ?
-               */
-               if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, skip)) {
-                       retv = SCSI_ABORT_BUSY;
-                       break;
-               }
-       /* fall through */
-       case HS_DISCONNECT:
-               cp->host_status=HS_ABORTED;
-               cp->tag = 0;
-               /*
-               **      wakeup this ccb.
-               */
-               ncr_complete (np, cp);
-               retv = SCSI_ABORT_SUCCESS;
-               break;
-       default:
-               cp->tag = 0;
-               /*
-               **      wakeup this ccb.
-               */
-               ncr_complete (np, cp);
-               retv = SCSI_ABORT_SUCCESS;
-               break;
-       }
+       cp->tlimit = 0;
+       retv = SCSI_ABORT_PENDING;
+
+       /*
+       **      If there are no requests, the script
+       **      processor will sleep on SEL_WAIT_RESEL.
+       **      Let's wake it up, since it may have to work.
+       */
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+       if (!np->stalling)
+#endif
+       OUTB (nc_istat, SIGP);
 
        restore_flags(flags);
 
@@ -4325,7 +5299,6 @@ static int ncr_detach(ncb_p np, int irq)
        lcb_p lp;
        int target, lun;
        int i;
-       u_char scntl3;
 
        printf("%s: releasing host resources\n", ncr_name(np));
 
@@ -4334,7 +5307,7 @@ static int ncr_detach(ncb_p np, int irq)
 **     Set release_stage to 1 and wait that ncr_timeout() set it to 2.
 */
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
        printf("%s: stopping the timer\n", ncr_name(np));
 #endif
        np->release_stage = 1;
@@ -4347,7 +5320,7 @@ static int ncr_detach(ncb_p np, int irq)
 **     Disable chip interrupts
 */
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
        printf("%s: disabling chip interrupts\n", ncr_name(np));
 #endif
        OUTW (nc_sien , 0);
@@ -4357,7 +5330,7 @@ static int ncr_detach(ncb_p np, int irq)
 **     Free irq
 */
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
        printf("%s: freeing irq %d\n", ncr_name(np), irq);
 #endif
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
@@ -4372,28 +5345,40 @@ static int ncr_detach(ncb_p np, int irq)
 
        /*
        **      Reset NCR chip
-       **      Preserve scntl3 for automatic clock detection.
+       **      Restore bios setting for automatic clock detection.
        */
 
        printf("%s: resetting chip\n", ncr_name(np));
-       scntl3 = INB (nc_scntl3);
        OUTB (nc_istat,  SRST);
        DELAY (1000);
        OUTB (nc_istat,  0   );
-       OUTB (nc_scntl3, scntl3);
+
+       OUTB(nc_dmode,  np->sv_dmode);
+       OUTB(nc_dcntl,  np->sv_dcntl);
+       OUTB(nc_ctest3, np->sv_ctest3);
+       OUTB(nc_ctest4, np->sv_ctest4);
+       OUTB(nc_ctest5, np->sv_ctest5);
+       OUTB(nc_gpcntl, np->sv_gpcntl);
+       OUTB(nc_stest2, np->sv_stest2);
+
+       ncr_selectclock(np, np->sv_scntl3);
 
        /*
        **      Release Memory mapped IO region and IO mapped region
        */
 
 #ifndef NCR_IOMAPPED
-#ifdef DEBUG
-       printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg_remapped, 128);
+#ifdef DEBUG_NCR53C8XX
+       printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
+#endif
+       unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
+#ifdef DEBUG_NCR53C8XX
+       printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
 #endif
-       unmap_pci_mem((vm_offset_t) np->reg_remapped, (u_long) 128);
+       unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
 #endif
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
        printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
 #endif
        release_region(np->port, 128);
@@ -4402,13 +5387,13 @@ static int ncr_detach(ncb_p np, int irq)
        **      Free allocated ccb(s)
        */
 
-       while ((cp=np->ccb.link_ccb) != NULL) {
-               np->ccb.link_ccb = cp->link_ccb;
+       while ((cp=np->ccb->link_ccb) != NULL) {
+               np->ccb->link_ccb = cp->link_ccb;
                if (cp->host_status) {
                printf("%s: shall free an active ccb (host_status=%d)\n",
                        ncr_name(np), cp->host_status);
                }
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
        printf("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
 #endif
                m_free(cp, sizeof(*cp));
@@ -4423,7 +5408,7 @@ static int ncr_detach(ncb_p np, int irq)
                for (lun = 0 ; lun < MAX_LUN ; lun++) {
                        lp = tp->lp[lun];
                        if (lp) {
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
        printf("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
 #endif
                                m_free(lp, sizeof(*lp));
@@ -4476,7 +5461,7 @@ void ncr_complete (ncb_p np, ccb_p cp)
        **      timestamp
        **      Optional, spare some CPU time
        */
-#ifdef SCSI_NCR_PROFILE
+#ifdef SCSI_NCR_PROFILE_SUPPORT
        ncb_profile (np, cp);
 #endif
 
@@ -4487,6 +5472,16 @@ void ncr_complete (ncb_p np, ccb_p cp)
        cmd = cp->cmd;
        cp->cmd = NULL;
        tp = &np->target[cmd->target];
+       lp = tp->lp[cmd->lun];
+
+       /*
+       **      We donnot queue more than 1 ccb per target 
+       **      with negotiation at any time. If this ccb was 
+       **      used for negotiation, clear this info in the tcb.
+       */
+
+       if (cp == tp->nego_cp)
+               tp->nego_cp = 0;
 
        /*
        **      Check for parity errors.
@@ -4555,11 +5550,11 @@ void ncr_complete (ncb_p np, ccb_p cp)
                */
                if (cmd->lun == 0 && cmd->cmnd[0] == 0x12) {
                        if (np->unit < SCSI_NCR_MAX_HOST) {
-#ifdef SCSI_NCR_FORCE_SYNC_NEGO
-                               ((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
-#endif
-                               ((char *) cmd->request_buffer)[7] &=
-                               (target_capabilities[np->unit].and_map[cmd->target]);
+                               if (driver_setup.force_sync_nego)
+                                       ((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
+                               else
+                                       ((char *) cmd->request_buffer)[7] &=
+                                       (target_capabilities[np->unit].and_map[cmd->target]);
                        }
                        bcopy ( cmd->request_buffer,
                                &tp->inqdata,
@@ -4568,12 +5563,7 @@ void ncr_complete (ncb_p np, ccb_p cp)
                        /*
                        **      set number of tags
                        */
-                       lp = tp->lp[cmd->lun];
-#ifndef SCSI_NCR_TAGGED_QUEUE_DISABLED
-                       if (lp && !lp->usetags) {
-                               ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
-                       }
-#endif
+                               ncr_setmaxtags (np, tp, driver_setup.default_tags);
                        /*
                        **      prepare negotiation of synch and wide.
                        */
@@ -4585,8 +5575,33 @@ void ncr_complete (ncb_p np, ccb_p cp)
                        tp->quirks |= QUIRK_UPDATE;
                }
 
+               /*
+               **      Announce changes to the generic driver.
+               */
+               if (lp) {
+                       ncr_settags (tp, lp);
+                       if (lp->reqlink != lp->actlink)
+                               ncr_opennings (np, lp, cmd);
+               };
+
                tp->bytes     += cp->data_len;
                tp->transfers ++;
+
+               /*
+               **      If tags was reduced due to queue full,
+               **      increase tags if 100 good status received.
+               */
+               if (tp->numtags < tp->maxtags) {
+                       ++tp->num_good;
+                       if (tp->num_good >= 100) {
+                               tp->num_good = 0;
+                               ++tp->numtags;
+                               if (tp->numtags == 1) {
+                                       PRINT_ADDR(cmd);
+                                       printf("tagged command queueing resumed\n");
+                               }
+                       }
+               }
        } else if ((cp->host_status == HS_COMPLETE)
                && (cp->scsi_status == (S_SENSE|S_GOOD) ||
                    cp->scsi_status == (S_SENSE|S_CHECK_COND))) {
@@ -4612,6 +5627,29 @@ void ncr_complete (ncb_p np, ccb_p cp)
                */
                cmd->result = ScsiResult(DID_OK, cp->scsi_status);
 
+       } else if ((cp->host_status == HS_COMPLETE)
+               && (cp->scsi_status == S_QUEUE_FULL)) {
+
+               /*
+               **   Target is stuffed.
+               */
+               cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+
+               /*
+               **  Suspend tagged queuing and start good status counter.
+               **  Announce changes to the generic driver.
+               */
+               if (tp->numtags) {
+                       PRINT_ADDR(cmd);
+                       printf("QUEUE FULL! suspending tagged command queueing\n");
+                       tp->numtags     = 0;
+                       tp->num_good    = 0;
+                       if (lp) {
+                               ncr_settags (tp, lp);
+                               if (lp->reqlink != lp->actlink)
+                                       ncr_opennings (np, lp, cmd);
+                       };
+               }
        } else if ((cp->host_status == HS_SEL_TIMEOUT)
                || (cp->host_status == HS_TIMEOUT)) {
 
@@ -4680,7 +5718,7 @@ void ncr_complete (ncb_p np, ccb_p cp)
        /*
        **      Free this ccb
        */
-       ncr_free_ccb (np, cp);
+       ncr_free_ccb (np, cp, cmd->target, cmd->lun);
 
        /*
        **      requeue awaiting scsi commands
@@ -4713,7 +5751,7 @@ void ncr_wakeup (ncb_p np, u_long code)
        **      complete all jobs that are not IDLE.
        */
 
-       ccb_p cp = &np->ccb;
+       ccb_p cp = np->ccb;
        while (cp) {
                switch (cp->host_status) {
 
@@ -4751,11 +5789,6 @@ void ncr_wakeup (ncb_p np, u_long code)
 void ncr_init (ncb_p np, char * msg, u_long code)
 {
        int     i;
-       u_long  usrsync;
-       u_char  usrwide;
-#if 0
-       u_char  burstlen;
-#endif
 
        /*
        **      Reset chip.
@@ -4768,7 +5801,7 @@ void ncr_init (ncb_p np, char * msg, u_long code)
        **      Message.
        */
 
-       if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg);
+       if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
 
        /*
        **      Clear Start Queue
@@ -4779,133 +5812,83 @@ void ncr_init (ncb_p np, char * msg, u_long code)
        /*
        **      Start at first entry.
        */
-
        np->squeueput = 0;
-       np->script->startpos[0] = NCB_SCRIPT_PHYS (np, tryloop);
-       np->script->start0  [0] = SCR_INT ^ IFFALSE (0);
+       np->script0->startpos[0] = NCB_SCRIPTH_PHYS (np, tryloop);
+       np->script0->start0  [0] = SCR_INT ^ IFFALSE (0);
 
        /*
        **      Wakeup all pending jobs.
        */
        ncr_wakeup (np, code);
 
-       /*
-       **      Remove Reset, abort ...
-       */
-       OUTB (nc_istat,  0      );
-
        /*
        **      Init chip.
        */
-/**    NCR53C810                       **/
-       if (ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion == 0) {
-               OUTB(nc_dmode, 0x80);   /* Set 8-transfer burst */
-       }
-       else
-/**    NCR53C815                       **/
-       if (ChipDevice == PCI_DEVICE_ID_NCR_53C815) {
-               OUTB(nc_dmode, 0x80);   /* Set 8-transfer burst */
-       }
-       else
-/**    NCR53C825                       **/
-       if (ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion == 0) {
-               OUTB(nc_dmode, 0x80);   /* Set 8-transfer burst */
-       }
-       else
-/**    NCR53C810A or NCR53C860         **/
-       if ((ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion >= 0x10) ||
-           ChipDevice == PCI_DEVICE_ID_NCR_53C860) {
-               OUTB(nc_dmode, 0xc0);   /* Set 16-transfer burst */
-#if 0
-               OUTB(nc_ctest3, 0x01);  /* Set write and invalidate */
-               OUTB(nc_dcntl, 0xa1);   /* Cache line size enable, */
-                                       /* pre-fetch enable and 700 comp */
-#endif
-       }
-       else
-/**    NCR53C825A or NCR53C875         **/
-       if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) ||
-           ChipDevice == PCI_DEVICE_ID_NCR_53C875) {
-               OUTB(nc_dmode, 0xc0);   /* Set 16-transfer burst */
-#if 0
-               OUTB(nc_ctest5, 0x04);  /* Set DMA FIFO to 88 */
-               OUTB(nc_ctest5, 0x24);  /* Set DMA FIFO to 536 */
-               OUTB(nc_dmode, 0x40);   /* Set 64-transfer burst */
-               OUTB(nc_ctest3, 0x01);  /* Set write and invalidate */
-               OUTB(nc_dcntl, 0x81);   /* Cache line size enable and 700 comp*/
-#endif
-       }
-/**    OTHERS                          **/
-       else {
-               OUTB(nc_dmode, 0xc0);   /* Set 16-transfer burst */
-       }
-#if 0
-       burstlen = 0xc0;
-#endif
 
-#ifdef SCSI_NCR_DISABLE_PARITY_CHECK
-       OUTB (nc_scntl0, 0xc0   );      /*  full arb., (no parity)           */
-#else
-       OUTB (nc_scntl0, 0xca   );      /*  full arb., ena parity, par->ATN  */
-#endif
+       OUTB (nc_istat,  0x00   );      /*  Remove Reset, abort */
+       OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+                                       /*  full arb., ena parity, par->ATN  */
+       OUTB (nc_scntl1, 0x00);         /*  odd parity, and remove CRST!! */
 
-       OUTB (nc_scntl1, 0x00   );      /*  odd parity, and remove CRST!!    */
-       OUTB (nc_scntl3, np->rv_scntl3);/*  timing prescaler                 */
-       OUTB (nc_scid  , RRE|np->myaddr);/*  host adapter SCSI address       */
-       OUTW (nc_respid, 1ul<<np->myaddr);/*  id to respond to               */
-       OUTB (nc_istat , SIGP   );      /*  Signal Process                   */
-#if 0
-       OUTB (nc_dmode , burstlen);     /*  Burst length = 2 .. 16 transfers */
-#endif
-       OUTB (nc_dcntl , NOCOM  );      /*  no single step mode, protect SFBR*/
+       ncr_selectclock(np, np->rv_scntl3);     /* Select SCSI clock */
 
-#ifdef SCSI_NCR_DISABLE_MPARITY_CHECK
-       OUTB (nc_ctest4, 0x00   );      /*  disable master parity checking   */
-#else
-       OUTB (nc_ctest4, 0x08   );      /*  enable master parity checking    */
-#endif
+       OUTB (nc_scid  , RRE|np->myaddr);       /* Adapter SCSI address */
+       OUTW (nc_respid, 1ul<<np->myaddr);      /* Id to respond to */
+       OUTB (nc_istat , SIGP   );              /*  Signal Process */
+       OUTB (nc_dmode , np->rv_dmode);         /* Burst length, dma mode */
+       OUTB (nc_ctest5, np->rv_ctest5);        /* Large fifo + large burst */
+
+       OUTB (nc_dcntl , NOCOM|np->rv_dcntl);   /* Protect SFBR */
+       OUTB (nc_ctest3, np->rv_ctest3);        /* Write and invalidate */
+       OUTB (nc_ctest4, np->rv_ctest4);        /* Master parity checking */
 
-       OUTB (nc_stest2, EXT    );      /*  Extended Sreq/Sack filtering     */
-       OUTB (nc_stest3, TE     );      /*  TolerANT enable                  */
-       OUTB (nc_stime0, 0x0d   );      /*  HTH = disable  STO = 0.4 sec.    */
-                                       /*  0.25 sec recommended for scsi 1  */
+       OUTB (nc_stest2, EXT|np->rv_stest2);    /* Extended Sreq/Sack filtering */
+       OUTB (nc_stest3, TE);                   /* TolerANT enable */
+       OUTB (nc_stime0, 0x0d   );              /* HTH disabled  STO 0.4 sec. */
 
        /*
-       **      Reinitialize usrsync.
-       **      Have to renegotiate synch mode.
+       **      Disable disconnects.
        */
 
-       usrsync = 255;
-
-#ifndef SCSI_NCR_FORCE_ASYNCHRONOUS
-       if (SCSI_NCR_MAX_SYNC) {
-               u_long period;
-               period =1000000/SCSI_NCR_MAX_SYNC; /* ns = 10e6 / kHz */
-               if (period <= 11 * np->ns_sync) {
-                       if (period < 4 * np->ns_sync)
-                               usrsync = np->ns_sync;
-                       else
-                               usrsync = period / 4;
-               };
-       };
-#endif
+       np->disc = 0;
 
        /*
-       **      Reinitialize usrwide.
-       **      Have to renegotiate wide mode.
+       **    Enable GPIO0 pin for writing if LED support.
        */
 
-       usrwide = (SCSI_NCR_MAX_WIDE);
-       if (usrwide > np->maxwide) usrwide=np->maxwide;
+       if (np->features & FE_LED0) {
+               OUTOFFB (nc_gpcntl, 0x01);
+       }
 
        /*
-       **      Disable disconnects.
+       **    Upload the script into on-board RAM
        */
+       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));
+       }
 
-       np->disc = 0;
+       /*
+       **      enable ints
+       */
+
+       OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
+       OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
+
+       /*
+       **      For 895/6 enable SBMC interrupt and save current SCSI bus mode.
+       */
+       if (np->features & FE_ULTRA2) {
+               OUTONW (nc_sien, SBMC);
+               np->scsi_mode = INB (nc_stest4) & SMODE;
+       }
 
        /*
        **      Fill in target structure.
+       **      Reinitialize usrsync.
+       **      Reinitialize usrwide.
+       **      Prepare sync negotiation according to actual SCSI bus mode.
        */
 
        for (i=0;i<MAX_TARGET;i++) {
@@ -4914,19 +5897,22 @@ void ncr_init (ncb_p np, char * msg, u_long code)
                tp->sval    = 0;
                tp->wval    = np->rv_scntl3;
 
-               tp->usrsync = usrsync;
-               tp->usrwide = usrwide;
+               if (tp->usrsync != 255) {
+                       if (tp->usrsync <= np->maxsync) {
+                               if (tp->usrsync < np->minsync) {
+                                       tp->usrsync = np->minsync;
+                               }
+                       }
+                       else
+                               tp->usrsync = 255;
+               };
+
+               if (tp->usrwide > np->maxwide)
+                       tp->usrwide = np->maxwide;
 
                ncr_negotiate (np, tp);
        }
 
-       /*
-       **      enable ints
-       */
-
-       OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
-       OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
-
        /*
        **    Start script processor.
        */
@@ -4950,7 +5936,13 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp)
 
        u_long minsync = tp->usrsync;
 
-       if (minsync < 25) minsync=25;
+       /*
+       **      SCSI bus mode limit
+       */
+
+       if (np->scsi_mode && np->scsi_mode == SMODE_SE) {
+               if (minsync < 12) minsync = 12;
+       }
 
        /*
        **      if not scsi 2
@@ -4964,18 +5956,18 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp)
        **      our limit ..
        */
 
-       if (minsync < np->ns_sync)
-               minsync = np->ns_sync;
+       if (minsync < np->minsync)
+               minsync = np->minsync;
 
        /*
        **      divider limit
        */
 
-       if (minsync > (np->ns_sync * 11) / 4)
+       if (minsync > np->maxsync)
                minsync = 255;
 
        tp->minsync = minsync;
-       tp->maxoffs = (minsync<255 ? 8 : 0);
+       tp->maxoffs = (minsync<255 ? np->maxoffs : 0);
 
        /*
        **      period=0: has to negotiate sync transfer
@@ -4989,6 +5981,109 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp)
        tp->widedone=0;
 }
 
+/*==========================================================
+**
+**     Get clock factor and sync divisor for a given 
+**     synchronous factor period.
+**     Returns the clock factor (in sxfer) and scntl3 
+**     synchronous divisor field.
+**
+**==========================================================
+*/
+
+static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p)
+{
+       u_long  clk = np->clock_khz;    /* SCSI clock frequency in kHz  */
+       int     div = np->clock_divn;   /* Number of divisors supported */
+       u_long  fak;                    /* Sync factor in sxfer         */
+       u_long  per;                    /* Period in tenths of ns       */
+       u_long  kpc;                    /* (per * clk)                  */
+
+       /*
+       **      Compute the synchronous period in tenths of nano-seconds
+       */
+       if      (sfac <= 10)    per = 250;
+       else if (sfac == 11)    per = 303;
+       else if (sfac == 12)    per = 500;
+       else                    per = 40 * sfac;
+
+       /*
+       **      Look for the greatest clock divisor that allows an 
+       **      input speed faster than the period.
+       */
+       kpc = per * clk;
+       while (--div >= 0)
+               if (kpc >= (div_10M[div] << 2)) break;
+
+       /*
+       **      Calculate the lowest clock factor that allows an output 
+       **      speed not faster than the period.
+       */
+       fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0  /* This optimization does not seem very usefull */
+
+       per = (fak * div_10M[div]) / clk;
+
+       /*
+       **      Why not to try the immediate lower divisor and to choose 
+       **      the one that allows the fastest output speed ?
+       **      We dont want input speed too much greater than output speed.
+       */
+       if (div >= 1 && fak < 8) {
+               u_long fak2, per2;
+               fak2 = (kpc - 1) / div_10M[div-1] + 1;
+               per2 = (fak2 * div_10M[div-1]) / clk;
+               if (per2 < per && fak2 <= 8) {
+                       fak = fak2;
+                       per = per2;
+                       --div;
+               }
+       }
+#endif
+
+       if (fak < 4) fak = 4;   /* Should never happen, too bad ... */
+
+       /*
+       **      Compute and return sync parameters for the ncr
+       */
+       *fakp           = fak - 4;
+       *scntl3p        = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
+}
+
+
+/*==========================================================
+**
+**     Set actual values, sync status and patch all ccbs of 
+**     a target according to new sync/wide agreement.
+**
+**==========================================================
+*/
+
+static void ncr_set_sync_wide_status (ncb_p np, u_char target)
+{
+       ccb_p cp;
+       tcb_p tp = &np->target[target];
+
+       /*
+       **      set actual value and sync_status
+       */
+       OUTB (nc_sxfer, tp->sval);
+       np->sync_st = tp->sval;
+       OUTB (nc_scntl3, tp->wval);
+       np->wide_st = tp->wval;
+
+       /*
+       **      patch ALL ccbs of this target.
+       */
+       for (cp = np->ccb; cp; cp = cp->link_ccb) {
+               if (!cp->cmd) continue;
+               if (cp->cmd->target != target) continue;
+               cp->sync_status = tp->sval;
+               cp->wide_status = tp->wval;
+       };
+}
+
 /*==========================================================
 **
 **     Switch sync mode for current job and it's target
@@ -4996,11 +6091,12 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp)
 **==========================================================
 */
 
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer)
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
 {
        Scsi_Cmnd *cmd;
        tcb_p tp;
-       u_char target = INB (nc_ctest0)&7;
+       u_char target = INB (nc_ctest0) & 0x0f;
+       u_char idiv;
 
        assert (cp);
        if (!cp) return;
@@ -5011,57 +6107,81 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer)
        assert (target == (cmd->target & 0xf));
 
        tp = &np->target[target];
-       tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff;
 
-       if (tp->sval == sxfer) return;
+       if (!scntl3 || !(sxfer & 0x1f))
+               scntl3 = np->rv_scntl3;
+       scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07);
+
+       /*
+       **      Deduce the value of controller sync period from scntl3.
+       **      period is in tenths of nano-seconds.
+       */
+
+       idiv = ((scntl3 >> 4) & 0x7);
+       if ((sxfer & 0x1f) && idiv)
+               tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+       else
+               tp->period = 0xffff;
+
+       /*
+       **       Stop there if sync parameters are unchanged
+       */
+       if (tp->sval == sxfer && tp->wval == scntl3) return;
        tp->sval = sxfer;
+       tp->wval = scntl3;
 
        /*
        **      Bells and whistles   ;-)
        */
        PRINT_ADDR(cmd);
-       if (sxfer & 0x0f) {
+       if (sxfer & 0x01f) {
+               unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
+               unsigned mb10 = (f10 + tp->period/2) / tp->period;
+               char *scsi;
+
                /*
                **  Disable extended Sreq/Sack filtering
                */
-               if (tp->period <= 200) OUTB (nc_stest2, 0);
+               if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
+
+               /*
+               **      Bells and whistles   ;-)
+               */
+               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";
 
-               printf ("%s%dns (%d Mb/sec) offset %d.\n",
-                       tp->period<200 ? "FAST SCSI-2 ":"",
-                       tp->period,
-                       (((tp->wval & EWS)? 2:1)*1000+tp->period/2)/tp->period,
-                       sxfer & 0x0f);
-       } else  printf ("asynchronous.\n");
+               printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
+                       tp->widedone > 1 ? "WIDE " : "",
+                       mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f);
+       } else
+               printf ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
 
        /*
        **      set actual value and sync_status
-       */
-       OUTB (nc_sxfer, sxfer);
-       np->sync_st = sxfer;
-
-       /*
        **      patch ALL ccbs of this target.
        */
-       for (cp = &np->ccb; cp; cp = cp->link_ccb) {
-               if (!cp->cmd) continue;
-               if (cp->cmd->target != target) continue;
-               cp->sync_status = sxfer;
-       };
+       ncr_set_sync_wide_status(np, target);
 }
 
 /*==========================================================
 **
 **     Switch wide mode for current job and it's target
+**     SCSI specs say: a SCSI device that accepts a WDTR 
+**     message shall reset the synchronous agreement to 
+**     asynchronous mode.
 **
 **==========================================================
 */
 
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack)
 {
        Scsi_Cmnd *cmd;
-       u_short target = INB (nc_ctest0)&7;
+       u_short target = INB (nc_ctest0) & 0x0f;
        tcb_p tp;
-       u_char  scntl3 = np->rv_scntl3 | (wide ? EWS : 0);
+       u_char  scntl3;
+       u_char  sxfer;
 
        assert (cp);
        if (!cp) return;
@@ -5073,32 +6193,33 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
 
        tp = &np->target[target];
        tp->widedone  =  wide+1;
-       if (tp->wval == scntl3) return;
-       tp->wval = scntl3;
+       scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
+
+       sxfer = ack ? 0 : tp->sval;
 
        /*
-       **      Bells and whistles   ;-)
+       **       Stop there if sync/wide parameters are unchanged
        */
-       PRINT_ADDR(cmd);
-       if (scntl3 & EWS)
-               printf ("WIDE SCSI (16 bit) enabled.\n");
-       else
-               printf ("WIDE SCSI disabled.\n");
+       if (tp->sval == sxfer && tp->wval == scntl3) return;
+       tp->sval = sxfer;
+       tp->wval = scntl3;
 
        /*
-       **      set actual value and sync_status
+       **      Bells and whistles   ;-)
        */
-       OUTB (nc_scntl3, scntl3);
-       np->wide_st = scntl3;
+       if (bootverbose >= 2) {
+               PRINT_ADDR(cmd);
+               if (scntl3 & EWS)
+                       printf ("WIDE SCSI (16 bit) enabled.\n");
+               else
+                       printf ("WIDE SCSI disabled.\n");
+       }
 
        /*
+       **      set actual value and sync_status
        **      patch ALL ccbs of this target.
        */
-       for (cp = &np->ccb; cp; cp = cp->link_ccb) {
-               if (!cp->cmd) continue;
-               if (cp->cmd->target != target) continue;
-               cp->wide_status = scntl3;
-       };
+       ncr_set_sync_wide_status(np, target);
 }
 
 /*==========================================================
@@ -5108,19 +6229,32 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
 **==========================================================
 */
 
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags)
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags)
 {
        int l;
-       tp->usrtags = usrtags;
+       if (numtags > tp->usrtags)
+               numtags = tp->usrtags;
+       tp->numtags = numtags;
+       tp->maxtags = numtags;
+
        for (l=0; l<MAX_LUN; l++) {
                lcb_p lp;
+               u_char wastags;
+
                if (!tp) break;
                lp=tp->lp[l];
                if (!lp) continue;
+
+               wastags = lp->usetags;
                ncr_settags (tp, lp);
-               if (lp->usetags > 0) {
+
+               if (numtags > 1 && lp->reqccbs > 1) {
                        PRINT_LUN(np, tp - np->target, l);
-                       printf("using tagged command queueing, up to %d cmds/lun\n", lp->usetags);
+                       printf("using tagged command queueing, up to %ld cmds/lun\n", numtags);
+               }
+               else if (numtags <= 1 && wastags) {
+                       PRINT_LUN(np, tp - np->target, l);
+                       printf("disabling tagged command queueing\n");
                }
        };
 }
@@ -5139,8 +6273,8 @@ static void ncr_settags (tcb_p tp, lcb_p lp)
        */
        if ((  tp->inqdata[2] & 0x7) >= 2 &&
            (  tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
-               && tp->usrtags) {
-               reqtags = tp->usrtags;
+               && tp->numtags > 1) {
+               reqtags = tp->numtags;
                if (lp->actlink <= 1)
                        lp->usetags=reqtags;
        } else {
@@ -5171,7 +6305,7 @@ static void ncr_settags (tcb_p tp, lcb_p lp)
 **----------------------------------------------------
 */
 
-#ifdef SCSI_NCR_USER_COMMAND
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
 
 static void ncr_usercmd (ncb_p np)
 {
@@ -5196,13 +6330,13 @@ static void ncr_usercmd (ncb_p np)
                        np->user.data = SCSI_NCR_MAX_TAGS;
                for (t=0; t<MAX_TARGET; t++) {
                        if (!((np->user.target>>t)&1)) continue;
+                       np->target[t].usrtags = np->user.data;
                        ncr_setmaxtags (np, &np->target[t], np->user.data);
                };
-               np->disc = 1;
                break;
 
        case UC_SETDEBUG:
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
                ncr_debug = np->user.data;
 #endif
                break;
@@ -5234,12 +6368,106 @@ static void ncr_usercmd (ncb_p np)
        case UC_CLEARPROF:
                bzero(&np->profile, sizeof(np->profile));
                break;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+       case UC_DEBUG_ERROR_RECOVERY:
+               np->debug_error_recovery = np->user.data;
+               break;
+#endif
        }
        np->user.cmd=0;
 }
 #endif
 
 
+/*=====================================================================
+**
+**    Embedded error recovery debugging code.
+**
+**=====================================================================
+**
+**    This code is conditionned by SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT.
+**    It only can be enabled after boot-up with a control command.
+**
+**    Every 30 seconds the timer handler of the driver decides to 
+**    change the behaviour of the driver in order to trigger errors.
+**
+**    If last command was "debug_error_recovery sge", the driver 
+**    sets sync offset of all targets that use sync transfers to 2, 
+**    and so hopes a SCSI gross error at the next read operation.
+**
+**    If last command was "debug_error_recovery abort", the driver 
+**    does not signal new scsi commands to the script processor, until 
+**    it is asked to abort or reset a command by the mid-level driver.
+**
+**    If last command was "debug_error_recovery reset", the driver 
+**    does not signal new scsi commands to the script processor, until 
+**    it is asked to reset a command by the mid-level driver.
+**
+**    If last command was "debug_error_recovery parity", the driver 
+**    will assert ATN on the next DATA IN phase mismatch, and so will 
+**    behave as if a parity error had been detected.
+**
+**    The command "debug_error_recovery none" makes the driver behave 
+**    normaly.
+**
+**=====================================================================
+*/
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+static void ncr_trigger_errors (ncb_p np)
+{
+       /*
+       **      If np->debug_error_recovery is not zero, we want to 
+       **      simulate common errors in order to test error recovery.
+       */
+       do {
+               static u_long last = 0l;
+
+               if (!np->debug_error_recovery)
+                       break;
+               if (!last)
+                       last = jiffies;
+               else if (jiffies < last + 30*HZ)
+                       break;
+               last = jiffies;
+               /*
+                * This one triggers SCSI gross errors.
+                */
+               if (np->debug_error_recovery == 1) {
+                       int i;
+                       printf("%s: testing error recovery from SCSI gross error...\n", ncr_name(np));
+                       for (i = 0 ; i < MAX_TARGET ; i++) {
+                               if (np->target[i].sval & 0x1f) {
+                                       np->target[i].sval &= ~0x1f;
+                                       np->target[i].sval += 2;
+                               }
+                       }
+               }
+               /*
+                * This one triggers abort from the mid-level driver.
+                */
+               else if (np->debug_error_recovery == 2) {
+                       printf("%s: testing error recovery from mid-level driver abort()...\n", ncr_name(np));
+                       np->stalling = 2;
+               }
+               /*
+                * This one triggers reset from the mid-level driver.
+                */
+               else if (np->debug_error_recovery == 3) {
+                       printf("%s: testing error recovery from mid-level driver reset()...\n", ncr_name(np));
+                       np->stalling = 3;
+               }
+               /*
+                * This one set ATN on phase mismatch in DATA IN phase and so 
+                * will behave as on scsi parity error detected.
+                */
+               else if (np->debug_error_recovery == 4) {
+                       printf("%s: testing data in parity error...\n", ncr_name(np));
+                       np->assert_atn = 1;
+               }
+       } while (0);
+}
+#endif
 
 /*==========================================================
 **
@@ -5267,7 +6495,7 @@ static void ncr_timeout (ncb_p np)
        **      If release process in progress, let's go
        **      Set the release stage from 1 to 2 to synchronize
        **      with the release process.
-       **/
+       */
 
        if (np->release_stage) {
                if (np->release_stage == 1) np->release_stage = 2;
@@ -5282,7 +6510,33 @@ static void ncr_timeout (ncb_p np)
 
        add_timer(&np->timer);
 
-       if (np->lasttime + HZ < thistime) {
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+       ncr_trigger_errors (np);
+#endif
+
+       /*
+       **      If we are resetting the ncr, wait for settle_time before 
+       **      clearing it. Then command processing will be resumed.
+       */
+       if (np->settle_time) {
+               if (np->settle_time <= thistime) {
+                       if (bootverbose > 1)
+                               printf("%s: command processing resumed\n", ncr_name(np));
+                       save_flags(flags); cli();
+                       np->settle_time = 0;
+                       np->disc        = 1;
+                       requeue_waiting_list(np);
+                       restore_flags(flags);
+               }
+               return;
+       }
+
+       /*
+       **      Since the generic scsi driver only allows us 0.5 second 
+       **      to perform abort of a command, we must look at ccbs about 
+       **      every 0.25 second.
+       */
+       if (np->lasttime + (HZ>>2) <= thistime) {
                /*
                **      block ncr interrupts
                */
@@ -5311,44 +6565,15 @@ static void ncr_timeout (ncb_p np)
                t = (thistime - np->heartbeat) / HZ;
 
                if (t<2) np->latetime=0; else np->latetime++;
-               if (np->latetime>5) {
-                       /*
-                       **      If there are no requests, the script
-                       **      processor will sleep on SEL_WAIT_RESEL.
-                       **      But we have to check whether it died.
-                       **      Let's wake it up.
-                       */
-                       OUTB (nc_istat, SIGP);
-               }
-               if (np->latetime>10) {
-                       /*
-                       **      Although we tried to wake it up,
-                       **      the script processor didn't respond.
-                       **
-                       **      May be a target is hanging,
-                       **      or another initator lets a tape device
-                       **      rewind with disconnect disabled :-(
-                       **
-                       **      We won't accept that.
-                       */
-                       if (INB (nc_sbcl) & CBSY)
-                               OUTB (nc_scntl1, CRST);
-                       DELAY (1000);
-                       ncr_init (np, "ncr dead ?", HS_TIMEOUT);
-#ifndef SCSI_NCR_NO_DISCONNECT
-                       np->disc = 1;
-#endif
-                       np->heartbeat = thistime;
-               }
 
                /*----------------------------------------------------
                **
-               **      should handle ccb timeouts
-               **      Let the middle scsi driver manage timeouts.
+               **      handle ccb timeouts
+               **
                **----------------------------------------------------
                */
 
-               for (cp=&np->ccb; cp; cp=cp->link_ccb) {
+               for (cp=np->ccb; cp; cp=cp->link_ccb) {
                        /*
                        **      look for timed out ccbs.
                        */
@@ -5357,7 +6582,7 @@ static void ncr_timeout (ncb_p np)
                        /*
                        **      Have to force ordered tag to avoid timeouts
                        */
-                       if (cp->cmd && cp->tlimit <= 
+                       if (cp->cmd && cp->tlimit && cp->tlimit <= 
                                thistime + NCR_TIMEOUT_INCREASE + SCSI_NCR_TIMEOUT_ALERT) {
                                lcb_p lp;
                                lp = np->target[cp->cmd->target].lp[cp->cmd->lun];
@@ -5365,24 +6590,14 @@ static void ncr_timeout (ncb_p np)
                                        lp->force_ordered_tag = 1;
                                }
                        }
-/*
-**     Let the middle scsi driver manage timeouts
-*/
-#if 0
-                       if (cp->tlimit > thistime) continue;
-
                        /*
-                       **      Disable reselect.
-                       **      Remove it from startqueue.
+                       **      ncr_abort_command() cannot complete canceled 
+                       **      commands immediately. It sets tlimit to zero 
+                       **      and ask the script to skip the scsi process if 
+                       **      necessary. We have to complete this work here.
                        */
-                       cp->jump_ccb.l_cmd = (SCR_JUMP);
-                       if (cp->phys.header.launch.l_paddr ==
-                               NCB_SCRIPT_PHYS (np, select)) {
-                               printf ("%s: timeout ccb=%p (skip)\n",
-                                       ncr_name (np), cp);
-                               cp->phys.header.launch.l_paddr
-                               = NCB_SCRIPT_PHYS (np, skip);
-                       };
+
+                       if (cp->tlimit) continue;
 
                        switch (cp->host_status) {
 
@@ -5397,7 +6612,7 @@ static void ncr_timeout (ncb_p np)
 
                                /* fall through */
                        case HS_DISCONNECT:
-                               cp->host_status=HS_TIMEOUT;
+                               cp->host_status=HS_ABORTED;
                        };
                        cp->tag = 0;
 
@@ -5405,7 +6620,11 @@ static void ncr_timeout (ncb_p np)
                        **      wakeup this ccb.
                        */
                        ncr_complete (np, cp);
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+                       if (!np->stalling)
 #endif
+                       OUTB (nc_istat, SIGP);
                }
                restore_flags(flags);
        }
@@ -5422,46 +6641,149 @@ static void ncr_timeout (ncb_p np)
                if (DEBUG_FLAGS & DEBUG_TINY) printf ("}");
                restore_flags(flags);
        }
-#endif /* SCSI_NCR_BROKEN_INTR */
+#endif /* SCSI_NCR_BROKEN_INTR */
+}
+
+/*==========================================================
+**
+**     log message for real hard errors
+**
+**     "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)."
+**     "             reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
+**
+**     exception register:
+**             ds:     dstat
+**             si:     sist
+**
+**     SCSI bus lines:
+**             so:     control lines as driver by NCR.
+**             si:     control lines as seen by NCR.
+**             sd:     scsi data lines as seen by NCR.
+**
+**     wide/fastmode:
+**             sxfer:  (see the manual)
+**             scntl3: (see the manual)
+**
+**     current script command:
+**             dsp:    script adress (relative to start of script).
+**             dbc:    first word of script command.
+**
+**     First 16 register of the chip:
+**             r0..rf
+**
+**==========================================================
+*/
+
+static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
+{
+       u_int32 dsp;
+       int     script_ofs;
+       int     script_size;
+       char    *script_name;
+       u_char  *script_base;
+       int     i;
+
+       dsp     = INL (nc_dsp);
+
+       if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
+               script_ofs      = dsp - np->p_script;
+               script_size     = sizeof(struct script);
+               script_base     = (u_char *) np->script;
+               script_name     = "script";
+       }
+       else {
+               script_ofs      = dsp - np->p_scripth;
+               script_size     = sizeof(struct scripth);
+               script_base     = (u_char *) np->scripth;
+               script_name     = "scripth";
+       }
+
+       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,
+               (unsigned)INL (nc_dbc));
+
+       if (((script_ofs & 3) == 0) &&
+           (unsigned)script_ofs < script_size) {
+               printf ("%s: script cmd = %08x\n", ncr_name(np),
+                       (int) *(ncrcmd *)(script_base + script_ofs));
+       }
+
+        printf ("%s: regdump:", ncr_name(np));
+        for (i=0; i<16;i++)
+            printf (" %02x", (unsigned)INB_OFF(i));
+        printf (".\n");
 }
 
-/*==========================================================
-**
+/*============================================================
 **
 **     ncr chip exception handler.
 **
+**============================================================
 **
-**==========================================================
+**     In normal cases, interrupt conditions occur one at a 
+**     time. The ncr is able to stack in some extra registers 
+**     other interrupts that will occurs after the first one.
+**     But severall interrupts may occur at the same time.
+**
+**     We probably should only try to deal with the normal 
+**     case, but it seems that multiple interrupts occur in 
+**     some cases that are not abnormal at all.
+**
+**     The most frequent interrupt condition is Phase Mismatch.
+**     We should want to service this interrupt quickly.
+**     A SCSI parity error may be delivered at the same time.
+**     The SIR interrupt is not very frequent in this driver, 
+**     since the INTFLY is likely used for command completion 
+**     signaling.
+**     The Selection Timeout interrupt may be triggered with 
+**     IID and/or UDC.
+**     The SBMC interrupt (SCSI Bus Mode Change) may probably 
+**     occur at any time.
+**
+**     This handler try to deal as cleverly as possible with all
+**     the above.
+**
+**============================================================
 */
 
 void ncr_exception (ncb_p np)
 {
        u_char  istat, dstat;
        u_short sist;
-       u_int32 dsp, dsa;
-       int     script_ofs;
        int     i;
 
        /*
        **      interrupt on the fly ?
        */
        while ((istat = INB (nc_istat)) & INTF) {
-               if (DEBUG_FLAGS & DEBUG_TINY) printf ("F");
+               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);
        };
 
-       if (!(istat & (SIP|DIP))) return;
+       if (!(istat & (SIP|DIP)))
+               return;
+
+       np->profile.num_int++;
+
+       if (istat & CABRT)
+               OUTB (nc_istat, CABRT);
 
        /*
        **      Steinbach's Guideline for Systems Programming:
        **      Never test for an error condition you don't know how to handle.
        */
 
-       dstat = (istat & DIP) ? INB (nc_dstat) : 0;
        sist  = (istat & SIP) ? INW (nc_sist)  : 0;
-       np->profile.num_int++;
+       dstat = (istat & DIP) ? INB (nc_dstat) : 0;
 
        if (DEBUG_FLAGS & DEBUG_TINY)
                printf ("<%d|%x:%x|%x:%x>",
@@ -5469,297 +6791,121 @@ void ncr_exception (ncb_p np)
                        dstat,sist,
                        (unsigned)INL(nc_dsp),
                        (unsigned)INL(nc_dbc));
-       if ((dstat==DFE) && (sist==PAR)) return;
-
-/*==========================================================
-**
-**     First the normal cases.
-**
-**==========================================================
-*/
-       /*-------------------------------------------
-       **      SCSI reset
-       **-------------------------------------------
-       */
-
-       if (sist & RST) {
-               ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET);
-               return;
-       };
 
-       /*-------------------------------------------
-       **      selection timeout
+       /*========================================================
+       **      First, interrupts we want to service cleanly.
        **
-       **      IID excluded from dstat mask!
-       **      (chip bug)
-       **-------------------------------------------
-       */
-
-       if ((sist  & STO) &&
-               !(sist  & (GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-               !(dstat & (MDPE|BF|ABRT|SIR))) {
-               ncr_int_sto (np);
-               return;
-       };
-
-       /*-------------------------------------------
-       **      Phase mismatch.
-       **-------------------------------------------
-       */
-
-       if ((sist  & MA) &&
-               !(sist  & (STO|GEN|HTH|SGE|UDC|RST|PAR)) &&
-               !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
-               ncr_int_ma (np);
+       **      Phase mismatch is the most frequent interrupt, and 
+       **      so we have to service it as quickly and as cleanly 
+       **      as possible.
+       **      Programmed interrupts are rarely used in this driver,
+       **      but we must handle them cleanly anyway.
+       **      We try to deal with PAR and SBMC combined with 
+       **      some other interrupt(s).
+       **=========================================================
+       */
+
+       if (!(sist  & (STO|GEN|HTH|SGE|UDC|RST)) &&
+           !(dstat & (MDPE|BF|ABRT|IID))) {
+               if ((sist & SBMC) && ncr_int_sbmc (np))
+                       return;
+               if ((sist & PAR)  && ncr_int_par  (np))
+                       return;
+               if (sist & MA) {
+                       ncr_int_ma (np);
+                       return;
+               }
+               if (dstat & SIR) {
+                       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);
+               OUTONB (nc_dcntl, (STD|NOCOM));
                return;
        };
 
-       /*----------------------------------------
-       **      move command with length 0
-       **----------------------------------------
+       /*========================================================
+       **      Now, interrupts that need some fixing up.
+       **      Order and multiple interrupts is so less important.
+       **
+       **      If SRST has been asserted, we just reset the chip.
+       **
+       **      Selection is intirely handled by the chip. If the 
+       **      chip says STO, we trust it. Seems some other 
+       **      interrupts may occur at the same time (UDC, IID), so 
+       **      we ignore them. In any case we do enough fix-up 
+       **      in the service routine.
+       **      We just exclude some fatal dma errors.
+       **=========================================================
        */
 
-       if ((dstat & IID) &&
-               !(sist  & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-               !(dstat & (MDPE|BF|ABRT|SIR)) &&
-               ((INL(nc_dbc) & 0xf8000000) == SCR_MOVE_TBL)) {
-               /*
-               **      Target wants more data than available.
-               **      The "no_data" script will do it.
-               */
-               OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, no_data));
+       if (sist & RST) {
+               ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET);
                return;
        };
 
-       /*-------------------------------------------
-       **      Programmed interrupt
-       **-------------------------------------------
-       */
-
-       if ((dstat & SIR) &&
-               !(sist  & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-               !(dstat & (MDPE|BF|ABRT|IID)) &&
-               (INB(nc_dsps) <= SIR_MAX)) {
-               ncr_int_sir (np);
+       if ((sist & STO) &&
+               !(dstat & (MDPE|BF|ABRT))) {
+               ncr_int_sto (np);
                return;
        };
 
-       /*========================================
-       **      do the register dump
-       **========================================
+       /*=========================================================
+       **      Now, interrupts we are not able to recover cleanly.
+       **      (At least for the moment).
+       **
+       **      Do the register dump.
+       **      Log message for real hard errors.
+       **      Clear all fifos.
+       **      For MDPE, BF, ABORT, IID, SGE and HTH we reset the 
+       **      BUS and the chip.
+       **      We are more soft for UDC.
+       **=========================================================
        */
        if (jiffies - np->regtime > 10*HZ) {
-               int i;
                np->regtime = jiffies;
-               for (i=0; i<sizeof(np->regdump); i++)
+               for (i = 0; i<sizeof(np->regdump); i++)
                        ((char*)&np->regdump)[i] = INB_OFF(i);
                np->regdump.nc_dstat = dstat;
                np->regdump.nc_sist  = sist;
        };
 
-       /*=========================================
-       **      log message for real hard errors
-       **=========================================
-
-       "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ (dsp:dbc)."
-       "             reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
-
-       exception register:
-               ds:     dstat
-               si:     sist
-
-       SCSI bus lines:
-               so:     control lines as driver by NCR.
-               si:     control lines as seen by NCR.
-               sd:     scsi data lines as seen by NCR.
-
-       wide/fastmode:
-               sxfer:  (see the manual)
-               scntl3: (see the manual)
+       ncr_log_hard_error(np, sist, dstat);
 
-       current script command:
-               dsp:    script adress (relative to start of script).
-               dbc:    first word of script command.
+       printf ("%s: have to clear fifos.\n", ncr_name (np));
+       OUTB (nc_stest3, TE|CSF);
+       OUTONB (nc_ctest3, CLF);
 
-       First 16 register of the chip:
-               r0..rf
-
-       =============================================
-       */
-
-       dsp = (unsigned) INL (nc_dsp);
-       dsa = (unsigned) INL (nc_dsa);
-
-       script_ofs = dsp - np->p_script;
-
-       printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%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_ofs,
-               (unsigned) INL (nc_dbc));
-
-       if (((script_ofs & 3) == 0) &&
-           (unsigned)script_ofs < sizeof(struct script)) {
-               printf ("\tscript cmd = %08x\n", 
-                       (int) *(ncrcmd *)((char*)np->script +script_ofs));
-       }
-
-        printf ("\treg:\t");
-        for (i=0; i<16;i++)
-            printf (" %02x", (unsigned)INB_OFF(i));
-        printf (".\n");
-
-       /*----------------------------------------
-       **      clean up the dma fifo
-       **----------------------------------------
-       */
-
-       if ( (INB(nc_sstat0) & (ILF|ORF|OLF)   ) ||
-            (INB(nc_sstat1) & (FF3210) ) ||
-            (INB(nc_sstat2) & (ILF1|ORF1|OLF1)) ||     /* wide .. */
-            !(dstat & DFE)) {
-               printf ("%s: have to clear fifos.\n", ncr_name (np));
-               OUTB (nc_stest3, TE|CSF);       /* clear scsi fifo */
-               OUTB (nc_ctest3, CLF);          /* clear dma fifo  */
-       }
-
-       /*----------------------------------------
-       **      handshake timeout
-       **----------------------------------------
-       */
+       if ((sist & (SGE)) ||
+               (dstat & (MDPE|BF|ABORT|IID))) {
+               ncr_start_reset(np, 2);
+               return;
+       };
 
        if (sist & HTH) {
                printf ("%s: handshake timeout\n", ncr_name(np));
-               OUTB (nc_scntl1, CRST);
-               DELAY (1000);
-               OUTB (nc_scntl1, 0x00);
-               OUTB (nc_scr0, HS_FAIL);
-               OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
-               return;
-       }
-
-       /*----------------------------------------
-       **      unexpected disconnect
-       **----------------------------------------
-       */
-
-       if ((sist  & UDC) &&
-               !(sist  & (STO|GEN|HTH|MA|SGE|RST|PAR)) &&
-               !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
-               OUTB (nc_scr0, HS_UNEXPECTED);
-               OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
+               ncr_start_reset(np, 2);
                return;
        };
 
-       /*----------------------------------------
-       **      cannot disconnect
-       **----------------------------------------
-       */
-
-       if ((dstat & IID) &&
-               !(sist  & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-               !(dstat & (MDPE|BF|ABRT|SIR)) &&
-               ((INL(nc_dbc) & 0xf8000000) == SCR_WAIT_DISC)) {
-               /*
-               **      Unexpected data cycle while waiting for disconnect.
-               */
-               if (INB(nc_sstat2) & LDSC) {
-                       /*
-                       **      It's an early reconnect.
-                       **      Let's continue ...
-                       */
-                       OUTB (nc_dcntl, (STD|NOCOM));
-                       /*
-                       **      info message
-                       */
-                       printf ("%s: INFO: LDSC while IID.\n",
-                               ncr_name (np));
-                       return;
+       if (sist & UDC) {
+               printf ("%s: unexpected disconnect\n", ncr_name(np));
+               if (INB (nc_scr1) != 0xff) {
+                       OUTB (nc_scr1, HS_UNEXPECTED);
+                       OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
                };
-               printf ("%s: target %d doesn't release the bus.\n",
-                       ncr_name (np), (int)INB (nc_ctest0)&0x0f);
-               /*
-               **      return without restarting the NCR.
-               **      timeout will do the real work.
-               */
-               return;
-       };
-
-       /*----------------------------------------
-       **      single step
-       **----------------------------------------
-       */
-
-       if ((dstat & SSI) &&
-               !(sist  & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-               !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
-               OUTB (nc_dcntl, (STD|NOCOM));
-               return;
-       };
-
-/*
-**     @RECOVER@ HTH, SGE, ABRT.
-**
-**     We should try to recover from these interrupts.
-**     They may occur if there are problems with synch transfers, or 
-**     if targets are switched on or off while the driver is running.
-*/
-
-       if (sist & SGE) {
-               OUTB (nc_ctest3, CLF);          /* clear scsi offsets */
-       }
-
-       /*
-       **      Freeze controller to be able to read the messages.
-       */
-
-       if (DEBUG_FLAGS & DEBUG_FREEZE) {
-               unsigned char val;
-               for (i=0; i<0x60; i++) {
-                       switch (i%16) {
-
-                       case 0:
-                               printf ("%s: reg[%d0]: ",
-                                       ncr_name(np),i/16);
-                               break;
-                       case 4:
-                       case 8:
-                       case 12:
-                               printf (" ");
-                               break;
-                       };
-                       val = INB_OFF(i);
-                       printf (" %x%x", val/16, val%16);
-                       if (i%16==15) printf (".\n");
-               }
-
-               del_timer(&np->timer);
-
-               printf ("%s: halted!\n", ncr_name(np));
-               /*
-               **      don't restart controller ...
-               */
-               OUTB (nc_istat,  SRST);
+               ncr_start_reset(np, 2);
                return;
        };
 
-#ifdef NCR_FREEZE
-       /*
-       **      Freeze system to be able to read the messages.
-       */
-       printf ("ncr: fatal error: system halted - press reset to reboot ...");
-       cli();
-       for (;;);
-#endif
-
-       /*
-       **      sorry, have to kill ALL jobs ...
+       /*=========================================================
+       **      We just miss the cause of the interrupt. :(
+       **      Print a message. The timeout will do the real work.
+       **=========================================================
        */
-
-       ncr_init (np, "fatal error", HS_FAIL);
-#ifndef SCSI_NCR_NO_DISCONNECT
-       np->disc = 1;
-#endif
+       printf ("%s: unknown interrupt\n", ncr_name(np));
 }
 
 /*==========================================================
@@ -5789,7 +6935,7 @@ void ncr_int_sto (ncb_p np)
        */
 
        dsa = INL (nc_dsa);
-       cp = &np->ccb;
+       cp = np->ccb;
        while (cp && (CCB_PHYS (cp, phys) != dsa))
                cp = cp->link_ccb;
 
@@ -5803,7 +6949,7 @@ void ncr_int_sto (ncb_p np)
        */
 
        scratcha = INL (nc_scratcha);
-       diff = scratcha - NCB_SCRIPT_PHYS (np, tryloop);
+       diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop);
 
 /*     assert ((diff <= MAX_START * 20) && !(diff % 20));*/
 
@@ -5813,9 +6959,55 @@ void ncr_int_sto (ncb_p np)
                return;
        };
        ncr_init (np, "selection timeout", HS_FAIL);
-#ifndef SCSI_NCR_NO_DISCONNECT
        np->disc = 1;
-#endif
+}
+
+/*==========================================================
+**
+**     ncr chip exception handler for SCSI bus mode change
+**
+**==========================================================
+**
+**     spi2-r12 11.2.3 says a transceiver mode change must 
+**     generate a reset event and a device that detects a reset 
+**     event shall initiate a hard reset. It says also that a
+**     device that detects a mode change shall set data transfer 
+**     mode to eight bit asynchronous, etc...
+**     So, just resetting should be enough.
+**      
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_sbmc (ncb_p np)
+{
+       u_char scsi_mode = INB (nc_stest4) & SMODE;
+
+       printf("%s: SCSI bus mode change from %x to %x, resetting ...\n",
+               ncr_name(np), np->scsi_mode, scsi_mode);
+
+       np->scsi_mode = scsi_mode;
+       ncr_start_reset(np, 2);
+
+       return 1;
+}
+
+/*==========================================================
+**
+**     ncr chip exception handler for SCSI parity error.
+**
+**==========================================================
+**
+**     SCSI parity errors are handled by the SCSI script.
+**     So, we just print some message.
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_par (ncb_p np)
+{
+       printf("%s: SCSI parity error detected\n", ncr_name(np));
+       return 0;
 }
 
 /*==========================================================
@@ -5836,49 +7028,76 @@ static void ncr_int_ma (ncb_p np)
 {
        u_int32 dbc;
        u_int32 rest;
-       u_int32 dsa;
        u_int32 dsp;
+       u_int32 dsa;
        u_int32 nxtdsp;
        u_int32 *vdsp;
        u_int32 oadr, olen;
        u_int32 *tblp;
         ncrcmd *newcmd;
-       u_char  cmd, sbcl, delta, ss0, ss2;
+       u_char  cmd, sbcl;
        ccb_p   cp;
 
-       dsp = INL (nc_dsp);
-       dsa = INL (nc_dsa);
-       dbc = INL (nc_dbc);
-       ss0 = INB (nc_sstat0);
-       ss2 = INB (nc_sstat2);
-       sbcl= INB (nc_sbcl);
+       dsp     = INL (nc_dsp);
+       dbc     = INL (nc_dbc);
+       sbcl    = INB (nc_sbcl);
 
-       cmd = dbc >> 24;
-       rest= dbc & 0xffffff;
-       delta=(INB (nc_dfifo) - rest) & 0x7f;
+       cmd     = dbc >> 24;
+       rest    = dbc & 0xffffff;
 
        /*
-       **      The data in the dma fifo has not been transfered to
-       **      the target -> add the amount to the rest
-       **      and clear the data.
-       **      Check the sstat2 register in case of wide transfer.
+       **      Take into account dma fifo and various buffers and latches,
+       **      only if the interrupted phase is an OUTPUT phase.
        */
 
-       if (! (INB(nc_dstat) & DFE)) rest += delta;
-       if (ss0 & OLF) rest++;
-       if (ss0 & ORF) rest++;
-       if (INB(nc_scntl3) & EWS) {
-               if (ss2 & OLF1) rest++;
-               if (ss2 & ORF1) rest++;
-       };
-       OUTB (nc_ctest3, CLF   );       /* clear dma fifo  */
-       OUTB (nc_stest3, TE|CSF);       /* clear scsi fifo */
+       if ((cmd & 1) == 0) {
+               u_char  ctest5, ss0, ss2;
+               u_short delta;
+
+               ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0;
+               if (ctest5 & DFS)
+                       delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
+               else
+                       delta=(INB (nc_dfifo) - rest) & 0x7f;
+
+               /*
+               **      The data in the dma fifo has not been transfered to
+               **      the target -> add the amount to the rest
+               **      and clear the data.
+               **      Check the sstat2 register in case of wide transfer.
+               */
+
+               rest += delta;
+               ss0  = INB (nc_sstat0);
+               if (ss0 & OLF) rest++;
+               if (ss0 & ORF) rest++;
+               if (INB(nc_scntl3) & EWS) {
+                       ss2 = INB (nc_sstat2);
+                       if (ss2 & OLF1) rest++;
+                       if (ss2 & ORF1) rest++;
+               };
+
+               OUTONB (nc_ctest3, CLF );       /* clear dma fifo  */
+               OUTB (nc_stest3, TE|CSF);       /* clear scsi fifo */
+
+               if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+                       printf ("P%x%x RL=%d D=%d SS0=%x ", cmd&7, sbcl&7,
+                               (unsigned) rest, (unsigned) delta, ss0);
+
+       } else  {
+               if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+                       printf ("P%x%x RL=%d ", cmd&7, sbcl&7, rest);
+               if ((cmd & 7) != 1) {
+                       OUTONB (nc_ctest3, CLF );
+                       OUTB (nc_stest3, TE|CSF);
+               }
+       }
 
        /*
        **      locate matching cp
        */
        dsa = INL (nc_dsa);
-       cp = &np->ccb;
+       cp  = np->ccb;
        while (cp && (CCB_PHYS (cp, phys) != dsa))
                cp = cp->link_ccb;
 
@@ -5904,19 +7123,18 @@ static void ncr_int_ma (ncb_p np)
        } else if (dsp == vtophys (&cp->patch[6])) {
                vdsp = &cp->patch[4];
                nxtdsp = vdsp[3];
-       } else {
+       } else if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
                vdsp = (u_int32 *) ((char*)np->script - np->p_script + dsp -8);
                nxtdsp = dsp;
+       } else {
+               vdsp = (u_int32 *) ((char*)np->scripth - np->p_scripth + dsp -8);
+               nxtdsp = dsp;
        };
 
        /*
        **      log the information
        */
-       if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) {
-               printf ("P%x%x ",cmd&7, sbcl&7);
-               printf ("RL=%d D=%d SS0=%x ",
-                       (unsigned) rest, (unsigned) delta, ss0);
-       };
+
        if (DEBUG_FLAGS & DEBUG_PHASE) {
                printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
                        cp, np->header.cp,
@@ -5948,7 +7166,7 @@ static void ncr_int_ma (ncb_p np)
        };
 
        /*
-       **      if old phase not dataphase, leave here.
+       **      check cmd against assumed interrupted script command.
        */
 
        if (cmd != (vdsp[0] >> 24)) {
@@ -5958,13 +7176,25 @@ static void ncr_int_ma (ncb_p np)
                
                return;
        }
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+       if ((cmd & 7) == 1 && np->assert_atn) {
+               np->assert_atn = 0;
+               OUTONB(nc_socl, CATN);
+       }
+#endif
+
+       /*
+       **      if old phase not dataphase, leave here.
+       */
+
        if (cmd & 0x06) {
                PRINT_ADDR(cp->cmd);
                printf ("phase change %x-%x %d@%08x resid=%d.\n",
                        cmd&7, sbcl&7, (unsigned)olen,
                        (unsigned)oadr, (unsigned)rest);
 
-               OUTB (nc_dcntl, (STD|NOCOM));
+               OUTONB (nc_dcntl, (STD|NOCOM));
                return;
        };
 
@@ -6000,7 +7230,10 @@ static void ncr_int_ma (ncb_p np)
        */
        np->profile.num_break++;
        OUTL (nc_temp, vtophys (newcmd));
-       OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+       if ((cmd & 7) == 0)
+               OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+       else
+               OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, checkatn));
 }
 
 /*==========================================================
@@ -6031,11 +7264,12 @@ static int ncr_show_msg (u_char * msg)
 
 void ncr_int_sir (ncb_p np)
 {
+       u_char scntl3;
        u_char chg, ofs, per, fak, wide;
        u_char num = INB (nc_dsps);
        ccb_p   cp=0;
        u_long  dsa;
-       u_char  target = INB (nc_ctest0) & 7;
+       u_char  target = INB (nc_ctest0) & 0x0f;
        tcb_p   tp     = &np->target[target];
        int     i;
        if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num);
@@ -6044,13 +7278,15 @@ void ncr_int_sir (ncb_p np)
        case SIR_SENSE_RESTART:
        case SIR_STALL_RESTART:
                break;
+       case SIR_STALL_QUEUE:   /* Ignore, just restart the script */
+               goto out;
 
        default:
                /*
                **      lookup the ccb
                */
                dsa = INL (nc_dsa);
-               cp = &np->ccb;
+               cp = np->ccb;
                while (cp && (CCB_PHYS (cp, phys) != dsa))
                        cp = cp->link_ccb;
 
@@ -6063,6 +7299,32 @@ void ncr_int_sir (ncb_p np)
        }
 
        switch (num) {
+               u_long endp;
+       case SIR_DATA_IO_IS_OUT:
+       case SIR_DATA_IO_IS_IN:
+/*
+**     We did not guess the direction of transfer. We have to wait for 
+**     actual data direction driven by the target before setting 
+**     pointers. We must patch the global header too.
+*/
+               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;
+               } 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.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);
+               return;
+               /* break; */
 
 /*--------------------------------------------------------------------
 **
@@ -6099,7 +7361,7 @@ void ncr_int_sir (ncb_p np)
                        if (DEBUG_FLAGS & DEBUG_RESTART)
                                printf ("+ restart job ..\n");
                        OUTL (nc_dsa, CCB_PHYS (cp, phys));
-                       OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, getcc));
+                       OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc));
                        return;
                };
 
@@ -6230,11 +7492,11 @@ void ncr_int_sir (ncb_p np)
                switch (cp->nego_status) {
 
                case NS_SYNC:
-                       ncr_setsync (np, cp, 0xe0);
+                       ncr_setsync (np, cp, 0, 0xe0);
                        break;
 
                case NS_WIDE:
-                       ncr_setwide (np, cp, 0);
+                       ncr_setwide (np, cp, 0, 0);
                        break;
 
                };
@@ -6277,8 +7539,8 @@ void ncr_int_sir (ncb_p np)
                **      check values against driver limits.
                */
 
-               if (per < np->ns_sync)
-                       {chg = 1; per = np->ns_sync;}
+               if (per < np->minsync)
+                       {chg = 1; per = np->minsync;}
                if (per < tp->minsync)
                        {chg = 1; per = tp->minsync;}
                if (ofs > tp->maxoffs)
@@ -6287,23 +7549,26 @@ void ncr_int_sir (ncb_p np)
                /*
                **      Check against controller limits.
                */
+               fak     = 7;
+               scntl3  = 0;
                if (ofs != 0) {
-                       fak = (4ul * per - 1) / np->ns_sync - 3;
-                       if (fak>7) {
+                       ncr_getsync(np, per, &fak, &scntl3);
+                       if (fak > 7) {
                                chg = 1;
                                ofs = 0;
                        }
                }
                if (ofs == 0) {
-                       fak = 7;
-                       per = 0;
+                       fak     = 7;
+                       per     = 0;
+                       scntl3  = 0;
                        tp->minsync = 0;
                }
 
                if (DEBUG_FLAGS & DEBUG_NEGO) {
                        PRINT_ADDR(cp->cmd);
-                       printf ("sync: per=%d ofs=%d fak=%d chg=%d.\n",
-                               per, ofs, fak, chg);
+                       printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n",
+                               per, scntl3, ofs, fak, chg);
                }
 
                if (INB (HS_PRT) == HS_NEGOTIATE) {
@@ -6318,19 +7583,19 @@ void ncr_int_sir (ncb_p np)
                                        /*
                                        **      Answer wasn't acceptable.
                                        */
-                                       ncr_setsync (np, cp, 0xe0);
+                                       ncr_setsync (np, cp, 0, 0xe0);
                                        OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
                                } else {
                                        /*
                                        **      Answer is ok.
                                        */
-                                       ncr_setsync (np, cp, (fak<<5)|ofs);
+                                       ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
                                        OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
                                };
                                return;
 
                        case NS_WIDE:
-                               ncr_setwide (np, cp, 0);
+                               ncr_setwide (np, cp, 0, 0);
                                break;
                        };
                };
@@ -6354,7 +7619,7 @@ void ncr_int_sir (ncb_p np)
                **      prepare an answer message
                */
 
-               ncr_setsync (np, cp, (fak<<5)|ofs);
+               ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
 
                np->msgout[0] = M_EXTENDED;
                np->msgout[1] = 3;
@@ -6367,7 +7632,7 @@ void ncr_int_sir (ncb_p np)
                if (DEBUG_FLAGS & DEBUG_NEGO) {
                        PRINT_ADDR(cp->cmd);
                        printf ("sync msgout: ");
-                       (void) ncr_show_msg (np->msgin);
+                       (void) ncr_show_msg (np->msgout);
                        printf (".\n");
                }
 
@@ -6429,19 +7694,19 @@ void ncr_int_sir (ncb_p np)
                                        /*
                                        **      Answer wasn't acceptable.
                                        */
-                                       ncr_setwide (np, cp, 0);
+                                       ncr_setwide (np, cp, 0, 1);
                                        OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
                                } else {
                                        /*
                                        **      Answer is ok.
                                        */
-                                       ncr_setwide (np, cp, wide);
+                                       ncr_setwide (np, cp, wide, 1);
                                        OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
                                };
                                return;
 
                        case NS_SYNC:
-                               ncr_setsync (np, cp, 0xe0);
+                               ncr_setsync (np, cp, 0, 0xe0);
                                break;
                        };
                };
@@ -6451,7 +7716,7 @@ void ncr_int_sir (ncb_p np)
                **      prepare an answer message
                */
 
-               ncr_setwide (np, cp, wide);
+               ncr_setwide (np, cp, wide, 1);
 
                np->msgout[0] = M_EXTENDED;
                np->msgout[1] = 2;
@@ -6534,13 +7799,14 @@ void ncr_int_sir (ncb_p np)
                */
 
                PRINT_ADDR(cp->cmd);
-               printf ("M_DISCONNECT received, but datapointer not saved:\n"
-                       "\tdata=%x save=%x goal=%x.\n",
+               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);
                break;
 
+#if 0   /* This stuff does not work */
 /*--------------------------------------------------------------------
 **
 **     Processing of a "S_QUEUE_FULL" status.
@@ -6570,15 +7836,10 @@ void ncr_int_sir (ncb_p np)
 
                np->script->start1[0] =  SCR_INT;
 
-               /*
-               **      For the moment tagged transfers cannot be disabled.
-               */
-#if 0
                /*
                **      Try to disable tagged transfers.
                */
                ncr_setmaxtags (np, &np->target[target], 0);
-#endif
 
                /*
                ** @QUEUE@
@@ -6601,7 +7862,7 @@ void ncr_int_sir (ncb_p np)
                /*
                **      Look for a disconnected job.
                */
-               cp = &np->ccb;
+               cp = np->ccb;
                while (cp && cp->host_status != HS_DISCONNECT)
                        cp = cp->link_ccb;
 
@@ -6623,10 +7884,11 @@ void ncr_int_sir (ncb_p np)
                printf ("%s: queue empty.\n", ncr_name (np));
                np->script->start1[0] =  SCR_INT ^ IFFALSE (0);
                break;
+#endif   /* This stuff does not work */
        };
 
 out:
-       OUTB (nc_dcntl, (STD|NOCOM));
+       OUTONB (nc_dcntl, (STD|NOCOM));
 }
 
 /*==========================================================
@@ -6649,7 +7911,9 @@ static    ccb_p ncr_get_ccb
        */
 
        lp = np->target[target].lp[lun];
-       if (lp) {
+
+       if (lp && lp->opennings && (!lp->active || lp->active < lp->reqlink)) {
+
                cp = lp->next_ccb;
 
                /*
@@ -6657,6 +7921,15 @@ static   ccb_p ncr_get_ccb
                */
 
                while (cp && cp->magic) cp = cp->next_ccb;
+
+               /*
+               **      Increment active commands and decrement credit.
+               */
+
+               if (cp) {
+                       ++lp->active;
+                       --lp->opennings;
+               }
        }
 
        /*
@@ -6668,7 +7941,7 @@ static    ccb_p ncr_get_ccb
        if ((!cp) && lp && lp->actccbs > 0)
                return ((ccb_p) 0);
 
-       if (!cp) cp = &np->ccb;
+       if (!cp) cp = np->ccb;
 
        /*
        **      Wait until available.
@@ -6697,18 +7970,30 @@ static  ccb_p ncr_get_ccb
 **==========================================================
 */
 
-void ncr_free_ccb (ncb_p np, ccb_p cp)
+void ncr_free_ccb (ncb_p np, ccb_p cp, u_long target, u_long lun)
 {
+       lcb_p lp;
+
        /*
        **    sanity
        */
 
        assert (cp != NULL);
 
+       /*
+       **      Decrement active commands and increment credit.
+       */
+
+       lp = np->target[target].lp[lun];
+       if (lp) {
+                       --lp->active;
+                       ++lp->opennings;
+       }
+
        cp -> host_status = HS_IDLE;
        cp -> magic = 0;
 #if 0
-       if (cp == &np->ccb)
+       if (cp == np->ccb)
                wakeup ((caddr_t) cp);
 #endif
 }
@@ -6743,10 +8028,12 @@ static  void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
                tp->jump_tcb.l_cmd   = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
                tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
 
-               tp->getscr[0] = SCR_COPY (1);
+               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] = SCR_COPY (1);
+               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);
 
@@ -6759,7 +8046,7 @@ static    void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
                tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun);
 
                tp->jump_lcb.l_cmd   = (SCR_JUMP);
-               tp->jump_lcb.l_paddr = NCB_SCRIPT_PHYS (np, abort);
+               tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
                np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb);
        }
 
@@ -6771,7 +8058,7 @@ static    void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
                /*
                **      Allocate a lcb
                */
-               lp = (lcb_p) m_alloc (sizeof (struct lcb));
+               lp = (lcb_p) m_alloc (sizeof (struct lcb), LCB_ALIGN_SHIFT);
                if (!lp) return;
 
                if (DEBUG_FLAGS & DEBUG_ALLOC) {
@@ -6790,31 +8077,25 @@ static  void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
                lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag);
 
                lp->jump_ccb.l_cmd   = (SCR_JUMP);
-               lp->jump_ccb.l_paddr = NCB_SCRIPT_PHYS (np, aborttag);
+               lp->jump_ccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag);
 
                lp->actlink = 1;
 
+               lp->active  = 1;
+
                /*
                **   Chain into LUN list
                */
                tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb);
                tp->lp[lun] = lp;
 
-#ifndef SCSI_NCR_TAGGED_QUEUE_DISABLED
-               if (!lp->usetags) {
-                       ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
-               }
-#endif
+               ncr_setmaxtags (np, tp, driver_setup.default_tags);
        }
 
        /*
        **      Allocate ccbs up to lp->reqccbs.
-       **
-       **      This modification will be reworked in a future release.
        */
 
-loop_alloc_ccb:
-
        /*
        **      Limit possible number of ccbs.
        **
@@ -6828,7 +8109,7 @@ loop_alloc_ccb:
        /*
        **      Allocate a ccb
        */
-       cp = (ccb_p) m_alloc (sizeof (struct ccb));
+       cp = (ccb_p) m_alloc (sizeof (struct ccb), CCB_ALIGN_SHIFT);
        if (!cp)
                return;
 
@@ -6864,18 +8145,70 @@ loop_alloc_ccb:
        cp->call_tmp.l_paddr = NCB_SCRIPT_PHYS (np, resel_tmp);
 
        /*
-       **      Chain into wakeup list
+       **      Chain into wakeup list
+       */
+       cp->link_ccb      = np->ccb->link_ccb;
+       np->ccb->link_ccb = cp;
+
+       /*
+       **      Chain into CCB list
+       */
+       cp->next_ccb    = lp->next_ccb;
+       lp->next_ccb    = cp;
+}
+
+/*==========================================================
+**
+**
+**     Announce the number of ccbs/tags to the scsi driver.
+**
+**
+**==========================================================
+*/
+
+static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * cmd)
+{
+       /*
+       **      want to reduce the number ...
        */
-       cp->link_ccb      = np->ccb.link_ccb;
-       np->ccb.link_ccb  = cp;
+       if (lp->actlink > lp->reqlink) {
+
+               /*
+               **      Try to  reduce the count.
+               **      We assume to run at splbio ..
+               */
+               u_char diff = lp->actlink - lp->reqlink;
+
+               if (!diff) return;
+
+               if (diff > lp->opennings)
+                       diff = lp->opennings;
+
+               lp->opennings   -= diff;
+
+               lp->actlink     -= diff;
+               if (DEBUG_FLAGS & DEBUG_TAGS)
+                       printf ("%s: actlink: diff=%d, new=%d, req=%d\n",
+                               ncr_name(np), diff, lp->actlink, lp->reqlink);
+               return;
+       };
 
        /*
-       **      Chain into CCB list
+       **      want to increase the number ?
        */
-       cp->next_ccb    = lp->next_ccb;
-       lp->next_ccb    = cp;
+       if (lp->reqlink > lp->actlink) {
+               u_char diff = lp->reqlink - lp->actlink;
+
+               lp->opennings   += diff;
 
-goto loop_alloc_ccb;
+               lp->actlink     += diff;
+#if 0
+               wakeup ((caddr_t) xp->sc_link);
+#endif
+               if (DEBUG_FLAGS & DEBUG_TAGS)
+                       printf ("%s: actlink: diff=%d, new=%d, req=%d\n",
+                               ncr_name(np), diff, lp->actlink, lp->reqlink);
+       };
 }
 
 /*==========================================================
@@ -6894,8 +8227,7 @@ goto loop_alloc_ccb;
 **----------------------------------------------------------
 */
 
-/*     FreeBSD driver important comments
-**     ---------------------------------
+/*
 **     We try to reduce the number of interrupts caused
 **     by unexpected phase changes due to disconnects.
 **     A typical harddisk may disconnect before ANY block.
@@ -6903,158 +8235,49 @@ goto loop_alloc_ccb;
 **     we had to use a break point every 512 bytes.
 **     Of course the number of scatter/gather blocks is
 **     limited.
+**     Under Linux, the scatter/gatter blocks are provided by 
+**     the generic driver. We just have to copy addresses and 
+**     sizes to the data segment array.
 */
 
-/*
-**     The scatterlist passed by the linux middle-level scsi drivers
-**     may contain blocks of any size (Generaly < 1024 bytes blocks,
-**     can be 4096 with a 4K fs).
-*/
-
-#if defined(SCSI_NCR_SEGMENT_SIZE)
 static int     ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
 {
-       struct scatterlist *scatter;
-       struct dsb *phys;
-       register u_short segment = 0;
-       register u_short o_segment = 0;
-       u_short chunk, chunk_min;
-       u_long segaddr;
-       int segsize;
-       int datalen;
-
-       phys = &cp->phys;
-       cp->data_len = 0;
-
-       /*
-       **      Compute a good value for chunk size
-       **      If SCSI_NCR_SEGMENT_SIZE is OK, we will try to use it. 
-       */
-
-       if (!cmd->use_sg)
-               cp->data_len    = cmd->request_bufflen;
-       else {
-               scatter = (struct scatterlist *)cmd->buffer;
-               for (segment = 0 ; segment < cmd->use_sg ; segment++)
-                       cp->data_len += scatter[segment].length;
-       }
-
-
-       if (!cp->data_len) {
-               bzero (&phys->data, sizeof (phys->data));
-               return 0;
-       }
+       struct scr_tblmove *data;
+       int segment     = 0;
+       int use_sg      = (int) cmd->use_sg;
 
-       chunk_min       = cp->data_len / MAX_SCATTER;
-       for (chunk = SCSI_NCR_SEGMENT_SIZE ; chunk < chunk_min ; chunk += chunk);
-
-       /*
-       **      If the linux scsi command is not a scatterlist,
-       **      the computed chunk size is OK.
-       */
-
-       if (!cmd->use_sg) {
-               bzero (&phys->data, sizeof (phys->data));
-               datalen = cmd->request_bufflen;
-               segaddr = vtophys(cmd->request_buffer);
-               segsize = chunk;
-               o_segment = 0;
-
-if (DEBUG_FLAGS & DEBUG_SCATTER)
-       printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n",
-       (unsigned) segaddr, (int) datalen, (int) chunk);
-
-               while (datalen && (o_segment < MAX_SCATTER)) {
-                       if (segsize > datalen) segsize  = datalen;
-                       phys->data[o_segment].addr      = segaddr;
-                       phys->data[o_segment].size      = segsize;
-
-                       datalen -= segsize;
-
-if(DEBUG_FLAGS & DEBUG_SCATTER)
-       printf ("ncr53c8xx:     seg #%d  addr=%lx  size=%d  (rest=%d).\n",
-       o_segment, segaddr, (int) segsize, (int) datalen);
-
-                       segaddr += segsize;
-                       o_segment++;
+#if 0
+       bzero (cp->phys.data, sizeof (cp->phys.data));
+#endif
+       data            = cp->phys.data;
+       cp->data_len    = 0;
+
+       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;
+                       segment = 1;
                }
-
-               return datalen ? -1 : o_segment;
        }
-
-       /*
-       **      Else, the computed chunk size is not so good
-       **      and we have to iterate.
-       **      Rescatter the Linux scatterlist into the data block descriptor.
-       **      Loop if necessary, beginning with the not so good chunk size and
-       **      doubling it if the scatter process fails.
-       */
-
-       scatter = (struct scatterlist *)cmd->buffer;
-       for (segment = 0; segment < cmd->use_sg; chunk += chunk) {
-               o_segment       = 0;
-               bzero (&phys->data, sizeof (phys->data));
-               for (segment = 0 ; segment < cmd->use_sg ; segment++) {
-                       datalen = scatter[segment].length;
-                       segaddr = vtophys(scatter[segment].address);
-                       segsize = chunk;
-
-if (DEBUG_FLAGS & DEBUG_SCATTER)
-       printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n",
-       (unsigned) segaddr, (int) datalen, (int) chunk);
-
-                       while (datalen && (o_segment < MAX_SCATTER)) {
-                               if (segsize > datalen) segsize  = datalen;
-                               phys->data[o_segment].addr      = segaddr;
-                               phys->data[o_segment].size      = segsize;
-
-                               datalen -= segsize;
-
-if(DEBUG_FLAGS & DEBUG_SCATTER)
-       printf ("ncr53c8xx:     seg #%d  addr=%lx  size=%d  (rest=%d).\n",
-       o_segment, segaddr, (int) segsize, (int) datalen);
-
-                               segaddr += segsize;
-                               o_segment++;
-                       }
-
-                       if (datalen) break;
+       else if (use_sg <= MAX_SCATTER) {
+               struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+
+               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;
+                       ++segment;
                }
        }
-
-       return segment < cmd->use_sg ? -1 : o_segment;
-}
-
-#else /* !defined SCSI_NCR_SEGMENT_SIZE */
-
-static int     ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
-{
-       struct dsb *phys = &cp->phys;
-       u_short segment  = 0;
-
-       cp->data_len = 0;
-       bzero (&phys->data, sizeof (phys->data));
-
-       if (!cmd->use_sg) {
-            phys->data[segment].addr = vtophys(cmd->request_buffer);
-            phys->data[segment].size = cmd->request_bufflen;
-            cp->data_len            += phys->data[segment].size;
-            segment++;
-            return segment;
-       }
-
-       while (segment < cmd->use_sg && segment < MAX_SCATTER) {
-            struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
-
-            phys->data[segment].addr = vtophys(scatter[segment].address);
-            phys->data[segment].size = scatter[segment].length;
-            cp->data_len            += phys->data[segment].size;
-            ++segment;
+       else {
+               return -1;
        }
 
-       return segment < cmd->use_sg ? -1 : segment;
+       return segment;
 }
-#endif /* SCSI_NCR_SEGMENT_SIZE */
 
 /*==========================================================
 **
@@ -7068,7 +8291,9 @@ static    int     ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
 */
 
 #ifndef NCR_IOMAPPED
+__initfunc(
 static int ncr_regtest (struct ncb* np)
+)
 {
        register volatile u_long data;
        /*
@@ -7092,12 +8317,14 @@ static int ncr_regtest (struct ncb* np)
 }
 #endif
 
+__initfunc(
 static int ncr_snooptest (struct ncb* np)
+)
 {
        u_long  ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc, err=0;
        int     i;
 #ifndef NCR_IOMAPPED
-       if (np->use_mmio) {
+       if (np->reg) {
             err |= ncr_regtest (np);
             if (err) return (err);
        }
@@ -7105,7 +8332,7 @@ static int ncr_snooptest (struct ncb* np)
        /*
        **      init
        */
-       pc  = NCB_SCRIPT_PHYS (np, snooptest);
+       pc  = NCB_SCRIPTH_PHYS (np, snooptest);
        host_wr = 1;
        ncr_wr  = 2;
        /*
@@ -7149,8 +8376,11 @@ static int ncr_snooptest (struct ncb* np)
        /*
        **      Check termination position.
        */
-       if (pc != NCB_SCRIPT_PHYS (np, snoopend)+8) {
+       if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) {
                printf ("CACHE TEST FAILED: script execution failed.\n");
+               printf ("start=%08lx, pc=%08lx, end=%08lx\n", 
+                       (u_long) NCB_SCRIPTH_PHYS (np, snooptest), pc,
+                       (u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8);
                return (0x40);
        };
        /*
@@ -7183,23 +8413,19 @@ static int ncr_snooptest (struct ncb* np)
 **==========================================================
 */
 
-/*
-**     Compute the difference in milliseconds.
-**/
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 
-#ifdef SCSI_NCR_PROFILE
+/*
+**     Compute the difference in jiffies ticks.
+*/
 
-static int ncr_delta (u_long from, u_long to)
-{
-       if (!from) return (-1);
-       if (!to) return (-2);
-       return ((to  - from) * 1000 / HZ );
-}
+#define ncr_delta(from, to) \
+       ( ((to) && (from))? (to) - (from) : -1 )
 
 #define PROFILE  cp->phys.header.stamp
 static void ncb_profile (ncb_p np, ccb_p cp)
 {
-       int co, da, st, en, di, se, post,work,disc;
+       int co, st, en, di, se, post,work,disc;
        u_long diff;
 
        PROFILE.end = jiffies;
@@ -7207,9 +8433,6 @@ static    void ncb_profile (ncb_p np, ccb_p cp)
        st = ncr_delta (PROFILE.start,PROFILE.status);
        if (st<0) return;       /* status  not reached  */
 
-       da = ncr_delta (PROFILE.start,PROFILE.data);
-       if (da<0) return;       /* No data transfer phase */
-
        co = ncr_delta (PROFILE.start,PROFILE.command);
        if (co<0) return;       /* command not executed */
 
@@ -7246,7 +8469,7 @@ static    void ncb_profile (ncb_p np, ccb_p cp)
 }
 #undef PROFILE
 
-#endif /* SCSI_NCR_PROFILE */
+#endif /* SCSI_NCR_PROFILE_SUPPORT */
 
 /*==========================================================
 **
@@ -7307,7 +8530,7 @@ static u_long ncr_lookup(char * id)
 /*==========================================================
 **
 **     Determine the ncr's clock frequency.
-**     This is important for the negotiation
+**     This is essential for the negotiation
 **     of the synchronous transfer rate.
 **
 **==========================================================
@@ -7315,84 +8538,161 @@ static u_long ncr_lookup(char * id)
 **     Note: we have to return the correct value.
 **     THERE IS NO SAVE DEFAULT VALUE.
 **
-**     We assume that all NCR based boards are delivered
-**     with a 40Mhz clock. Because we have to divide
-**     by an integer value greater than 3, only clock
-**     frequencies of 40Mhz (/4) or 50MHz (/5) permit
-**     the FAST-SCSI rate of 10MHz.
+**     Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock.
+**     53C860 and 53C875 rev. 1 support fast20 transfers but 
+**     do not have a clock doubler and so are provided with a 
+**     80 MHz clock. All other fast20 boards incorporate a doubler 
+**     and so should be delivered with a 40 MHz clock.
+**     The future fast40 chips (895/895) use a 40 Mhz base clock 
+**     and provide a clock quadrupler (160 Mhz). The code below 
+**     tries to deal as cleverly as possible with all this stuff.
 **
 **----------------------------------------------------------
 */
 
-#ifndef NCR_CLOCK
-#      define NCR_CLOCK 40
-#endif /* NCR_CLOCK */
+/*
+ *     Select NCR SCSI clock frequency
+ */
+static void ncr_selectclock(ncb_p np, u_char scntl3)
+{
+       if (np->multiplier < 2) {
+               OUTB(nc_scntl3, scntl3);
+               return;
+       }
+
+       if (bootverbose >= 2)
+               printf ("%s: enabling clock multiplier\n", ncr_name(np));
+
+       OUTB(nc_stest1, DBLEN);    /* Enable clock multiplier             */
+       if (np->multiplier > 2) {  /* Poll bit 5 of stest4 for quadrupler */
+               int i = 20;
+               while (!(INB(nc_stest4) & LCKFRQ) && --i > 0)
+                       DELAY(20);
+               if (!i)
+                       printf("%s: the chip cannot lock the frequency\n", ncr_name(np));
+       } else                  /* Wait 20 micro-seconds for doubler    */
+               DELAY(20);
+       OUTB(nc_stest3, HSC);           /* Halt the scsi clock          */
+       OUTB(nc_scntl3, scntl3);
+       OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier      */
+       OUTB(nc_stest3, 0x00);          /* Restart scsi clock           */
+}
 
 
-static void ncr_getclock (ncb_p np, u_char scntl3)
+/*
+ *     calculate NCR SCSI clock frequency (in KHz)
+ */
+__initfunc(
+static unsigned ncrgetfreq (ncb_p np, int gen)
+)
 {
-#if 0
-       u_char  tbl[5] = {6,2,3,4,6};
-       u_char  f;
-       u_char  ns_clock = (1000/NCR_CLOCK);
+       unsigned ms = 0;
+
+       /*
+        * Measure GEN timer delay in order 
+        * to calculate SCSI clock frequency
+        *
+        * This code will never execute too
+        * many loop iterations (if DELAY is 
+        * reasonably correct). It could get
+        * too low a delay (too high a freq.)
+        * if the CPU is slow executing the 
+        * loop for some reason (an NMI, for
+        * example). For this reason we will
+        * if multiple measurements are to be 
+        * performed trust the higher delay 
+        * (lower frequency returned).
+        */
+       OUTB (nc_stest1, 0);    /* make sure clock doubler is OFF */
+       OUTW (nc_sien , 0);     /* mask all scsi interrupts */
+       (void) INW (nc_sist);   /* clear pending scsi interrupt */
+       OUTB (nc_dien , 0);     /* mask all dma interrupts */
+       (void) INW (nc_sist);   /* another one, just to be sure :) */
+       OUTB (nc_scntl3, 4);    /* set pre-scaler to divide by 3 */
+       OUTB (nc_stime1, 0);    /* disable general purpose timer */
+       OUTB (nc_stime1, gen);  /* set to nominal delay of 1<<gen * 125us */
+       while (!(INW(nc_sist) & GEN) && ms++ < 100000)
+               DELAY(1000);    /* count ms */
+       OUTB (nc_stime1, 0);    /* disable general purpose timer */
+       /*
+        * set prescaler to divide by whatever 0 means
+        * 0 ought to choose divide by 2, but appears
+        * to set divide by 3.5 mode in my 53c810 ...
+        */
+       OUTB (nc_scntl3, 0);
+
+       if (bootverbose >= 2)
+               printf ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
+       /*
+        * adjust for prescaler, and convert into KHz 
+        */
+       return ms ? ((1 << gen) * 4340) / ms : 0;
+}
 
-       /*
-       **      Compute the best value for scntl3.
-       */
+/*
+ *     Get/probe NCR SCSI clock frequency
+ */
+__initfunc(
+static void ncr_getclock (ncb_p np, int mult)
+)
+{
+       unsigned char scntl3 = INB(nc_scntl3);
+       unsigned char stest1 = INB(nc_stest1);
+       unsigned f1;
 
-       f = (2 * MIN_SYNC_PD - 1) / ns_clock;
-       if (!f ) f=1;
-       if (f>4) f=4;
-       np -> ns_sync = (ns_clock * tbl[f]) / 2;
-       np -> rv_scntl3 = f<<4;
+       np->multiplier = 1;
+       f1 = 40000;
 
-       f = (2 * MIN_ASYNC_PD - 1) / ns_clock;
-       if (!f ) f=1;
-       if (f>4) f=4;
-       np -> ns_async = (ns_clock * tbl[f]) / 2;
-       np -> rv_scntl3 |= f;
-       if (DEBUG_FLAGS & DEBUG_TIMING)
-               printf ("%s: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n",
-               ncr_name (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3);
-#else
        /*
-        * If NCR53C875 chip with clock doubler enabled,
-        *      disable clock doubler and assume 40 MHz clock.
-        * If NCR53C860 chip assume 80 MHz clock.
-        */
-
-       switch(np->device_id) {
-       case PCI_DEVICE_ID_NCR_53C875:
-               if ((INB(nc_stest1) & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
-                       if (bootverbose)
-                               printf ("%s: disabling clock doubler\n", ncr_name(np));
-                       OUTB(nc_stest1, 0);
-                       scntl3  = 3;
-               }
-               break;
-       case PCI_DEVICE_ID_NCR_53C860:
-               scntl3  = 5;
-               break;
+       **      True with 875 or 895 with clock multiplier selected
+       */
+       if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+               if (bootverbose >= 2)
+                       printf ("%s: clock multiplier found\n", ncr_name(np));
+               np->multiplier = mult;
        }
 
        /*
-        *      For now just preserve the BIOS setting ...
-        */
+       **      If multiplier not found or scntl3 not 7,5,3,
+       **      reset chip and get frequency from general purpose timer.
+       **      Otherwise trust scntl3 BIOS setting.
+       */
+       if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+               unsigned f2;
 
-       if ((scntl3 & 7) < 3) {
-               printf ("%s: assuming 40MHz clock\n", ncr_name(np));
-               scntl3 = 3; /* assume 40MHz if no value supplied by BIOS */
-       }
+               OUTB(nc_istat, SRST); DELAY(5); OUTB(nc_istat, 0);
+
+               (void) ncrgetfreq (np, 11);     /* throw away first result */
+               f1 = ncrgetfreq (np, 11);
+               f2 = ncrgetfreq (np, 11);
+
+               if (bootverbose)
+                       printf ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
+
+               if (f1 > f2) f1 = f2;           /* trust lower result   */
+
+               if      (f1 <   45000)          f1 =  40000;
+               else if (f1 <   55000)          f1 =  50000;
+               else                            f1 =  80000;
 
-       np->ns_sync   = 25;
-       np->ns_async  = 50;
-       np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7);
+               if (f1 < 80000 && mult > 1) {
+                       if (bootverbose >= 2)
+                               printf ("%s: clock multiplier assumed\n", ncr_name(np));
+                       np->multiplier  = mult;
+               }
+       } else {
+               if      ((scntl3 & 7) == 3)     f1 =  40000;
+               else if ((scntl3 & 7) == 5)     f1 =  80000;
+               else                            f1 = 160000;
 
-       if (bootverbose) {
-               printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
-                       ncr_name(np), scntl3, np->rv_scntl3);
+               f1 /= np->multiplier;
        }
-#endif
+
+       /*
+       **      Compute controller synchronous parameters.
+       */
+       f1              *= np->multiplier;
+       np->clock_khz   = f1;
 }
 
 /*===================== LINUX ENTRY POINTS SECTION ==========================*/
@@ -7409,29 +8709,108 @@ static void ncr_getclock (ncb_p np, u_char scntl3)
 #define ulong unsigned long
 #endif
 
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip,
-            uchar bus, uchar device_fn, int options);
-
-/*
-**   NCR53C8XX devices description table
+/* ---------------------------------------------------------------------
+**
+**     Driver setup from the boot command line
+**
+** ---------------------------------------------------------------------
 */
 
-static struct {
-     ushort pci_device_id;
-     int chip;
-     int max_revision;
-     int min_revision;
-} pci_chip_ids[] = { 
-     {PCI_DEVICE_ID_NCR_53C810,   810, -1, -1}, 
-/*   {PCI_DEVICE_ID_NCR_53C810AP, 810, -1, -1}, */
-     {PCI_DEVICE_ID_NCR_53C815,   815, -1, -1},
-     {PCI_DEVICE_ID_NCR_53C820,   820, -1, -1},
-     {PCI_DEVICE_ID_NCR_53C825,   825, -1, -1},
-     {PCI_DEVICE_ID_NCR_53C860,   860, -1, -1},
-     {PCI_DEVICE_ID_NCR_53C875,   875, -1, -1}
-};
+__initfunc(
+void ncr53c8xx_setup(char *str, int *ints)
+)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+       char *cur = str;
+       char *pc, *pv;
+       int val;
+       int base;
+       int c;
+
+       while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+               val = 0;
+               pv = pc;
+               c = *++pv;
+               if      (c == 'n')
+                       val = 0;
+               else if (c == 'y')
+                       val = 1;
+               else {
+                       base = 0;
+#if 0
+                       if      (c == '0') {
+                               c = *pv++;
+                               base = 8;
+                       }
+                       if      (c == 'x') {
+                               ++pv;
+                               base = 16;
+                       }
+                       else if (c >= '0' && c <= '9')
+                               base = 10;
+                       else
+                               break;
+#endif
+                       val = (int) simple_strtoul(pv, NULL, base);
+               }
+
+               if      (!strncmp(cur, "mpar:", 5))
+                       driver_setup.master_parity      = val;
+               else if (!strncmp(cur, "spar:", 5))
+                       driver_setup.scsi_parity        = val;
+               else if (!strncmp(cur, "disc:", 5))
+                       driver_setup.disconnection      = val;
+               else if (!strncmp(cur, "specf:", 6))
+                       driver_setup.special_features = val;
+               else if (!strncmp(cur, "ultra:", 6))
+                       driver_setup.ultra_scsi = val;
+               else if (!strncmp(cur, "fsn:", 4))
+                       driver_setup.force_sync_nego    = val;
+               else if (!strncmp(cur, "revprob:", 8))
+                       driver_setup.reverse_probe      = val;
+               else if (!strncmp(cur, "tags:", 5)) {
+                       if (val > SCSI_NCR_MAX_TAGS)
+                               val = SCSI_NCR_MAX_TAGS;
+                       driver_setup.default_tags       = val;
+                       }
+               else if (!strncmp(cur, "sync:", 5))
+                       driver_setup.default_sync       = val;
+               else if (!strncmp(cur, "verb:", 5))
+                       driver_setup.verbose    = val;
+               else if (!strncmp(cur, "debug:", 6))
+                       driver_setup.debug      = val;
+               else if (!strncmp(cur, "burst:", 6))
+                       driver_setup.burst_max  = val;
+               else if (!strncmp(cur, "led:", 4))
+                       driver_setup.led_pin    = val;
+               else if (!strncmp(cur, "wide:", 5))
+                       driver_setup.max_wide   = val? 1:0;
+               else if (!strncmp(cur, "settle:", 7))
+                       driver_setup.settle_delay= val;
+               else if (!strncmp(cur, "diff:", 5))
+                       driver_setup.diff_support= val;
+               else if (!strncmp(cur, "irqm:", 5))
+                       driver_setup.irqm       = val;
+               else if (!strncmp(cur, "pcifix:", 7))
+                       driver_setup.pci_fix_up = val;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+               else if (!strncmp(cur, "nvram:", 6))
+                       driver_setup.use_nvram  = val;
+#endif
+
+               else if (!strncmp(cur, "safe:", 5) && val)
+                       memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
+               else
+                       printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", pc-cur+1, cur);
+
+               if ((cur = strchr(cur, ',')) != NULL)
+                       ++cur;
+       }
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+}
 
-#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0]))
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+            uchar bus, uchar device_fn, ncr_device *device);
 
 /*
 **   Linux entry point for NCR53C8XX devices detection routine.
@@ -7442,15 +8821,156 @@ static struct {
 **   Read the PCI configuration and try to attach each
 **   detected NCR board.
 **
+**   If NVRAM is present, try to attach boards according to 
+**   the used defined boot order.
+**
 **   Returns the number of boards successfully attached.
 */
 
+__initfunc(
+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",
+                       YesNo(driver_setup.disconnection),
+                       YesNo(driver_setup.special_features),
+                       YesNo(driver_setup.ultra_scsi),
+                       driver_setup.default_tags,
+                       driver_setup.default_sync,
+                       driver_setup.burst_max,
+                       YesNo(driver_setup.max_wide),
+                       driver_setup.diff_support);
+       printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,led:%c,settle:%d,irqm:%d\n",
+                       YesNo(driver_setup.master_parity),
+                       YesNo(driver_setup.scsi_parity),
+                       YesNo(driver_setup.force_sync_nego),
+                       driver_setup.verbose,
+                       driver_setup.debug,
+                       YesNo(driver_setup.led_pin),
+                       driver_setup.settle_delay,
+                       driver_setup.irqm);
+#undef YesNo
+}
+
+/*
+**   NCR53C8XX devices description table and chip ids list.
+*/
+
+static ncr_chip        ncr_chip_table[] __initdata     = SCSI_NCR_CHIP_TABLE;
+static ushort  ncr_chip_ids[]   __initdata     = SCSI_NCR_CHIP_IDS;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+__initfunc(
+static int
+ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[])
+)
+{
+       int i, j;
+       int attach_count = 0;
+       ncr_nvram  *nvram;
+       ncr_device *devp;
+
+       if (!nvram_index)
+               return 0;
+
+       /* find first Symbios NVRAM if there is one as we need to check it for host boot order */
+       for (i = 0, nvram_index = -1; i < count; i++) {
+               devp  = &device[i];
+               nvram = devp->nvram;
+               if (!nvram)
+                       continue;
+               if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) {
+                       if (nvram_index == -1)
+                               nvram_index = i;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+                       printf("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
+                               devp->chip.name, devp->slot.bus, 
+                               (int) (devp->slot.device_fn & 0xf8) >> 3, 
+                               (int) devp->slot.device_fn & 7);
+                       for (j = 0 ; j < 4 ; j++) {
+                               Symbios_host *h = &nvram->data.Symbios.host[j];
+                       printf("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
+                               j,              h->device_id,   h->vendor_id,
+                               h->device_fn,   h->io_port,
+                               (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
+                       }
+               }
+               else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) {
+                       /* display Tekram nvram data */
+                       printf("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
+                               devp->chip.name, devp->slot.bus, 
+                               (int) (devp->slot.device_fn & 0xf8) >> 3, 
+                               (int) devp->slot.device_fn & 7);
+#endif
+               }
+       }
+
+       if (nvram_index >= 0 && nvram_index < count)
+               nvram = device[nvram_index].nvram;
+       else
+               nvram = 0;
+
+       if (!nvram)
+               goto out;
+
+       /* 
+       ** check devices in the boot record against devices detected. 
+       ** attach devices if we find a match. boot table records that 
+       ** do not match any detected devices will be ignored. 
+       ** devices that do not match any boot table will not be attached
+       ** here but will attempt to be attached during the device table 
+       ** rescan.
+       */
+       for (i = 0; i < 4; i++) {
+               Symbios_host *h = &nvram->data.Symbios.host[i];
+               for (j = 0 ; j < count ; j++) {
+                       devp = &device[j];
+                       if (h->device_fn == devp->slot.device_fn &&
+#if 0  /* bus number location in nvram ? */
+                           h->bus       == devp->slot.bus       &&
+#endif
+                           h->device_id == devp->chip.device_id)
+                               break;
+               }
+               if (j < count && !devp->attached &&
+                   !ncr_attach (tpnt, attach_count, devp)) {
+                       attach_count++;
+                       devp->attached = 1;
+               }
+       }
+
+out:
+       return attach_count;
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
 int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
+)
 {
-     int i;
-     int count = 0;                    /* Number of boards detected */
-     uchar pci_bus, pci_device_fn;
-     short pci_index;  /* Device index to PCI BIOS calls */
+       int i, j;
+       int chips;
+       int count = 0;
+       uchar bus, device_fn;
+       short index;
+       int attach_count = 0;
+       ncr_device device[8];
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       ncr_nvram  nvram[4];
+       int k, nvrams;
+#endif
+       int hosts;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       int nvram_index = 0;
+#endif
+       if (initverbose >= 2)
+               ncr_print_driver_setup();
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+       ncr_debug = driver_setup.debug;
+#endif
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
      tpnt->proc_dir = &proc_scsi_ncr53c8xx;
@@ -7459,131 +8979,363 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 # endif
 #endif
 
-     if (pcibios_present()) {
-         for (i = 0; i < NPCI_CHIP_IDS; ++i) 
-              for (pci_index = 0;
-                   !pcibios_find_device(PCI_VENDOR_ID_NCR, 
-                                        pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, 
-                                        &pci_device_fn);
-                    ++pci_index)
-                   if (!ncr53c8xx_pci_init(tpnt, count, 0, pci_chip_ids[i].chip, 
-                             pci_bus, pci_device_fn, /* no options */ 0))
-                   ++count;
-     }
+       /* 
+       ** Detect all 53c8xx hosts and then attach them.
+       **
+       ** If we are using NVRAM, once all hosts are detected, we need to check
+       ** any NVRAM for boot order in case detect and boot order differ and
+       ** attach them using the order in the NVRAM.
+       **
+       ** If no NVRAM is found or data appears invalid attach boards in the 
+       ** the order they are detected.
+       */
 
-     return count;
-}
+       if (!pcibios_present())
+               return 0;
+
+       chips   = sizeof(ncr_chip_ids)  / sizeof(ncr_chip_ids[0]);
+       hosts   = sizeof(device)        / sizeof(device[0]);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       k = 0;
+       if (driver_setup.use_nvram & 0x1)
+               nvrams  = sizeof(nvram) / sizeof(nvram[0]);
+       else
+               nvrams  = 0;
+#endif
+
+       for (j = 0; j < chips ; ++j) {
+               i = driver_setup.reverse_probe ? chips-1 - j : j;
+               for (index = 0; ; index++) {
+                       char *msg = "";
+                       if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+                                               index, &bus, &device_fn)) ||
+                           (count == hosts))
+                               break;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+                       device[count].nvram = k < nvrams ? &nvram[k] : 0;
+#else
+                       device[count].nvram = 0;
+#endif
+                       if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) {
+                               device[count].nvram = 0;
+                               continue;
+                       }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+                       if (device[count].nvram) {
+                               ++k;
+                               nvram_index |= device[count].nvram->type;
+                               switch (device[count].nvram->type) {
+                               case SCSI_NCR_TEKRAM_NVRAM:
+                                       msg = "with Tekram NVRAM";
+                                       break;
+                               case SCSI_NCR_SYMBIOS_NVRAM:
+                                       msg = "with Symbios NVRAM";
+                                       break;
+                               default:
+                                       msg = "";
+                                       device[count].nvram = 0;
+                                       --k;
+                               }
+                       }
+#endif
+                       printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
+                               device[count].chip.name, msg);
+
+                       device[count].attached = 0;
+                       ++count;
+               }
+       }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device);
+#endif
+       /* 
+       ** rescan device list to make sure all boards attached.
+       ** devices without boot records will not be attached yet
+       ** so try to attach them here.
+       */
+       for (i= 0; i < count; i++) {
+               if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+                       attach_count++;
+                       device[i].attached = 1;
+               }
+       }
 
+       return attach_count;
+}
 
 /*
-**   Read the PCI configuration of a found NCR board and
-**   try yo attach it.
+**   Read and check the PCI configuration for any detected NCR 
+**   boards and save data for attaching after all boards have 
+**   been detected.
 */
 
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip,
-                   uchar bus, uchar device_fn, int options)
+__initfunc(
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+                             uchar bus, uchar device_fn, ncr_device *device)
+)
 {
-     ushort vendor_id, device_id, command;
+       ushort vendor_id, device_id, command;
+       uchar cache_line_size, latency_timer;
+       uchar irq, revision;
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-     uint base, io_port; 
+       uint base, io_port; 
 #else
-     ulong base, io_port; 
+       ulong base, io_port; 
 #endif
-     uchar irq, revision;
-     int error, expected_chip;
-     int expected_id = -1, max_revision = -1, min_revision = -1;
-     int i;
+       int i, error;
 
-     printk("ncr53c8xx : at PCI bus %d, device %d, function %d\n",
-           bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       ncr_nvram *nvram = device->nvram;
+#endif
+       ncr_chip *chip;
 
-     if (!pcibios_present()) {
-         printk("ncr53c8xx : not initializing due to lack of PCI BIOS,\n");
-         return -1;
-     }
+       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
+        */
+       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;
+
+       /*
+        *      Check if the chip is supported
+        */
+       chip = 0;
+       for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+               if (device_id != ncr_chip_table[i].device_id)
+                       continue;
+               if (revision > ncr_chip_table[i].revision_id)
+                       continue;
+               chip = &device->chip;
+               memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+               chip->revision_id = revision;
+               break;
+       }
+       if (!chip) {
+               printk("ncr53c8xx: not initializing, device not supported\n");
+               return -1;
+       }
 
-     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))) {
-         printk("ncr53c8xx : error %s not initializing due to error reading configuration space\n",
-                pcibios_strerror(error));
-         return -1;
-     }
+       /*
+        * Check availability of IO space, memory space and master capability.
+        */
+       if (command & PCI_COMMAND_IO) { 
+               if ((io_port & 3) != 1) {
+                       printk("ncr53c8xx: disabling I/O mapping since base address 0 (0x%x)\n"
+                               "           bits 0..1 indicate a non-IO mapping\n", (int) io_port);
+                       io_port = 0;
+               }
+               else
+                       io_port &= PCI_BASE_ADDRESS_IO_MASK;
+       }
+       else
+               io_port = 0;
 
-     if (vendor_id != PCI_VENDOR_ID_NCR) {
-         printk("ncr53c8xx : not initializing, 0x%04x is not NCR vendor ID\n", (int) vendor_id);
-         return -1;
-     }
+       if (command & PCI_COMMAND_MEMORY) {
+               if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+                       printk("ncr53c8xx: disabling memory mapping since base address 1\n"
+                               "            contains a non-memory mapping\n");
+                       base = 0;
+               }
+               else 
+                       base &= PCI_BASE_ADDRESS_MEM_MASK;
+       }
+       else
+               base = 0;
+       
+       if (!io_port && !base) {
+               printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
+               return -1;
+       }
+
+       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));
+               return -1;
+       }
+       
+       if (!(command & PCI_COMMAND_MASTER)) {
+               printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
+               return -1;
+       }
+
+       /*
+        * Fix some features according to driver setup.
+        */
+       if (!driver_setup.special_features)
+               chip->features &= ~FE_SPECIAL_SET;
+       if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+               chip->features |=  FE_ULTRA;
+               chip->features &= ~FE_ULTRA2;
+       }
+       if (driver_setup.ultra_scsi < 1)
+               chip->features &= ~FE_ULTRA;
+       if (!driver_setup.max_wide)
+               chip->features &= ~FE_WIDE;
+
+
+#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
+
+       /*
+        * Try to fix up PCI config according to wished features.
+        */
+#if defined(__i386) && !defined(MODULE)
+       if ((driver_setup.pci_fix_up & 1) &&
+           (chip->features & FE_CLSE) && cache_line_size == 0) {
+               extern char x86;
+               switch(x86) {
+               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;
+               if (initverbose)
+                       printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
+       }
+
+       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;
+               if (initverbose)
+                       printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
+       }
+#endif
+       /*
+        * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
+        * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
+        *   and donnot enable READ LINE.
+        * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed). 
+        */
+
+       if (!(chip->features & FE_CLSE)) {
+               int burst_max = chip->burst_max;
+               if (cache_line_size == 0) {
+                       chip->features  &= ~FE_ERL;
+                       if (burst_max > 3)
+                               burst_max = 3;
+               }
+               else {
+                       while (cache_line_size < (1 << burst_max))
+                               --burst_max;
+               }
+               chip->burst_max = burst_max;
+       }
+
+       /*
+        * Tune PCI LATENCY TIMER according to burst max length transfer.
+        * (latency timer >= burst length + 6, we add 10 to be quite sure)
+        * If current value is zero, the device has probably been configured 
+        * for no bursting due to some broken hardware.
+        */
 
+       if (latency_timer == 0 && chip->burst_max)
+               printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
+
+       if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
+               uchar lt = (1 << chip->burst_max) + 6 + 10;
+               if (latency_timer < lt) {
+                       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,
+                                       PCI_LATENCY_TIMER, latency_timer);
+                       if (error)
+                               goto err_pcibios;
+               }
+       }
+
+       /*
+        * Fix up for recent chips that support CACHE LINE SIZE.
+        * If PCI config space is not OK, remove features that shall not be 
+        * used by the chip. No need to trigger possible chip bugs.
+        */
+
+       if ((chip->features & FE_CLSE) && cache_line_size == 0) {
+               chip->features &= ~FE_CACHE_SET;
+               printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
+       }
+
+       if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+               chip->features &= ~FE_WRIE;
+               printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
+       }
+
+#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+       /* initialise ncr_device structure with items required by ncr_attach */
+       device->slot.bus        = bus;
+       device->slot.device_fn  = device_fn;
+       device->slot.base       = base;
+       device->slot.io_port    = io_port;
+       device->slot.irq        = irq;
+       device->attached        = 0;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+       if (!nvram)
+               goto out;
+
+       /*
+       ** Get access to chip IO registers
+       */
+#ifdef NCR_IOMAPPED
+       request_region(io_port, 128, "ncr53c8xx");
+       device->slot.port = ioport;
+#else
+       device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
+       if (!device->slot.reg)
+               goto out;
+#endif
 
-     if (command & PCI_COMMAND_IO) { 
-         if ((io_port & 3) != 1) {
-              printk("ncr53c8xx : disabling I/O mapping since base address 0 (0x%x)\n"
-                     "            bits 0..1 indicate a non-IO mapping\n", (int) io_port);
-              io_port = 0;
-         }
-         else
-              io_port &= PCI_BASE_ADDRESS_IO_MASK;
-     }
-     else
-         io_port = 0;
-
-     if (command & PCI_COMMAND_MEMORY) {
-         if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
-              printk("ncr53c8xx : disabling memory mapping since base address 1\n"
-                     "            contains a non-memory mapping\n");
-              base = 0;
-         }
-         else 
-              base &= PCI_BASE_ADDRESS_MEM_MASK;
-     }
-     else
-         base = 0;
-       
-     if (!io_port && !base) {
-         printk("ncr53c8xx : not initializing, both I/O and memory mappings disabled\n");
-         return -1;
-     }
-       
-     if (!(command & PCI_COMMAND_MASTER)) {
-         printk ("ncr53c8xx : not initializing, BUS MASTERING was disabled\n");
-         return -1;
-     }
+       /*
+       ** Try to read SYMBIOS nvram.
+       ** Data can be used to order booting of boards.
+       **
+       ** Data is saved in ncr_device structure if NVRAM found. This
+       ** is then used to find drive boot order for ncr_attach().
+       **
+       ** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
+       ** for any device set up.
+       **
+       ** Try to read TEKRAM nvram if Symbios nvram not found.
+       */
 
-     for (i = 0; i < NPCI_CHIP_IDS; ++i) {
-         if (device_id == pci_chip_ids[i].pci_device_id) {
-              max_revision  = pci_chip_ids[i].max_revision;
-              min_revision  = pci_chip_ids[i].min_revision;
-              expected_chip = pci_chip_ids[i].chip;
-         }
-         if (chip == pci_chip_ids[i].chip)
-              expected_id = pci_chip_ids[i].pci_device_id;
-     }
+       if      (!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
+               nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
+       else if (!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
+               nvram->type = SCSI_NCR_TEKRAM_NVRAM;
+       else
+               nvram->type = 0;
+out:
+       /*
+       ** Release access to chip IO registers
+       */
+#ifdef NCR_IOMAPPED
+       release_region(device->slot.port, 128);
+#else
+       unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
+#endif
 
-     if (chip && device_id != expected_id) 
-         printk("ncr53c8xx : warning : device id of 0x%04x doesn't\n"
-                "            match expected 0x%04x\n",
-                 (unsigned int) device_id, (unsigned int) expected_id );
-    
-     if (max_revision != -1 && revision > max_revision) 
-         printk("ncr53c8xx : warning : revision %d is greater than expected.\n",
-                (int) revision);
-     else if (min_revision != -1 && revision < min_revision)
-         printk("ncr53c8xx : warning : revision %d is lower than expected.\n",
-                (int) revision);
-
-     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));
-         return -1;
-     }
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+       return 0;     
 
-     return ncr_attach (tpnt, unit, device_id, revision, chip, base, io_port, 
-                      (int) irq, bus, (uchar) device_fn);
+err_pcibios:
+       printk("ncr53c8xx: error %s reading configuration space\n",
+               pcibios_strerror(error));
+       return -1;
 }
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -7596,13 +9348,18 @@ static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_de
 
        for (device = devlist; device; device = device->next) {
                if (device->host == host) {
+#if SCSI_NCR_MAX_TAGS > 1
                        if (device->tagged_supported) {
                                device->queue_depth = SCSI_NCR_MAX_TAGS;
                        }
                        else {
-                               device->queue_depth = 1;
+                               device->queue_depth = 2;
                        }
-#ifdef DEBUG
+#else
+                       device->queue_depth = 1;
+#endif
+
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n",
        device->id, device->lun, device->queue_depth);
 #endif
@@ -7618,19 +9375,19 @@ printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n",
 int ncr53c8xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 {
      int sts;
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx_queue_command\n");
 #endif
 
      if ((sts = ncr_queue_command(cmd, done)) != DID_OK) {
          cmd->result = ScsiResult(sts, 0);
          done(cmd);
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : command not queued - result=%d\n", sts);
 #endif
           return sts;
      }
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : command successfully queued\n");
 #endif
      return sts;
@@ -7648,8 +9405,13 @@ static void ncr53c8xx_intr(int irq, struct pt_regs * regs)
 {
      struct Scsi_Host *host;
      struct host_data *host_data;
+#if 0
+     u_long flags;
+
+     save_flags(flags); cli();
+#endif
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : interrupt received\n");
 #endif
 
@@ -7658,12 +9420,20 @@ printk("ncr53c8xx : interrupt received\n");
               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_data)
+               if (dev_id == host_data->ncb) {
+#else
+               if (1) {
 #   endif
 #endif
-              ncr_intr(&host_data->ncb_data);
+                    if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+                   ncr_exception(host_data->ncb);
+                    if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+               }
          }
      }
+#if 0
+     restore_flags(flags);
+#endif
 }
 
 /*
@@ -7679,40 +9449,106 @@ static void ncr53c8xx_timeout(unsigned long np)
 **   Linux entry point of reset() function
 */
 
-#if    LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+
 int ncr53c8xx_reset(Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+       int sts;
+       unsigned long flags;
+
+       printk("ncr53c8xx_reset: pid=%lu reset_flags=%x serial_number=%ld serial_number_at_timeout=%ld\n",
+               cmd->pid, reset_flags, cmd->serial_number, cmd->serial_number_at_timeout);
+
+       save_flags(flags); cli();
+
+       /*
+        * We have to just ignore reset requests in some situations.
+        */
+#if defined SCSI_RESET_NOT_RUNNING
+       if (cmd->serial_number != cmd->serial_number_at_timeout) {
+               sts = SCSI_RESET_NOT_RUNNING;
+               goto out;
+       }
+#endif
+       /*
+        * If the mid-level driver told us reset is synchronous, it seems 
+        * that we must call the done() callback for the involved command, 
+        * even if this command was not queued to the low-level driver, 
+        * before returning SCSI_RESET_SUCCESS.
+        */
+
+       sts = ncr_reset_bus(cmd,
+       (reset_flags & (SCSI_RESET_SYNCHRONOUS | SCSI_RESET_ASYNCHRONOUS)) == SCSI_RESET_SYNCHRONOUS);
+       /*
+        * Since we always reset the controller, when we return success, 
+        * we add this information to the return code.
+        */
+#if defined SCSI_RESET_HOST_RESET
+       if (sts == SCSI_RESET_SUCCESS)
+               sts |= SCSI_RESET_HOST_RESET;
+#endif
+
+out:
+       restore_flags(flags);
+       return sts;
+}
 #else
 int ncr53c8xx_reset(Scsi_Cmnd *cmd)
-#endif
 {
-#ifdef DEBUG
-printk("ncr53c8xx_reset : reset call\n");
-#endif
-       return ncr_reset_bus(cmd);
+       printk("ncr53c8xx_reset: command pid %lu\n", cmd->pid);
+       return ncr_reset_bus(cmd, 1);
 }
+#endif
 
 /*
 **   Linux entry point of abort() function
 */
 
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+
+int ncr53c8xx_abort(Scsi_Cmnd *cmd)
+{
+       int sts;
+       unsigned long flags;
+
+       printk("ncr53c8xx_abort: pid=%lu serial_number=%ld serial_number_at_timeout=%ld\n",
+               cmd->pid, cmd->serial_number, cmd->serial_number_at_timeout);
+
+       save_flags(flags); cli();
+
+       /*
+        * We have to just ignore abort requests in some situations.
+        */
+       if (cmd->serial_number != cmd->serial_number_at_timeout) {
+               sts = SCSI_ABORT_NOT_RUNNING;
+               goto out;
+       }
+
+       sts = ncr_abort_command(cmd);
+out:
+       restore_flags(flags);
+       return sts;
+}
+#else
 int ncr53c8xx_abort(Scsi_Cmnd *cmd)
 {
-printk("ncr53c8xx_abort : abort call\n");
+       printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid);
        return ncr_abort_command(cmd);
 }
+#endif
 
 #ifdef MODULE
 int ncr53c8xx_release(struct Scsi_Host *host)
 {
      struct host_data *host_data;
-#ifdef DEBUG
+#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_data, host->irq);
+              ncr_detach(host_data->ncb, host->irq);
          }
      }
 
@@ -7752,17 +9588,19 @@ static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd)
        }
 }
 
-static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd)
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd)
 {
        Scsi_Cmnd *wcmd;
 
        if (!(wcmd = np->waiting_list)) return 0;
        while (wcmd->next_wcmd) {
                if (cmd == (Scsi_Cmnd *) wcmd->next_wcmd) {
-                       wcmd->next_wcmd = cmd->next_wcmd;
-                       cmd->next_wcmd = 0;
+                       if (to_remove) {
+                               wcmd->next_wcmd = cmd->next_wcmd;
+                               cmd->next_wcmd = 0;
+                       }
 #ifdef DEBUG_WAITING_LIST
-       printf("%s: cmd %lx removed from waiting list\n", ncr_name(np), (u_long) cmd);
+       printf("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
 #endif
                        return cmd;
                }
@@ -7802,13 +9640,7 @@ static void process_waiting_list(ncb_p np, int sts)
 #undef next_wcmd
 
 /*
-**     In order to patch the SCSI script for SAVE/RESTORE DATA POINTER,
-**     we need the direction of transfer.
-**     Linux middle-level scsi driver does not provide this information.
-**     So we have to guess it.
-**     My documentation about SCSI-II standard is old. Probably some opcode
-**     are missing.
-**     If I do'nt know the command code, I assume input transfer direction.
+**     Returns data transfer direction for common op-codes.
 */
 
 static int guess_xfer_direction(int opcode)
@@ -7816,111 +9648,31 @@ static int guess_xfer_direction(int opcode)
        int d;
 
        switch(opcode) {
-       case 0x00:  /*  TEST UNIT READY                 00 */
-       case 0x08:  /*  READ(6)                         08 */
        case 0x12:  /*  INQUIRY                         12 */
        case 0x4D:  /*  LOG SENSE                       4D */
        case 0x5A:  /*  MODE SENSE(10)                  5A */
        case 0x1A:  /*  MODE SENSE(6)                   1A */
-       case 0x28:  /*  READ(10)                        28 */
-       case 0xA8:  /*  READ(12)                        A8 */
        case 0x3C:  /*  READ BUFFER                     3C */
        case 0x1C:  /*  RECEIVE DIAGNOSTIC RESULTS      1C */
-       case 0xB7:  /*  READ DEFECT DATA(12)            B7 */
-       case 0xB8:  /*  READ ELEMENT STATUS             B8 */
-                   /*  GET WINDOW                      25 */
-       case 0x25:  /*  READ CAPACITY                   25 */
-       case 0x29:  /*  READ GENERATION                 29 */
-       case 0x3E:  /*  READ LONG                       3E */
-                   /*  GET DATA BUFFER STATUS          34 */
-                   /*  PRE-FETCH                       34 */
-       case 0x34:  /*  READ POSITION                   34 */
        case 0x03:  /*  REQUEST SENSE                   03 */
-       case 0x05:  /*  READ BLOCK LIMITS               05 */
-       case 0x0F:  /*  READ REVERSE                    0F */
-       case 0x14:  /*  RECOVER BUFFERED DATA           14 */
-       case 0x2D:  /*  READ UPDATED BLOCK              2D */
-       case 0x37:  /*  READ DEFECT DATA(10)            37 */
-       case 0x42:  /*  READ SUB-CHANNEL                42 */
-       case 0x43:  /*  READ TOC                        43 */
-       case 0x44:  /*  READ HEADER                     44 */
-       case 0xC7:  /*  ???                  ???        C7 */
                d = XferIn;
                break;
        case 0x39:  /*  COMPARE                         39 */
        case 0x3A:  /*  COPY AND VERIFY                 3A */
-                   /*  PRINT                           0A */
-                   /*  SEND MESSAGE(6)                 0A */
-       case 0x0A:  /*  WRITE(6)                        0A */
        case 0x18:  /*  COPY                            18 */
        case 0x4C:  /*  LOG SELECT                      4C */
        case 0x55:  /*  MODE SELECT(10)                 55 */
        case 0x3B:  /*  WRITE BUFFER                    3B */
        case 0x1D:  /*  SEND DIAGNOSTIC                 1D */
        case 0x40:  /*  CHANGE DEFINITION               40 */
-                   /*  SEND MESSAGE(12)                AA */
-       case 0xAA:  /*  WRITE(12)                       AA */
-       case 0xB6:  /*  SEND VOLUME TAG                 B6 */
-       case 0x3F:  /*  WRITE LONG                      3F */
-       case 0x04:  /*  FORMAT UNIT                     04 */
-                   /*  INITIALIZE ELEMENT STATUS       07 */
-       case 0x07:  /*  REASSIGN BLOCKS                 07 */
        case 0x15:  /*  MODE SELECT(6)                  15 */
-       case 0x24:  /*  SET WINDOW                      24 */
-       case 0x2A:  /*  WRITE(10)                       2A */
-       case 0x2E:  /*  WRITE AND VERIFY(10)            2E */
-       case 0xAE:  /*  WRITE AND VERIFY(12)            AE */
-       case 0xB0:  /*  SEARCH DATA HIGH(12)            B0 */
-       case 0xB1:  /*  SEARCH DATA EQUAL(12)           B1 */
-       case 0xB2:  /*  SEARCH DATA LOW(12)             B2 */
-                   /*  OBJECT POSITION                 31 */
-       case 0x30:  /*  SEARCH DATA HIGH(10)            30 */
-       case 0x31:  /*  SEARCH DATA EQUAL(10)           31 */
-       case 0x32:  /*  SEARCH DATA LOW(10)             32 */
-       case 0x38:  /*  MEDIUM SCAN                     38 */
-       case 0x3D:  /*  UPDATE BLOCK                    3D */
-       case 0x41:  /*  WRITE SAME                      41 */
-                   /*  LOAD UNLOAD                     1B */
-                   /*  SCAN                            1B */
-       case 0x1B:  /*  START STOP UNIT                 1B */
                d = XferOut;
                break;
-       case 0x01:  /*  REZERO UNIT                     01 */
-                   /*  SEEK(6)                         0B */
-       case 0x0B:  /*  SLEW AND PRINT                  0B */
-                   /*  SYNCHRONIZE BUFFER              10 */
-       case 0x10:  /*  WRITE FILEMARKS                 10 */
-       case 0x11:  /*  SPACE                           11 */
-       case 0x13:  /*  VERIFY                          13 */
-       case 0x16:  /*  RESERVE UNIT                    16 */
-       case 0x17:  /*  RELEASE UNIT                    17 */
-       case 0x19:  /*  ERASE                           19 */
-                   /*  LOCATE                          2B */
-                   /*  POSITION TO ELEMENT             2B */
-       case 0x2B:  /*  SEEK(10)                        2B */
-       case 0x1E:  /*  PREVENT ALLOW MEDIUM REMOVAL    1E */
-       case 0x2C:  /*  ERASE(10)                       2C */
-       case 0xAC:  /*  ERASE(12)                       AC */
-       case 0x2F:  /*  VERIFY(10)                      2F */
-       case 0xAF:  /*  VERIFY(12)                      AF */
-       case 0x33:  /*  SET LIMITS(10)                  33 */
-       case 0xB3:  /*  SET LIMITS(12)                  B3 */
-       case 0x35:  /*  SYNCHRONIZE CACHE               35 */
-       case 0x36:  /*  LOCK UNLOCK CACHE               36 */
-       case 0x45:  /*  PLAY AUDIO(10)                  45 */
-       case 0x47:  /*  PLAY AUDIO MSF                  47 */
-       case 0x48:  /*  PLAY AUDIO TRACK/INDEX          48 */
-       case 0x49:  /*  PLAY TRACK RELATIVE(10)         49 */
-       case 0xA9:  /*  PLAY TRACK RELATIVE(12)         A9 */
-       case 0x4B:  /*  PAUSE/RESUME                    4B */
-                   /*  MOVE MEDIUM                     A5 */
-       case 0xA5:  /*  PLAY AUDIO(12)                  A5 */
-       case 0xA6:  /*  EXCHANGE MEDIUM                 A6 */
-       case 0xB5:  /*  REQUEST VOLUME ELEMENT ADDRESS  B5 */
+       case 0x00:  /*  TEST UNIT READY                 00 */
                d = XferNone;
                break;
        default:
-               d = XferIn;
+               d = XferBoth;
                break;
        }
 
@@ -7940,6 +9692,8 @@ static int guess_xfer_direction(int opcode)
 **=========================================================================
 */
 
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
+
 #define is_digit(c)    ((c) >= '0' && (c) <= '9')
 #define digit_to_bin(c)        ((c) - '0')
 #define is_space(c)    ((c) == ' ' || (c) == '\t')
@@ -8021,6 +9775,10 @@ static int ncr_user_command(ncb_p np, char *buffer, int length)
                uc->cmd = UC_SETFLAG;
        else if ((arg_len = is_keyword(ptr, len, "clearprof")) != 0)
                uc->cmd = UC_CLEARPROF;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+       else if ((arg_len = is_keyword(ptr, len, "debug_error_recovery")) != 0)
+               uc->cmd = UC_DEBUG_ERROR_RECOVERY;
+#endif
        else
                arg_len = 0;
 
@@ -8038,13 +9796,16 @@ printf("ncr_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd);
        case UC_SETWIDE:
        case UC_SETFLAG:
                SKIP_SPACES(1);
-               GET_INT_ARG(target);
+               if ((arg_len = is_keyword(ptr, len, "all")) != 0) {
+                       ptr += arg_len; len -= arg_len;
+                       uc->target = ~0;
+               } else {
+                       GET_INT_ARG(target);
+                       uc->target = (1<<target);
 #ifdef DEBUG_PROC_INFO
 printf("ncr_user_command: target=%ld\n", target);
 #endif
-               if (target > MAX_TARGET)
-                       return -EINVAL;
-               uc->target = (1<<target);
+               }
                break;
        }
 
@@ -8074,6 +9835,8 @@ printf("ncr_user_command: data=%ld\n", uc->data);
                        SKIP_SPACES(1);
                        if      ((arg_len = is_keyword(ptr, len, "alloc")))
                                uc->data |= DEBUG_ALLOC;
+                       else if ((arg_len = is_keyword(ptr, len, "phase")))
+                               uc->data |= DEBUG_PHASE;
                        else if ((arg_len = is_keyword(ptr, len, "poll")))
                                uc->data |= DEBUG_POLL;
                        else if ((arg_len = is_keyword(ptr, len, "queue")))
@@ -8086,6 +9849,8 @@ printf("ncr_user_command: data=%ld\n", uc->data);
                                uc->data |= DEBUG_SCRIPT;
                        else if ((arg_len = is_keyword(ptr, len, "tiny")))
                                uc->data |= DEBUG_TINY;
+                       else if ((arg_len = is_keyword(ptr, len, "timing")))
+                               uc->data |= DEBUG_TIMING;
                        else if ((arg_len = is_keyword(ptr, len, "nego")))
                                uc->data |= DEBUG_NEGO;
                        else if ((arg_len = is_keyword(ptr, len, "tags")))
@@ -8107,24 +9872,37 @@ printf("ncr_user_command: data=%ld\n", uc->data);
                        SKIP_SPACES(1);
                        if      ((arg_len = is_keyword(ptr, len, "trace")))
                                uc->data |= UF_TRACE;
+                       else if ((arg_len = is_keyword(ptr, len, "no_disc")))
+                               uc->data |= UF_NODISC;
                        else
                                return -EINVAL;
                        ptr += arg_len; len -= arg_len;
                }
                break;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+       case UC_DEBUG_ERROR_RECOVERY:
+               SKIP_SPACES(1);
+               if      ((arg_len = is_keyword(ptr, len, "sge")))
+                       uc->data = 1;
+               else if ((arg_len = is_keyword(ptr, len, "abort")))
+                       uc->data = 2;
+               else if ((arg_len = is_keyword(ptr, len, "reset")))
+                       uc->data = 3;
+               else if ((arg_len = is_keyword(ptr, len, "parity")))
+                       uc->data = 4;
+               else if ((arg_len = is_keyword(ptr, len, "none")))
+                       uc->data = 0;
+               else
+                       return -EINVAL;
+               ptr += arg_len; len -= arg_len;
+               break;
+#endif
        default:
                break;
        }
 
-       /*
-       ** Not allow to disable tagged queue
-       */ 
-       if (uc->cmd == UC_SETTAGS && uc->data < 1)
-               return -EINVAL;
-
        if (len)
                return -EINVAL;
-#ifdef SCSI_NCR_USER_COMMAND
        else {
                long flags;
 
@@ -8132,10 +9910,13 @@ printf("ncr_user_command: data=%ld\n", uc->data);
                ncr_usercmd (np);
                restore_flags(flags);
        }
-#endif
        return length;
 }
 
+#endif /* SCSI_NCR_USER_COMMAND_SUPPORT */
+
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
 struct info_str
 {
        char *buffer;
@@ -8182,6 +9963,8 @@ static int copy_info(struct info_str *info, char *fmt, ...)
 **     Copy formatted profile information into the input buffer.
 */
 
+#define to_ms(t) ((t) * 1000 / HZ)
+
 static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
 {
        struct info_str info;
@@ -8192,7 +9975,7 @@ static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
        info.pos        = 0;
 
        copy_info(&info, "General information:\n");
-       copy_info(&info, "  Chip NCR53C%03d, ", np->chip);
+       copy_info(&info, "  Chip NCR53C%s, ",   np->chip_name);
        copy_info(&info, "device id 0x%x, ",    np->device_id);
        copy_info(&info, "revision id 0x%x\n",  np->revision_id);
 
@@ -8200,12 +9983,19 @@ static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
        copy_info(&info, "IRQ number %d\n", (int) np->irq);
 
 #ifndef NCR_IOMAPPED
-       if (np->use_mmio)
+       if (np->reg)
                copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
-                                 (u_long) np->reg_remapped);
+                                 (u_long) np->reg);
 #endif
+       copy_info(&info, "  Synchronous period factor %d, ", (int) np->minsync);
+       copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
+
+       if (driver_setup.debug || driver_setup.verbose > 1) {
+               copy_info(&info, "  Debug flags 0x%x, ", driver_setup.debug);
+               copy_info(&info, "verbosity level %d\n", driver_setup.verbose);
+       }
 
-#ifdef SCSI_NCR_PROFILE
+#ifdef SCSI_NCR_PROFILE_SUPPORT
        copy_info(&info, "Profiling information:\n");
        copy_info(&info, "  %-12s = %lu\n", "num_trans",np->profile.num_trans);
        copy_info(&info, "  %-12s = %lu\n", "num_kbytes",np->profile.num_kbytes);
@@ -8213,15 +10003,17 @@ static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
        copy_info(&info, "  %-12s = %lu\n", "num_break",np->profile.num_break);
        copy_info(&info, "  %-12s = %lu\n", "num_int",  np->profile.num_int);
        copy_info(&info, "  %-12s = %lu\n", "num_fly",  np->profile.num_fly);
-       copy_info(&info, "  %-12s = %lu\n", "ms_setup", np->profile.ms_setup);
-       copy_info(&info, "  %-12s = %lu\n", "ms_data",  np->profile.ms_data);
-       copy_info(&info, "  %-12s = %lu\n", "ms_disc",  np->profile.ms_disc);
-       copy_info(&info, "  %-12s = %lu\n", "ms_post",  np->profile.ms_post);
+       copy_info(&info, "  %-12s = %lu\n", "ms_setup", to_ms(np->profile.ms_setup));
+       copy_info(&info, "  %-12s = %lu\n", "ms_data",  to_ms(np->profile.ms_data));
+       copy_info(&info, "  %-12s = %lu\n", "ms_disc",  to_ms(np->profile.ms_disc));
+       copy_info(&info, "  %-12s = %lu\n", "ms_post",  to_ms(np->profile.ms_post));
 #endif
        
        return info.pos > info.offset? info.pos - info.offset : 0;
 }
 
+#endif /* SCSI_NCR_USER_INFO_SUPPORT */
+
 /*
 **     Entry point of the scsi proc fs of the driver.
 **     - func = 0 means read  (returns profile data)
@@ -8243,7 +10035,7 @@ printf("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
        for (host = first_host; host; host = host->next) {
                if (host->hostt == the_template && host->host_no == hostno) {
                        host_data = (struct host_data *) host->hostdata;
-                       ncb = &host_data->ncb_data;
+                       ncb = host_data->ncb;
                        break;
                }
        }
@@ -8252,26 +10044,486 @@ printf("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
                return -EINVAL;
 
        if (func) {
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
                retv = ncr_user_command(ncb, buffer, length);
-#ifdef DEBUG_PROC_INFO
-printf("ncr_user_command: retv=%d\n", retv);
+#else
+               retv = -EINVAL;
 #endif
        }
        else {
                if (start)
                        *start = buffer;
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
                retv = ncr_host_info(ncb, buffer, offset, length);
+#else
+               retv = -EINVAL;
+#endif
        }
 
        return retv;
 }
 
+
 /*=========================================================================
 **     End of proc file system stuff
 **=========================================================================
 */
 #endif
 
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/* ---------------------------------------------------------------------
+**
+**     Try reading Symbios format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in/data out
+** GPIO1 - clock
+**
+**     return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
+static void nvram_start(ncr_slot *np, u_char *gpreg);
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
+static void nvram_stop(ncr_slot *np, u_char *gpreg);
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
+
+__initfunc(
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+)
+{
+       static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+       u_char  gpcntl, gpreg;
+       u_char  old_gpcntl, old_gpreg;
+       u_short csum;
+       u_char  ack_data;
+       int     retv = 1;
+
+       /* save current state of GPCNTL and GPREG */
+       old_gpreg       = INB (nc_gpreg);
+       old_gpcntl      = INB (nc_gpcntl);
+       gpcntl          = old_gpcntl & 0xfc;
+
+       /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+       OUTB (nc_gpreg,  old_gpreg);
+       OUTB (nc_gpcntl, gpcntl);
+
+       /* this is to set NVRAM into a known state with GPIO0/1 both low */
+       gpreg = old_gpreg;
+       nvram_setBit(np, 0, &gpreg, CLR_CLK);
+       nvram_setBit(np, 0, &gpreg, CLR_BIT);
+               
+       /* now set NVRAM inactive with GPIO0/1 both high */
+       nvram_stop(np, &gpreg);
+       
+       /* activate NVRAM */
+       nvram_start(np, &gpreg);
+
+       /* write device code and random address MSB */
+       nvram_write_byte(np, &ack_data,
+               0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+       if (ack_data & 0x01)
+               goto out;
+
+       /* write random address LSB */
+       nvram_write_byte(np, &ack_data,
+               (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
+       if (ack_data & 0x01)
+               goto out;
+
+       /* regenerate START state to set up for reading */
+       nvram_start(np, &gpreg);
+       
+       /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+       nvram_write_byte(np, &ack_data,
+               0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+       if (ack_data & 0x01)
+               goto out;
+
+       /* now set up GPIO0 for inputting data */
+       gpcntl |= 0x01;
+       OUTB (nc_gpcntl, gpcntl);
+               
+       /* input all active data - only part of total NVRAM */
+       csum = nvram_read_data(np,
+                       (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
+
+       /* finally put NVRAM back in inactive mode */
+       gpcntl &= 0xfe;
+       OUTB (nc_gpcntl, gpcntl);
+       nvram_stop(np, &gpreg);
+       
+#ifdef SCSI_NCR_DEBUG_NVRAM
+printf("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
+       nvram->start_marker,
+       nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
+       nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
+       nvram->byte_count, sizeof(*nvram) - 12,
+       nvram->checksum, csum);
+#endif
+
+       /* check valid NVRAM signature, verify byte count and checksum */
+       if (nvram->start_marker == 0 &&
+           !memcmp(nvram->trailer, Symbios_trailer, 6) &&
+           nvram->byte_count == sizeof(*nvram) - 12 &&
+           csum == nvram->checksum)
+               retv = 0;
+out:
+       /* return GPIO0/1 to original states after having accessed NVRAM */
+       OUTB (nc_gpcntl, old_gpcntl);
+       OUTB (nc_gpreg,  old_gpreg);
+
+       return retv;
+}
+
+/*
+ * Read Symbios NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
+)
+{
+       int     x;
+       u_short csum;
+
+       for (x = 0; x < len; x++) 
+               nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
+
+       for (x = 6, csum = 0; x < len - 6; x++)
+               csum += data[x];
+
+       return csum;
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+__initfunc(
+static void nvram_start(ncr_slot *np, u_char *gpreg)
+)
+{
+       nvram_setBit(np, 1, gpreg, SET_BIT);
+       nvram_setBit(np, 0, gpreg, SET_CLK);
+       nvram_setBit(np, 0, gpreg, CLR_BIT);
+       nvram_setBit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+__initfunc(
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+       int x;
+       
+       for (x = 0; x < 8; x++)
+               nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+               
+       nvram_readAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+__initfunc(
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+       int x;
+       u_char read_bit;
+
+       *read_data = 0;
+       for (x = 0; x < 8; x++) {
+               nvram_doBit(np, &read_bit, 1, gpreg);
+               *read_data |= ((read_bit & 0x01) << (7 - x));
+       }
+
+       nvram_writeAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+__initfunc(
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+       OUTB (nc_gpcntl, *gpcntl & 0xfe);
+       nvram_doBit(np, 0, write_bit, gpreg);
+       OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+__initfunc(
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+       OUTB (nc_gpcntl, *gpcntl | 0x01);
+       nvram_doBit(np, read_bit, 1, gpreg);
+       OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+__initfunc(
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+)
+{
+       nvram_setBit(np, write_bit, gpreg, SET_BIT);
+       nvram_setBit(np, 0, gpreg, SET_CLK);
+       if (read_bit)
+               *read_bit = INB (nc_gpreg);
+       nvram_setBit(np, 0, gpreg, CLR_CLK);
+       nvram_setBit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+__initfunc(
+static void nvram_stop(ncr_slot *np, u_char *gpreg)
+)
+{
+       nvram_setBit(np, 0, gpreg, SET_CLK);
+       nvram_setBit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+__initfunc(
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+)
+{
+       DELAY(5);
+       switch (bit_mode){
+       case SET_BIT:
+               *gpreg |= write_bit;
+               break;
+       case CLR_BIT:
+               *gpreg &= 0xfe;
+               break;
+       case SET_CLK:
+               *gpreg |= 0x02;
+               break;
+       case CLR_CLK:
+               *gpreg &= 0xfd;
+               break;
+
+       }
+       OUTB (nc_gpreg, *gpreg);
+       DELAY(5);
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+
+/* ---------------------------------------------------------------------
+**
+**     Try reading Tekram format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in
+** GPIO1 - data out
+** GPIO2 - clock
+** GPIO4 - chip select
+**
+**     return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
+
+__initfunc(
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
+)
+{
+       u_char gpcntl, gpreg;
+       u_char old_gpcntl, old_gpreg;
+       u_short csum;
+
+       /* save current state of GPCNTL and GPREG */
+       old_gpreg       = INB (nc_gpreg);
+       old_gpcntl      = INB (nc_gpcntl);
+
+       /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+          1/2/4 out */
+       gpreg = old_gpreg & 0xe9;
+       OUTB (nc_gpreg, gpreg);
+       gpcntl = (old_gpcntl & 0xe9) | 0x09;
+       OUTB (nc_gpcntl, gpcntl);
+
+       /* input all of NVRAM, 64 words */
+       csum = Tnvram_read_data(np, (u_short *) nvram,
+                       sizeof(*nvram) / sizeof(short), &gpreg);
+       
+       /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+       OUTB (nc_gpcntl, old_gpcntl);
+       OUTB (nc_gpreg,  old_gpreg);
+
+       /* check data valid */
+       if (csum != 0x1234)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Read Tekram NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
+)
+{
+       u_char  read_bit;
+       u_short csum;
+       int     x;
+
+       for (x = 0, csum = 0; x < len; x++)  {
+
+               /* output read command and address */
+               Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+               if (read_bit & 0x01)
+                       return 0; /* Force bad checksum */
+
+               Tnvram_Read_Word(np, &data[x], gpreg);
+               csum += data[x];
+
+               Tnvram_Stop(np, gpreg);
+       }
+
+       return csum;
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+__initfunc(
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
+)
+{
+       int x;
+
+       /* send 9 bits, start bit (1), command (2), address (6)  */
+       for (x = 0; x < 9; x++)
+               Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+       *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ a byte from the NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+)
+{
+       int x;
+       u_char read_bit;
+
+       *nvram_data = 0;
+       for (x = 0; x < 16; x++) {
+               Tnvram_Read_Bit(np, &read_bit, gpreg);
+
+               if (read_bit & 0x01)
+                       *nvram_data |=  (0x01 << (15 - x));
+               else
+                       *nvram_data &= ~(0x01 << (15 - x));
+       }
+}
+
+/* 
+ * Read bit from NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+)
+{
+       DELAY(2);
+       Tnvram_Clk(np, gpreg);
+       *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+__initfunc(
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+)
+{
+       if (write_bit & 0x01)
+               *gpreg |= 0x02;
+       else
+               *gpreg &= 0xfd;
+               
+       *gpreg |= 0x10;
+               
+       OUTB (nc_gpreg, *gpreg);
+       DELAY(2);
+
+       Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+__initfunc(
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
+)
+{
+       *gpreg &= 0xef;
+       OUTB (nc_gpreg, *gpreg);
+       DELAY(2);
+
+       Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+__initfunc(
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+)
+{
+       OUTB (nc_gpreg, *gpreg | 0x04);
+       DELAY(2);
+       OUTB (nc_gpreg, *gpreg);
+}
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
 /*
 **     Module stuff
 */
index cb01aca8a083cd3ebb4da7e0a7e7325d27c9a3f2..ba78224cc07f5db2a4ded6fcd08a92055b4a9563 100644 (file)
 #ifndef NCR53C8XX_H
 #define NCR53C8XX_H
 
-/*********** LINUX SPECIFIC SECTION ******************/
+/*
+**     Name and revision of the driver
+*/
+#define SCSI_NCR_DRIVER_NAME           "ncr53c8xx - revision 2.4"
 
 /*
 **     Check supported Linux versions
 #define LINUX_VERSION_CODE LinuxVersionCode(1,2,13)
 #endif
 
-#if !defined(VERSION)
-#define VERSION                ((LINUX_VERSION_CODE >> 16) & 0xff)
-#define PATCHLEVEL     ((LINUX_VERSION_CODE >> 8)  & 0xff)
-#define SUBLEVEL       ((LINUX_VERSION_CODE >> 0)  & 0xff)
-#endif
-
-#if    VERSION == 0 || VERSION > 3
-#      error Only Linux version 1 and probable 2 or 3 supported.
-#endif
-
-#if    VERSION == 1 && PATCHLEVEL == 2
-#      if      SUBLEVEL != 13
-#              error Only sublevel 13 of Linux 1.2 is supported.
-#      endif
-#endif
-
-#if    VERSION == 1 && PATCHLEVEL == 3
-#      if      SUBLEVEL < 45
-#              error Only sublevels >=45 of Linux 1.3 are supported.
-#      endif
-#endif
-
 /*
 **     Normal IO or memory mapped IO.
 **
 #endif
 
 /*
-**     Avoid to change these constants, unless you know what you are doing.
+**     If you want a driver as small as possible, donnot define the 
+**     following options.
+*/
+
+#define SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+#define SCSI_NCR_DEBUG_INFO_SUPPORT
+#define SCSI_NCR_PCI_FIX_UP_SUPPORT
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
+#      define  SCSI_NCR_PROFILE_SUPPORT
+#      define  SCSI_NCR_USER_COMMAND_SUPPORT
+#      define  SCSI_NCR_USER_INFO_SUPPORT
+/* #   define  SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT */
+#endif
+
+/*==========================================================
+**
+** nvram settings - #define SCSI_NCR_NVRAM_SUPPORT to enable
+**
+**==========================================================
+*/
+
+#ifdef CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+#define SCSI_NCR_NVRAM_SUPPORT
+/* #define SCSI_NCR_DEBUG_NVRAM */
+#endif
+
+/* ---------------------------------------------------------------------
+** Take into account kernel configured parameters.
+** Most of these options can be overridden at startup by a command line.
+** ---------------------------------------------------------------------
 */
 
+/*
+ * 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_ULTRA_SCSI              (2)
+#define SCSI_NCR_MAX_SYNC                      (40)
+
+/*
+ * Allow tags from 2 to 12, default 4
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#if    CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2
+#define SCSI_NCR_MAX_TAGS      (2)
+#elif  CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 12
+#define SCSI_NCR_MAX_TAGS      (12)
+#else
+#define        SCSI_NCR_MAX_TAGS       CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#endif
+#else
 #define SCSI_NCR_MAX_TAGS      (4)
-#define SCSI_NCR_ALWAYS_SIMPLE_TAG
+#endif
 
-#ifdef CONFIG_SCSI_NCR53C8XX_IOMAPPED
+/*
+ * Allow tagged command queuing support if configured with default number 
+ * of tags set to max (see above).
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
+#define        SCSI_NCR_SETUP_DEFAULT_TAGS     SCSI_NCR_MAX_TAGS
+#else
+#define        SCSI_NCR_SETUP_DEFAULT_TAGS     (0)
+#endif
+
+/*
+ * Use normal IO if configured. Forced for alpha.
+ */
+#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) || defined(__alpha__)
 #define        SCSI_NCR_IOMAPPED
 #endif
 
-#ifndef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
-#define SCSI_NCR_TAGGED_QUEUE_DISABLED
+/*
+ * Sync transfer frequency at startup.
+ * Allow from 5Mhz to 40Mhz default 10 Mhz.
+ */
+#ifndef        CONFIG_SCSI_NCR53C8XX_SYNC
+#define        CONFIG_SCSI_NCR53C8XX_SYNC      (5)
+#elif  CONFIG_SCSI_NCR53C8XX_SYNC > SCSI_NCR_MAX_SYNC
+#define        SCSI_NCR_SETUP_DEFAULT_SYNC     SCSI_NCR_MAX_SYNC
 #endif
 
-#ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
-#define SCSI_NCR_NO_DISCONNECT
+#if    CONFIG_SCSI_NCR53C8XX_SYNC == 0
+#define        SCSI_NCR_SETUP_DEFAULT_SYNC     (255)
+#elif  CONFIG_SCSI_NCR53C8XX_SYNC <= 5
+#define        SCSI_NCR_SETUP_DEFAULT_SYNC     (50)
+#elif  CONFIG_SCSI_NCR53C8XX_SYNC <= 20
+#define        SCSI_NCR_SETUP_DEFAULT_SYNC     (250/(CONFIG_SCSI_NCR53C8XX_SYNC))
+#elif  CONFIG_SCSI_NCR53C8XX_SYNC <= 33
+#define        SCSI_NCR_SETUP_DEFAULT_SYNC     (11)
+#else
+#define        SCSI_NCR_SETUP_DEFAULT_SYNC     (10)
 #endif
 
-#ifdef CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS
-#define SCSI_NCR_FORCE_ASYNCHRONOUS
+/*
+ * Disallow disconnections at boot-up
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
+#define SCSI_NCR_SETUP_DISCONNECTION   (0)
+#else
+#define SCSI_NCR_SETUP_DISCONNECTION   (1)
 #endif
 
+/*
+ * Force synchronous negotiation for all targets
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-#define SCSI_NCR_FORCE_SYNC_NEGO
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (1)
+#else
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (0)
 #endif
 
+/*
+ * Disable master parity checking (flawed hardwares need that)
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-#define SCSI_NCR_DISABLE_MPARITY_CHECK
+#define SCSI_NCR_SETUP_MASTER_PARITY   (0)
+#else
+#define SCSI_NCR_SETUP_MASTER_PARITY   (1)
 #endif
 
+/*
+ * Disable scsi parity checking (flawed devices may need that)
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
-#define SCSI_NCR_DISABLE_PARITY_CHECK
+#define SCSI_NCR_SETUP_SCSI_PARITY     (0)
+#else
+#define SCSI_NCR_SETUP_SCSI_PARITY     (1)
 #endif
 
-#if 0
-#define SCSI_NCR_SEGMENT_SIZE  (512)
+/*
+ * Vendor specific stuff
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+#define SCSI_NCR_SETUP_LED_PIN         (1)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT    (3)
+#else
+#define SCSI_NCR_SETUP_LED_PIN         (0)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT    (0)
 #endif
 
-#define SCSI_NCR_MAX_SCATTER   (128)
+/*
+ * Settle time after reset at boot-up
+ */
+#define SCSI_NCR_SETUP_SETTLE_TIME     (2)
+
+/*
+**     Other parameters not configurable with "make config"
+**     Avoid to change these constants, unless you know what you are doing.
+*/
+
+#define SCSI_NCR_ALWAYS_SIMPLE_TAG
+#define SCSI_NCR_MAX_SCATTER   (127)
 #define SCSI_NCR_MAX_TARGET    (16)
 #define SCSI_NCR_MAX_HOST      (2)
-#define SCSI_NCR_SETTLE_TIME   (2)
 #define SCSI_NCR_TIMEOUT_ALERT (3*HZ)
 
 #define SCSI_NCR_CAN_QUEUE     (7*SCSI_NCR_MAX_TAGS)
 #define SCSI_NCR_CMD_PER_LUN   (SCSI_NCR_MAX_TAGS)
-#define SCSI_NCR_SG_TABLESIZE  (SCSI_NCR_MAX_SCATTER-1)
+#define SCSI_NCR_SG_TABLESIZE  (SCSI_NCR_MAX_SCATTER)
+
+#define SCSI_NCR_TIMER_INTERVAL        ((HZ+5-1)/5)
 
 #if 1 /* defined CONFIG_SCSI_MULTI_LUN */
 #define SCSI_NCR_MAX_LUN       (8)
 #define SCSI_NCR_MAX_LUN       (1)
 #endif
 
-#define SCSI_NCR_TIMER_INTERVAL        ((HZ+5-1)/5)
-
 /*
 **     Define Scsi_Host_Template parameters
 **
@@ -196,7 +290,7 @@ int ncr53c8xx_release(struct Scsi_Host *);
 
 #if    LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 
-#define NCR53C8XX {NULL,NULL,NULL,NULL,"ncr53c8xx (rel 1.12d)", ncr53c8xx_detect,\
+#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,\
@@ -207,7 +301,7 @@ int ncr53c8xx_release(struct Scsi_Host *);
 #else
 
 
-#define NCR53C8XX {NULL, NULL, "ncr53c8xx (rel 1.12d)", ncr53c8xx_detect,\
+#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,\
@@ -221,6 +315,200 @@ int ncr53c8xx_release(struct Scsi_Host *);
 
 #ifndef HOSTS_C
 
+/*
+**     NCR53C8XX Device Ids
+*/
+
+#ifndef PCI_DEVICE_ID_NCR_53C810
+#define PCI_DEVICE_ID_NCR_53C810 1
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C810AP
+#define PCI_DEVICE_ID_NCR_53C810AP 5
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C815
+#define PCI_DEVICE_ID_NCR_53C815 4
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C820
+#define PCI_DEVICE_ID_NCR_53C820 2
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C825
+#define PCI_DEVICE_ID_NCR_53C825 3
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C860
+#define PCI_DEVICE_ID_NCR_53C860 6
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C875
+#define PCI_DEVICE_ID_NCR_53C875 0xf
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C875J
+#define PCI_DEVICE_ID_NCR_53C875J 0x8f
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C885
+#define PCI_DEVICE_ID_NCR_53C885 0xd
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C895
+#define PCI_DEVICE_ID_NCR_53C895 0xc
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C896
+#define PCI_DEVICE_ID_NCR_53C896 0xb
+#endif
+
+/*
+**   NCR53C8XX devices features table.
+*/
+typedef struct {
+       unsigned short  device_id;
+       unsigned short  revision_id;
+       char    *name;
+       unsigned char   burst_max;
+       unsigned char   offset_max;
+       unsigned char   nr_divisor;
+       unsigned int    features;
+#define FE_LED0                (1<<0)
+#define FE_WIDE                (1<<1)
+#define FE_ULTRA       (1<<2)
+#define FE_ULTRA2      (1<<3)
+#define FE_DBLR                (1<<4)
+#define FE_QUAD                (1<<5)
+#define FE_ERL         (1<<6)
+#define FE_CLSE                (1<<7)
+#define FE_WRIE                (1<<8)
+#define FE_ERMP                (1<<9)
+#define FE_BOF         (1<<10)
+#define FE_DFS         (1<<11)
+#define FE_PFEN                (1<<12)
+#define FE_LDSTR       (1<<13)
+#define FE_RAM         (1<<14)
+#define FE_CLK80       (1<<15)
+#define FE_CACHE_SET   (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
+#define FE_SCSI_SET    (FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80)
+#define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
+} ncr_chip;
+
+#define SCSI_NCR_CHIP_TABLE                                            \
+{                                                                      \
+ {PCI_DEVICE_ID_NCR_53C810, 0x0f, "810",  4,  8, 4,                    \
+ FE_ERL}                                                               \
+ ,                                                                     \
+ {PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4,  8, 4,                    \
+ FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF}                                 \
+ ,                                                                     \
+ {PCI_DEVICE_ID_NCR_53C815, 0xff, "815",  4,  8, 4,                    \
+ FE_ERL|FE_BOF}                                                                \
+ ,                                                                     \
+ {PCI_DEVICE_ID_NCR_53C820, 0xff, "820",  4,  8, 4,                    \
+ FE_WIDE|FE_ERL}                                                       \
+ ,                                                                     \
+ {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_53C860, 0xff, "860",  4,  8, 5,                    \
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN|FE_RAM}        \
+ ,                                                                     \
+ {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, 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_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_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_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}\
+ ,                                                                     \
+ {PCI_DEVICE_ID_NCR_53C896, 0xff, "896",  7, 31, 7,                    \
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+}
+
+/*
+ * List of supported NCR chip ids
+ */
+#define SCSI_NCR_CHIP_IDS              \
+{                                      \
+       PCI_DEVICE_ID_NCR_53C810,       \
+       PCI_DEVICE_ID_NCR_53C815,       \
+       PCI_DEVICE_ID_NCR_53C820,       \
+       PCI_DEVICE_ID_NCR_53C825,       \
+       PCI_DEVICE_ID_NCR_53C860,       \
+       PCI_DEVICE_ID_NCR_53C875,       \
+       PCI_DEVICE_ID_NCR_53C875J,      \
+       PCI_DEVICE_ID_NCR_53C885,       \
+       PCI_DEVICE_ID_NCR_53C895,       \
+       PCI_DEVICE_ID_NCR_53C896        \
+}
+
+/*
+**     Initial setup.
+**     Can be overriden at startup by a command line.
+*/
+#define SCSI_NCR_DRIVER_SETUP                  \
+{                                              \
+       SCSI_NCR_SETUP_MASTER_PARITY,           \
+       SCSI_NCR_SETUP_SCSI_PARITY,             \
+       SCSI_NCR_SETUP_DISCONNECTION,           \
+       SCSI_NCR_SETUP_SPECIAL_FEATURES,        \
+       SCSI_NCR_SETUP_ULTRA_SCSI,              \
+       SCSI_NCR_SETUP_FORCE_SYNC_NEGO,         \
+       0,                                      \
+       0,                                      \
+       1,                                      \
+       1,                                      \
+       SCSI_NCR_SETUP_DEFAULT_TAGS,            \
+       SCSI_NCR_SETUP_DEFAULT_SYNC,            \
+       0x00,                                   \
+       7,                                      \
+       SCSI_NCR_SETUP_LED_PIN,                 \
+       1,                                      \
+       SCSI_NCR_SETUP_SETTLE_TIME,             \
+       SCSI_NCR_SETUP_DIFF_SUPPORT,            \
+       0                                       \
+}
+
+/*
+**     Boot fail safe setup.
+**     Override initial setup from boot command line:
+**     ncr53c8xx=safe:y
+*/
+#define SCSI_NCR_DRIVER_SAFE_SETUP             \
+{                                              \
+       0,                                      \
+       1,                                      \
+       0,                                      \
+       0,                                      \
+       0,                                      \
+       0,                                      \
+       0,                                      \
+       0,                                      \
+       1,                                      \
+       2,                                      \
+       0,                                      \
+       255,                                    \
+       0x00,                                   \
+       255,                                    \
+       0,                                      \
+       0,                                      \
+       10,                                     \
+       1,                                      \
+       1                                       \
+}
+
 /*
 **     Define the table of target capabilities by host and target
 **
@@ -309,38 +597,6 @@ int ncr53c8xx_release(struct Scsi_Host *);
 #endif
 #endif
 
-/*
-**     NCR53C8XX Device Ids
-*/
-
-#ifndef PCI_DEVICE_ID_NCR_53C810
-#define PCI_DEVICE_ID_NCR_53C810 1
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C810AP
-#define PCI_DEVICE_ID_NCR_53C810AP 5
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C815
-#define PCI_DEVICE_ID_NCR_53C815 4
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C820
-#define PCI_DEVICE_ID_NCR_53C820 2
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C825
-#define PCI_DEVICE_ID_NCR_53C825 3
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C860
-#define PCI_DEVICE_ID_NCR_53C860 6
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C875
-#define PCI_DEVICE_ID_NCR_53C875 0xf
-#endif
-
 /**************** ORIGINAL CONTENT of ncrreg.h from FreeBSD ******************/
 
 /*-----------------------------------------------------------------
@@ -365,6 +621,7 @@ struct ncr_reg {
 
 /*03*/  u_char    nc_scntl3;    /* cnf system clock dependent       */
        #define   EWS     0x08  /* cmd: enable wide scsi         [W]*/
+       #define   ULTRA   0x80  /* cmd: ULTRA enable                */
 
 /*04*/  u_char    nc_scid;     /* cnf host adapter scsi address    */
        #define   RRE     0x40  /* r/w:e enable response to resel.  */
@@ -418,6 +675,7 @@ struct ncr_reg {
         #define   ILF1    0x80  /* sta: data in SIDL register msb[W]*/
         #define   ORF1    0x40  /* sta: data in SODR register msb[W]*/
         #define   OLF1    0x20  /* sta: data in SODL register msb[W]*/
+        #define   DM      0x04  /* sta: DIFFSENS mismatch (895/6 only) */
         #define   LDSC    0x02  /* sta: disconnect & reconnect      */
 
 /*10*/  u_int32    nc_dsa;     /* --> Base page                    */
@@ -443,13 +701,20 @@ struct ncr_reg {
        #define   CSIGP   0x40
 
 /*1b*/  u_char    nc_ctest3;
-       #define   CLF     0x04  /* clear scsi fifo                  */
+       #define   FLF     0x08  /* cmd: flush dma fifo              */
+       #define   CLF     0x04  /* cmd: clear dma fifo              */
+       #define   FM      0x02  /* mod: fetch pin mode              */
+       #define   WRIE    0x01  /* mod: write and invalidate enable */
 
 /*1c*/  u_int32    nc_temp;    /* ### Temporary stack              */
 
 /*20*/ u_char    nc_dfifo;
 /*21*/  u_char    nc_ctest4;
+       #define   BDIS    0x80  /* mod: burst disable               */
+       #define   MPEE    0x08  /* mod: master parity error enable  */
+
 /*22*/  u_char    nc_ctest5;
+       #define   DFS     0x20  /* mod: dma fifo size               */
 /*23*/  u_char    nc_ctest6;
 
 /*24*/  u_int32    nc_dbc;     /* ### Byte count and command       */
@@ -459,18 +724,31 @@ struct ncr_reg {
 /*34*/  u_int32    nc_scratcha;  /* ??? Temporary register a         */
 
 /*38*/  u_char    nc_dmode;
+       #define   BL_2    0x80  /* mod: burst length shift value +2 */
+       #define   BL_1    0x40  /* mod: burst length shift value +1 */
+       #define   ERL     0x08  /* mod: enable read line            */
+       #define   ERMP    0x04  /* mod: enable read multiple        */
+       #define   BOF     0x02  /* mod: burst op code fetch         */
+
 /*39*/  u_char    nc_dien;
 /*3a*/  u_char    nc_dwt;
 
 /*3b*/  u_char    nc_dcntl;    /* --> Script execution control     */
-        #define   SSM     0x10  /* mod: single step mode            */
-        #define   STD     0x04  /* cmd: start dma mode              */
-       #define   NOCOM   0x01  /* cmd: protect sfbr while reselect */
+
+       #define   CLSE    0x80  /* mod: cache line size enable      */
+       #define   PFF     0x40  /* cmd: pre-fetch flush             */
+       #define   PFEN    0x20  /* mod: pre-fetch enable            */
+       #define   SSM     0x10  /* mod: single step mode            */
+       #define   IRQM    0x08  /* mod: irq mode (1 = totem pole !) */
+       #define   STD     0x04  /* cmd: start dma mode              */
+       #define   IRQD    0x02  /* mod: irq disable                 */
+       #define   NOCOM   0x01  /* cmd: protect sfbr while reselect */
 
 /*3c*/  u_int32    nc_adder;
 
 /*40*/  u_short   nc_sien;     /* -->: interrupt enable            */
 /*42*/  u_short   nc_sist;     /* <--: interrupt status            */
+        #define   SBMC    0x1000/* sta: SCSI Bus Mode Change (895/6 only) */
         #define   STO     0x0400/* sta: timeout (select)            */
         #define   GEN     0x0200/* sta: timeout (general)           */
         #define   HTH     0x0100/* sta: timeout (handshake)         */
@@ -496,6 +774,7 @@ struct ncr_reg {
 /*4d*/  u_char    nc_stest1;
        #define   DBLEN   0x08  /* clock doubler running                */
        #define   DBLSEL  0x04  /* clock doubler selected               */
+  
 
 /*4e*/  u_char    nc_stest2;
        #define   ROF     0x40  /* reset scsi offset (after gross error!) */
@@ -503,10 +782,18 @@ struct ncr_reg {
 
 /*4f*/  u_char    nc_stest3;
        #define   TE     0x80   /* c: tolerAnt enable */
+       #define   HSC    0x20   /* c: Halt SCSI Clock */
        #define   CSF    0x02   /* c: clear scsi fifo */
 
 /*50*/  u_short   nc_sidl;     /* Lowlevel: latched from scsi data */
-/*52*/  u_short   nc_52_;
+/*52*/  u_char    nc_stest4;
+       #define   SMODE  0xc0   /* SCSI bus mode      (895/6 only) */
+       #define    SMODE_HVD 0x40       /* High Voltage Differential       */
+       #define    SMODE_SE  0x80       /* Single Ended                    */
+       #define    SMODE_LVD 0xc0       /* Low Voltage Differential        */
+       #define   LCKFRQ 0x20   /* Frequency Lock (895/6 only)     */
+
+/*53*/ u_char    nc_53_;
 /*54*/  u_short   nc_sodl;     /* Lowlevel: data out to scsi data  */
 /*56*/  u_short   nc_56_;
 /*58*/  u_short   nc_sbdl;     /* Lowlevel: data from scsi data    */
@@ -659,10 +946,18 @@ struct scr_tblsel {
 **     << source_address >>
 **     << destination_address >>
 **
+**     SCR_COPY   sets the NO FLUSH option by default.
+**     SCR_COPY_F does not set this option.
+**
+**     For chips which do not support this option,
+**     ncr_copy_and_bind() will remove this bit.
 **-----------------------------------------------------------
 */
 
-#define SCR_COPY(n) (0xc0000000 | (n))
+#define SCR_NO_FLUSH 0x01000000
+
+#define SCR_COPY(n) (0xc0000000 | SCR_NO_FLUSH | (n))
+#define SCR_COPY_F(n) (0xc0000000 | (n))
 
 /*-----------------------------------------------------------
 **
@@ -768,6 +1063,7 @@ struct scr_tblsel {
 **-----------------------------------------------------------
 */
 
+#define SCR_NO_OP        0x80000000
 #define SCR_JUMP        0x80080000
 #define SCR_JUMPR       0x80880000
 #define SCR_CALL        0x88080000
diff --git a/drivers/scsi/scsiiom.c b/drivers/scsi/scsiiom.c
new file mode 100644 (file)
index 0000000..97801d7
--- /dev/null
@@ -0,0 +1,1540 @@
+/***********************************************************************
+ *     FILE NAME : SCSIIOM.C                                          *
+ *          BY   : C.L. Huang,    ching@tekram.com.tw                 *
+ *     Description: Device Driver for Tekram DC-390 (T) PCI SCSI      *
+ *                  Bus Master Host Adapter                           *
+ ***********************************************************************/
+
+
+static USHORT
+DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+    USHORT ioport, rc;
+    UCHAR  bval, bval1, i, cnt;
+    PUCHAR ptr;
+    ULONG  wlval;
+
+    pSRB->TagNumber = 31;
+    ioport = pACB->IOPortBase;
+    bval = pDCB->UnitSCSIID;
+    outb(bval,ioport+Scsi_Dest_ID);
+    bval = pDCB->SyncPeriod;
+    outb(bval,ioport+Sync_Period);
+    bval = pDCB->SyncOffset;
+    outb(bval,ioport+Sync_Offset);
+    bval = pDCB->CtrlR1;
+    outb(bval,ioport+CtrlReg1);
+    bval = pDCB->CtrlR3;
+    outb(bval,ioport+CtrlReg3);
+    bval = pDCB->CtrlR4;
+    outb(bval,ioport+CtrlReg4);
+    bval = CLEAR_FIFO_CMD;        /* Flush FIFO */
+    outb(bval,ioport+ScsiCmd);
+
+    pSRB->ScsiPhase = SCSI_NOP0;
+    bval = pDCB->IdentifyMsg;
+    if( !(pDCB->SyncMode & EN_ATN_STOP) )
+    {
+       if( (pSRB->CmdBlock[0] == INQUIRY) ||
+           (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+           (pSRB->SRBFlag & AUTO_REQSENSE) )
+       {
+           bval &= 0xBF;       /* NO disconnection */
+           outb(bval,ioport+ScsiFifo);
+           bval1 = SELECT_W_ATN;
+           pSRB->SRBState = SRB_START_;
+           if( pDCB->SyncMode & SYNC_ENABLE )
+           {
+               if( !(pDCB->IdentifyMsg & 7) ||
+                   (pSRB->CmdBlock[0] != INQUIRY) )
+               {
+                   bval1 = SEL_W_ATN_STOP;
+                   pSRB->SRBState = SRB_MSGOUT;
+               }
+           }
+       }
+       else
+       {
+           if(pDCB->SyncMode & EN_TAG_QUEUING)
+           {
+               outb(bval,ioport+ScsiFifo);
+               bval = MSG_SIMPLE_QTAG;
+               outb(bval,ioport+ScsiFifo);
+               wlval = 1;
+               bval = 0;
+               while( wlval & pDCB->TagMask )
+               {
+                   wlval = wlval << 1;
+                   bval++;
+               }
+               outb(bval,ioport+ScsiFifo);
+               pDCB->TagMask |= wlval;
+               pSRB->TagNumber = bval;
+               bval1 = SEL_W_ATN2;
+               pSRB->SRBState = SRB_START_;
+           }
+           else
+           {
+               outb(bval,ioport+ScsiFifo);
+               bval1 = SELECT_W_ATN;
+               pSRB->SRBState = SRB_START_;
+           }
+       }
+
+       if( pSRB->SRBFlag & AUTO_REQSENSE )
+       {
+           bval = REQUEST_SENSE;
+           outb(bval,ioport+ScsiFifo);
+           bval = pDCB->IdentifyMsg << 5;
+           outb(bval,ioport+ScsiFifo);
+           bval = 0;
+           outb(bval,ioport+ScsiFifo);
+           outb(bval,ioport+ScsiFifo);
+           bval = sizeof(pSRB->pcmd->sense_buffer);
+           outb(bval,ioport+ScsiFifo);
+           bval = 0;
+           outb(bval,ioport+ScsiFifo);
+       }
+       else
+       {
+           cnt = pSRB->ScsiCmdLen;
+           ptr = (PUCHAR) pSRB->CmdBlock;
+           for(i=0; i<cnt; i++)
+           {
+               bval = *ptr++;
+               outb(bval,ioport+ScsiFifo);
+           }
+       }
+    }
+    else       /* ATN_STOP */
+    {
+       if( (pSRB->CmdBlock[0] == INQUIRY) ||
+           (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+           (pSRB->SRBFlag & AUTO_REQSENSE) )
+       {
+           bval &= 0xBF;
+           outb(bval,ioport+ScsiFifo);
+           bval1 = SELECT_W_ATN;
+           pSRB->SRBState = SRB_START_;
+           if( pDCB->SyncMode & SYNC_ENABLE )
+           {
+               if( !(pDCB->IdentifyMsg & 7) ||
+                   (pSRB->CmdBlock[0] != INQUIRY) )
+               {
+                   bval1 = SEL_W_ATN_STOP;
+                   pSRB->SRBState = SRB_MSGOUT;
+               }
+           }
+       }
+       else
+       {
+           if(pDCB->SyncMode & EN_TAG_QUEUING)
+           {
+               outb(bval,ioport+ScsiFifo);
+               pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG;
+               wlval = 1;
+               bval = 0;
+               while( wlval & pDCB->TagMask )
+               {
+                   wlval = wlval << 1;
+                   bval++;
+               }
+               pDCB->TagMask |= wlval;
+               pSRB->TagNumber = bval;
+               pSRB->MsgOutBuf[1] = bval;
+               pSRB->MsgCnt = 2;
+               bval1 = SEL_W_ATN_STOP;
+               pSRB->SRBState = SRB_START_;
+           }
+           else
+           {
+               outb(bval,ioport+ScsiFifo);
+               pSRB->MsgOutBuf[0] = MSG_NOP;
+               pSRB->MsgCnt = 1;
+               pSRB->SRBState = SRB_START_;
+               bval1 = SEL_W_ATN_STOP;
+           }
+       }
+    }
+    bval = inb( ioport+Scsi_Status );
+    if( bval & INTERRUPT )
+    {
+       pSRB->SRBState = SRB_READY;
+       pDCB->TagMask &= ~( 1 << pSRB->TagNumber );
+       rc = 1;
+    }
+    else
+    {
+       pSRB->ScsiPhase = SCSI_NOP1;
+       pACB->pActiveDCB = pDCB;
+       pDCB->pActiveSRB = pSRB;
+       rc = 0;
+       outb(bval1,ioport+ScsiCmd);
+    }
+    return( rc );
+}
+
+
+#ifndef  VERSION_ELF_1_2_13
+static void
+DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void
+DC390_Interrupt( int irq, struct pt_regs *regs)
+#endif
+{
+    PACB   pACB;
+    PDCB   pDCB;
+    PSRB   pSRB;
+    USHORT ioport = 0;
+    USHORT phase, i;
+    void   (*stateV)( PACB, PSRB, PUCHAR );
+    UCHAR  istate = 0;
+    UCHAR  sstatus=0, istatus;
+
+    pACB = pACB_start;
+    if( pACB == NULL )
+       return;
+    for( i=0; i < adapterCnt; i++ )
+    {
+       if( pACB->IRQLevel == (UCHAR) irq )
+       {
+            ioport = pACB->IOPortBase;
+            sstatus = inb( ioport+Scsi_Status );
+            if( sstatus & INTERRUPT )
+                break;
+            else
+               pACB = pACB->pNextACB;
+       }
+       else
+       {
+           pACB = pACB->pNextACB;
+       }
+    }
+
+#ifdef DC390_DEBUG1
+       printk("sstatus=%2x,",sstatus);
+#endif
+
+    if( pACB == (PACB )-1 )
+    {
+       printk("DC390: Spurious interrupt detected!\n");
+       return;
+    }
+
+    istate = inb( ioport+Intern_State );
+    istatus = inb( ioport+INT_Status );
+
+#ifdef DC390_DEBUG1
+       printk("Istatus=%2x,",istatus);
+#endif
+
+    if(istatus &  DISCONNECTED)
+    {
+       DC390_Disconnect( pACB );
+       return;
+    }
+
+    if(istatus &  RESELECTED)
+    {
+       DC390_Reselect( pACB );
+       return;
+    }
+
+    if(istatus &  INVALID_CMD)
+    {
+       DC390_InvalidCmd( pACB );
+       return;
+    }
+
+    if(istatus &  SCSI_RESET)
+    {
+       DC390_ScsiRstDetect( pACB );
+       return;
+    }
+
+    if( istatus & (SUCCESSFUL_OP+SERVICE_REQUEST) )
+    {
+       pDCB = pACB->pActiveDCB;
+       pSRB = pDCB->pActiveSRB;
+       if( pDCB )
+       {
+           if( pDCB->DCBFlag & ABORT_DEV_ )
+               EnableMsgOut( pACB, pSRB );
+       }
+
+       phase = (USHORT) pSRB->ScsiPhase;
+       stateV = (void *) DC390_phase0[phase];
+       stateV( pACB, pSRB, &sstatus );
+
+       pSRB->ScsiPhase = sstatus & 7;
+       phase = (USHORT) sstatus & 7;
+       stateV = (void *) DC390_phase1[phase];
+       stateV( pACB, pSRB, &sstatus );
+    }
+}
+
+
+static void
+DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR   sstatus, bval;
+    USHORT  ioport;
+    PSGL    psgl;
+    ULONG   ResidCnt, xferCnt;
+
+    ioport = pACB->IOPortBase;
+    sstatus = *psstatus;
+
+    if( !(pSRB->SRBState & SRB_XFERPAD) )
+    {
+       if( sstatus & PARITY_ERR )
+           pSRB->SRBStatus |= PARITY_ERROR;
+
+       if( sstatus & COUNT_2_ZERO )
+       {
+           bval = inb(ioport+DMA_Status);
+           while( !(bval & DMA_XFER_DONE) )
+               bval = inb(ioport+DMA_Status);
+           pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+           pSRB->SGIndex++;
+           if( pSRB->SGIndex < pSRB->SGcount )
+           {
+               pSRB->pSegmentList++;
+               psgl = pSRB->pSegmentList;
+
+#ifndef VERSION_ELF_1_2_13
+               pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+               pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+               pSRB->SGToBeXferLen = (ULONG) psgl->length;
+           }
+           else
+               pSRB->SGToBeXferLen = 0;
+       }
+       else
+       {
+           bval = inb( ioport+Current_Fifo );
+           bval &= 0x1f;
+           ResidCnt = (ULONG) inb(ioport+CtcReg_High);
+           ResidCnt = ResidCnt << 8;
+           ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
+           ResidCnt = ResidCnt << 8;
+           ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
+           ResidCnt += (ULONG) bval;
+
+           xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+           pSRB->SGPhysAddr += xferCnt;
+           pSRB->TotalXferredLen += xferCnt;
+           pSRB->SGToBeXferLen = ResidCnt;
+       }
+    }
+    bval = WRITE_DIRECTION+DMA_IDLE_CMD;
+    outb( bval, ioport+DMA_Cmd);
+}
+
+static void
+DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR   sstatus, bval;
+    USHORT  i, ioport, residual;
+    PSGL    psgl;
+    ULONG   ResidCnt, xferCnt;
+    PUCHAR  ptr;
+
+
+    ioport = pACB->IOPortBase;
+    sstatus = *psstatus;
+
+    if( !(pSRB->SRBState & SRB_XFERPAD) )
+    {
+       if( sstatus & PARITY_ERR )
+           pSRB->SRBStatus |= PARITY_ERROR;
+
+       if( sstatus & COUNT_2_ZERO )
+       {
+           bval = inb(ioport+DMA_Status);
+           while( !(bval & DMA_XFER_DONE) )
+               bval = inb(ioport+DMA_Status);
+
+           bval = READ_DIRECTION+DMA_IDLE_CMD;
+           outb( bval, ioport+DMA_Cmd);
+
+           pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+           pSRB->SGIndex++;
+           if( pSRB->SGIndex < pSRB->SGcount )
+           {
+               pSRB->pSegmentList++;
+               psgl = pSRB->pSegmentList;
+
+#ifndef VERSION_ELF_1_2_13
+               pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+               pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+               pSRB->SGToBeXferLen = (ULONG) psgl->length;
+           }
+           else
+               pSRB->SGToBeXferLen = 0;
+       }
+       else    /* phase changed */
+       {
+           residual = 0;
+           bval = inb(ioport+Current_Fifo);
+           while( bval & 0x1f )
+           {
+               if( (bval & 0x1f) == 1 )
+               {
+                   for(i=0; i< 0x100; i++)
+                   {
+                       bval = inb(ioport+Current_Fifo);
+                       if( !(bval & 0x1f) )
+                           goto din_1;
+                       else if( i == 0x0ff )
+                       {
+                           residual = 1;   /* ;1 residual byte */
+                           goto din_1;
+                       }
+                   }
+               }
+               else
+                   bval = inb(ioport+Current_Fifo);
+           }
+din_1:
+           bval = READ_DIRECTION+DMA_BLAST_CMD;
+           outb(bval, ioport+DMA_Cmd);
+           for(i=0; i<0x8000; i++)
+           {
+               bval = inb(ioport+DMA_Status);
+               if(bval & BLAST_COMPLETE)
+                   break;
+           }
+           bval = READ_DIRECTION+DMA_IDLE_CMD;
+           outb(bval, ioport+DMA_Cmd);
+
+           ResidCnt = (ULONG) inb(ioport+CtcReg_High);
+           ResidCnt = ResidCnt << 8;
+           ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
+           ResidCnt = ResidCnt << 8;
+           ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
+
+           xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+           pSRB->SGPhysAddr += xferCnt;
+           pSRB->TotalXferredLen += xferCnt;
+           pSRB->SGToBeXferLen = ResidCnt;
+
+           if( residual )
+           {
+               bval = inb(ioport+ScsiFifo);        /* get residual byte */
+#ifndef VERSION_ELF_1_2_13
+               ptr = (PUCHAR) phys_to_virt( pSRB->SGPhysAddr );
+#else
+               ptr = (PUCHAR) pSRB->SGPhysAddr;
+#endif
+               *ptr = bval;
+               pSRB->SGPhysAddr++;
+               pSRB->TotalXferredLen++;
+               pSRB->SGToBeXferLen--;
+           }
+       }
+    }
+}
+
+static void
+DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+static void
+DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  bval;
+    USHORT ioport;
+
+    ioport = pACB->IOPortBase;
+    bval = inb(ioport+ScsiFifo);
+    pSRB->TargetStatus = bval;
+    bval++;
+    bval = inb(ioport+ScsiFifo);       /* get message */
+    pSRB->EndMessage = bval;
+
+    *psstatus = SCSI_NOP0;
+    pSRB->SRBState = SRB_COMPLETED;
+    bval = MSG_ACCEPTED_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) )
+       *psstatus = SCSI_NOP0;
+}
+
+static void
+DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  bval;
+    USHORT ioport, wval, wval1;
+    PDCB   pDCB;
+    PSRB   psrb;
+
+    ioport = pACB->IOPortBase;
+    pDCB = pACB->pActiveDCB;
+
+    bval = inb( ioport+ScsiFifo );
+    if( !(pSRB->SRBState & SRB_MSGIN_MULTI) )
+    {
+       if(bval == MSG_DISCONNECT)
+       {
+           pSRB->SRBState = SRB_DISCONNECT;
+       }
+       else if( bval == MSG_SAVE_PTR )
+           goto  min6;
+       else if( (bval == MSG_EXTENDED) || ((bval >= MSG_SIMPLE_QTAG) &&
+                                           (bval <= MSG_ORDER_QTAG)) )
+       {
+           pSRB->SRBState |= SRB_MSGIN_MULTI;
+           pSRB->MsgInBuf[0] = bval;
+           pSRB->MsgCnt = 1;
+           pSRB->pMsgPtr = &pSRB->MsgInBuf[1];
+       }
+       else if(bval == MSG_REJECT_)
+       {
+           bval = RESET_ATN_CMD;
+           outb(bval, ioport+ScsiCmd);
+           if( pSRB->SRBState & DO_SYNC_NEGO)
+               goto  set_async;
+       }
+       else if( bval == MSG_RESTORE_PTR)
+           goto  min6;
+       else
+           goto  min6;
+    }
+    else
+    {  /* minx: */
+
+       *pSRB->pMsgPtr = bval;
+       pSRB->MsgCnt++;
+       pSRB->pMsgPtr++;
+       if( (pSRB->MsgInBuf[0] >= MSG_SIMPLE_QTAG) &&
+           (pSRB->MsgInBuf[0] <= MSG_ORDER_QTAG) )
+       {
+           if( pSRB->MsgCnt == 2)
+           {
+               pSRB->SRBState = 0;
+               bval = pSRB->MsgInBuf[1];
+               pSRB = pDCB->pGoingSRB;
+               psrb = pDCB->pGoingLast;
+               if( pSRB )
+               {
+                   for( ;; )
+                   {
+                       if(pSRB->TagNumber != bval)
+                       {
+                           if( pSRB == psrb )
+                               goto  mingx0;
+                           pSRB = pSRB->pNextSRB;
+                       }
+                       else
+                           break;
+                   }
+                   if( pDCB->DCBFlag & ABORT_DEV_ )
+                   {
+                       pSRB->SRBState = SRB_ABORT_SENT;
+                       EnableMsgOut( pACB, pSRB );
+                   }
+                   if( !(pSRB->SRBState & SRB_DISCONNECT) )
+                       goto  mingx0;
+                   pDCB->pActiveSRB = pSRB;
+                   pSRB->SRBState = SRB_DATA_XFER;
+               }
+               else
+               {
+mingx0:
+                   pSRB = pACB->pTmpSRB;
+                   pSRB->SRBState = SRB_UNEXPECT_RESEL;
+                   pDCB->pActiveSRB = pSRB;
+                   pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
+                   EnableMsgOut2( pACB, pSRB );
+               }
+           }
+       }
+       else if( (pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5) )
+       {
+           pSRB->SRBState &= ~(SRB_MSGIN_MULTI+DO_SYNC_NEGO);
+           if( (pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1) )
+           {   /* reject_msg: */
+               pSRB->MsgCnt = 1;
+               pSRB->MsgInBuf[0] = MSG_REJECT_;
+               bval = SET_ATN_CMD;
+               outb(bval, ioport+ScsiCmd);
+           }
+           else if( !(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4]) )
+           {
+set_async:
+               pDCB = pSRB->pSRBDCB;
+               pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
+               pDCB->SyncPeriod = 0;
+               pDCB->SyncOffset = 0;
+               pDCB->CtrlR3 = FAST_CLK;     /* ;non_fast */
+               pDCB->CtrlR4 &= 0x3f;
+               pDCB->CtrlR4 |= EATER_25NS;  /* ; 25ns glitch eater */
+               goto  re_prog;
+           }
+           else
+           {   /* set_sync: */
+
+               pDCB = pSRB->pSRBDCB;
+               pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE;
+               pDCB->SyncOffset &= 0x0f0;
+               pDCB->SyncOffset |= pSRB->MsgInBuf[4];
+               pDCB->NegoPeriod = pSRB->MsgInBuf[3];
+               wval = (USHORT) pSRB->MsgInBuf[3];
+               wval = wval << 2;
+               wval--;
+               wval1 = wval / 25;
+               if( (wval1 * 25) != wval)
+                   wval1++;
+               bval = FAST_CLK+FAST_SCSI;
+               pDCB->CtrlR4 &= 0x3f;
+               if(wval1 >= 8)
+               {
+                   wval1--;
+                   bval = FAST_CLK;        /* ;fast clock/normal scsi */
+                   pDCB->CtrlR4 |= EATER_25NS;   /* ;25 ns glitch eater */
+               }
+               pDCB->CtrlR3 = bval;
+               pDCB->SyncPeriod = (UCHAR)wval1;
+re_prog:
+               bval = pDCB->SyncPeriod;
+               outb(bval, ioport+Sync_Period);
+               bval = pDCB->SyncOffset;
+               outb(bval, ioport+Sync_Offset);
+               bval = pDCB->CtrlR3;
+               outb(bval, ioport+CtrlReg3);
+               bval = pDCB->CtrlR4;
+               outb(bval, ioport+CtrlReg4);
+               SetXferRate( pACB, pDCB);
+           }
+       }
+    }
+min6:
+    *psstatus = SCSI_NOP0;
+    bval = MSG_ACCEPTED_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir)
+{
+    PSGL   psgl;
+    UCHAR  bval;
+    USHORT ioport;
+    ULONG  lval;
+
+
+    ioport = pACB->IOPortBase;
+    if( pSRB->SGIndex < pSRB->SGcount )
+    {
+       bval = DMA_IDLE_CMD | ioDir;   /* ;+EN_DMA_INT */
+       outb( bval, ioport+DMA_Cmd);
+       if( !pSRB->SGToBeXferLen )
+       {
+           psgl = pSRB->pSegmentList;
+#ifndef VERSION_ELF_1_2_13
+           pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+           pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+           pSRB->SGToBeXferLen = (ULONG) psgl->length;
+       }
+       lval = pSRB->SGToBeXferLen;
+       bval = (UCHAR) lval;
+       outb(bval,ioport+CtcReg_Low);
+       lval = lval >> 8;
+       bval = (UCHAR) lval;
+       outb(bval,ioport+CtcReg_Mid);
+       lval = lval >> 8;
+       bval = (UCHAR) lval;
+       outb(bval,ioport+CtcReg_High);
+
+       lval = pSRB->SGToBeXferLen;
+       outl(lval, ioport+DMA_XferCnt);
+
+       lval = pSRB->SGPhysAddr;
+       outl( lval, ioport+DMA_XferAddr);
+
+       bval = DMA_COMMAND+INFO_XFER_CMD;
+       outb(bval, ioport+ScsiCmd);
+
+       pSRB->SRBState = SRB_DATA_XFER;
+
+       bval = DMA_IDLE_CMD | ioDir;    /* ;+EN_DMA_INT */
+       outb(bval, ioport+DMA_Cmd);
+
+       bval = DMA_START_CMD | ioDir;   /* ;+EN_DMA_INT */
+       outb(bval, ioport+DMA_Cmd);
+    }
+    else    /* xfer pad */
+    {
+       if( pSRB->SGcount )
+       {
+           pSRB->AdaptStatus = H_OVER_UNDER_RUN;
+           pSRB->SRBStatus |= OVER_RUN;
+       }
+       bval = 0;
+       outb(bval,ioport+CtcReg_Low);
+       outb(bval,ioport+CtcReg_Mid);
+       outb(bval,ioport+CtcReg_High);
+
+       pSRB->SRBState |= SRB_XFERPAD;
+       bval = DMA_COMMAND+XFER_PAD_BYTE;
+       outb(bval, ioport+ScsiCmd);
+/*
+       bval = DMA_IDLE_CMD | ioDir;    ;+EN_DMA_INT
+       outb(bval, ioport+DMA_Cmd);
+       bval = DMA_START_CMD | ioDir;   ;+EN_DMA_INT
+       outb(bval, ioport+DMA_Cmd);
+*/
+    }
+}
+
+
+static void
+DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  ioDir;
+
+    ioDir = WRITE_DIRECTION;
+    DataIO_Comm( pACB, pSRB, ioDir);
+}
+
+static void
+DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  ioDir;
+
+    ioDir = READ_DIRECTION;
+    DataIO_Comm( pACB, pSRB, ioDir);
+}
+
+static void
+DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    PDCB   pDCB;
+    UCHAR  bval;
+    PUCHAR ptr;
+    USHORT ioport, i,  cnt;
+
+
+    ioport = pACB->IOPortBase;
+    bval = RESET_ATN_CMD;
+    outb(bval, ioport+ScsiCmd);
+    bval = CLEAR_FIFO_CMD;
+    outb(bval, ioport+ScsiCmd);
+    if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
+    {
+       cnt = (USHORT) pSRB->ScsiCmdLen;
+       ptr = (PUCHAR) pSRB->CmdBlock;
+       for(i=0; i < cnt; i++)
+       {
+           outb(*ptr, ioport+ScsiFifo);
+           ptr++;
+       }
+    }
+    else
+    {
+       bval = REQUEST_SENSE;
+       outb(bval, ioport+ScsiFifo);
+       pDCB = pACB->pActiveDCB;
+       bval = pDCB->IdentifyMsg << 5;
+       outb(bval, ioport+ScsiFifo);
+       bval = 0;
+       outb(bval, ioport+ScsiFifo);
+       outb(bval, ioport+ScsiFifo);
+       bval = sizeof(pSRB->pcmd->sense_buffer);
+       outb(bval, ioport+ScsiFifo);
+       bval = 0;
+       outb(bval, ioport+ScsiFifo);
+    }
+    pSRB->SRBState = SRB_COMMAND;
+    bval = INFO_XFER_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  bval;
+    USHORT ioport;
+
+    ioport = pACB->IOPortBase;
+    bval = CLEAR_FIFO_CMD;
+    outb(bval, ioport+ScsiCmd);
+    pSRB->SRBState = SRB_STATUS;
+    bval = INITIATOR_CMD_CMPLTE;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR   bval;
+    USHORT  ioport, i, cnt;
+    PUCHAR  ptr;
+    PDCB    pDCB;
+
+    ioport = pACB->IOPortBase;
+    bval = CLEAR_FIFO_CMD;
+    outb(bval, ioport+ScsiCmd);
+    pDCB = pACB->pActiveDCB;
+    if( !(pSRB->SRBState & SRB_MSGOUT) )
+    {
+       cnt = pSRB->MsgCnt;
+       if( cnt )
+       {
+           ptr = (PUCHAR) pSRB->MsgOutBuf;
+           for(i=0; i < cnt; i++)
+           {
+               outb(*ptr, ioport+ScsiFifo);
+               ptr++;
+           }
+           pSRB->MsgCnt = 0;
+           if( (pDCB->DCBFlag & ABORT_DEV_) &&
+               (pSRB->MsgOutBuf[0] == MSG_ABORT) )
+               pSRB->SRBState = SRB_ABORT_SENT;
+       }
+       else
+       {
+           bval = MSG_ABORT;   /* ??? MSG_NOP */
+           if( (pSRB->CmdBlock[0] == INQUIRY ) ||
+               (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+               (pSRB->SRBFlag & AUTO_REQSENSE) )
+           {
+               if( pDCB->SyncMode & SYNC_ENABLE )
+                   goto  mop1;
+           }
+           outb(bval, ioport+ScsiFifo);
+       }
+       bval = INFO_XFER_CMD;
+       outb( bval, ioport+ScsiCmd);
+    }
+    else
+    {
+mop1:
+       bval = MSG_EXTENDED;
+       outb(bval, ioport+ScsiFifo);
+       bval = 3;       /*    ;length of extended msg */
+       outb(bval, ioport+ScsiFifo);
+       bval = 1;       /*    ; sync nego */
+       outb(bval, ioport+ScsiFifo);
+       bval = pDCB->NegoPeriod;
+       outb(bval, ioport+ScsiFifo);
+       bval = SYNC_NEGO_OFFSET;
+       outb(bval, ioport+ScsiFifo);
+       pSRB->SRBState |= DO_SYNC_NEGO;
+       bval = INFO_XFER_CMD;
+       outb(bval, ioport+ScsiCmd);
+    }
+}
+
+static void
+DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  bval;
+    USHORT ioport;
+
+    ioport = pACB->IOPortBase;
+    bval = CLEAR_FIFO_CMD;
+    outb(bval, ioport+ScsiCmd);
+    if( !(pSRB->SRBState & SRB_MSGIN) )
+    {
+       pSRB->SRBState &= SRB_DISCONNECT;
+       pSRB->SRBState |= SRB_MSGIN;
+    }
+    bval = INFO_XFER_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+static void
+DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+
+static void
+SetXferRate( PACB pACB, PDCB pDCB )
+{
+    UCHAR  bval;
+    USHORT cnt, i;
+    PDCB   ptr;
+
+    if( !(pDCB->IdentifyMsg & 0x07) )
+    {
+       if( pACB->scan_devices )
+       {
+           CurrSyncOffset = pDCB->SyncOffset;
+       }
+       else
+       {
+           ptr = pACB->pLinkDCB;
+           cnt = pACB->DeviceCnt;
+           bval = pDCB->UnitSCSIID;
+           for(i=0; i<cnt; i++)
+           {
+               if( ptr->UnitSCSIID == bval )
+               {
+                   ptr->SyncPeriod = pDCB->SyncPeriod;
+                   ptr->SyncOffset = pDCB->SyncOffset;
+                   ptr->CtrlR3 = pDCB->CtrlR3;
+                   ptr->CtrlR4 = pDCB->CtrlR4;
+                   ptr->SyncMode = pDCB->SyncMode;
+               }
+               ptr = ptr->pNextDCB;
+           }
+       }
+    }
+    return;
+}
+
+
+static void
+DC390_Disconnect( PACB pACB )
+{
+    PDCB   pDCB;
+    PSRB   pSRB, psrb;
+    ULONG  flags;
+    USHORT ioport, i, cnt;
+    UCHAR  bval;
+
+#ifdef DC390_DEBUG0
+    printk("DISC,");
+#endif
+
+    save_flags(flags);
+    cli();
+    ioport = pACB->IOPortBase;
+    pDCB = pACB->pActiveDCB;
+    if (!pDCB)
+     {
+#ifdef DC390_DEBUG0
+       printk("ACB:%08lx->ActiveDCB:%08lx !,",(ULONG)pACB,(ULONG)pDCB);
+#endif
+       restore_flags(flags); return;
+     }
+    pSRB = pDCB->pActiveSRB;
+    pACB->pActiveDCB = 0;
+    pSRB->ScsiPhase = SCSI_NOP0;
+    bval = EN_SEL_RESEL;
+    outb(bval, ioport+ScsiCmd);
+    if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
+    {
+       pSRB->SRBState = 0;
+       DoWaitingSRB( pACB );
+    }
+    else if( pSRB->SRBState & SRB_ABORT_SENT )
+    {
+       pDCB->TagMask = 0;
+       pDCB->DCBFlag = 0;
+       cnt = pDCB->GoingSRBCnt;
+       pDCB->GoingSRBCnt = 0;
+       pSRB = pDCB->pGoingSRB;
+       for( i=0; i < cnt; i++)
+       {
+           psrb = pSRB->pNextSRB;
+           pSRB->pNextSRB = pACB->pFreeSRB;
+           pACB->pFreeSRB = pSRB;
+           pSRB = psrb;
+       }
+       pDCB->pGoingSRB = 0;
+       DoWaitingSRB( pACB );
+    }
+    else
+    {
+       if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) ||
+          !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) )
+       {       /* Selection time out */
+           if( !(pACB->scan_devices) )
+           {
+               pSRB->SRBState = SRB_READY;
+               RewaitSRB( pDCB, pSRB);
+           }
+           else
+           {
+               pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT;
+               goto  disc1;
+           }
+       }
+       else if( pSRB->SRBState & SRB_DISCONNECT )
+       {
+           DoWaitingSRB( pACB );
+       }
+       else if( pSRB->SRBState & SRB_COMPLETED )
+       {
+disc1:
+           if(pDCB->MaxCommand > 1)
+           {
+              bval = pSRB->TagNumber;
+              pDCB->TagMask &= (~(1 << bval));   /* free tag mask */
+           }
+           pDCB->pActiveSRB = 0;
+           pSRB->SRBState = SRB_FREE;
+           SRBdone( pACB, pDCB, pSRB);
+       }
+    }
+    restore_flags(flags);
+    return;
+}
+
+
+static void
+DC390_Reselect( PACB pACB )
+{
+    PDCB   pDCB, pdcb;
+    PSRB   pSRB;
+    USHORT ioport, wval;
+    UCHAR  bval, bval1;
+
+
+#ifdef DC390_DEBUG0
+    printk("RSEL,");
+#endif
+    ioport = pACB->IOPortBase;
+    pDCB = pACB->pActiveDCB;
+    if( pDCB )
+    {  /* Arbitration lost but Reselection win */
+       pSRB = pDCB->pActiveSRB;
+       if( !( pACB->scan_devices ) )
+       {
+           pSRB->SRBState = SRB_READY;
+           RewaitSRB( pDCB, pSRB);
+       }
+    }
+    bval = inb(ioport+ScsiFifo);       /* get ID */
+    bval = bval ^ pACB->HostID_Bit;
+    wval = 0;
+    bval1 = 1;
+    for(;;)
+    {
+       if( !(bval & bval1) )
+       {
+           bval1 = bval1 << 1;
+           wval++;
+       }
+       else
+           break;
+    }
+    wval |=  ( (USHORT) inb(ioport+ScsiFifo) & 7) << 8;  /* get LUN */
+    pDCB = pACB->pLinkDCB;
+    pdcb = pDCB;
+    while( wval != *((PUSHORT) &pDCB->UnitSCSIID) )
+    {
+       pDCB = pDCB->pNextDCB;
+       if( pDCB == pdcb )
+           return;
+    }
+    pACB->pActiveDCB = pDCB;
+    if( pDCB->SyncMode & EN_TAG_QUEUING )
+    {
+       pSRB = pACB->pTmpSRB;
+       pDCB->pActiveSRB = pSRB;
+    }
+    else
+    {
+       pSRB = pDCB->pActiveSRB;
+       if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) )
+       {
+           pSRB= pACB->pTmpSRB;
+           pSRB->SRBState = SRB_UNEXPECT_RESEL;
+           pDCB->pActiveSRB = pSRB;
+           EnableMsgOut( pACB, pSRB );
+       }
+       else
+       {
+           if( pDCB->DCBFlag & ABORT_DEV_ )
+           {
+               pSRB->SRBState = SRB_ABORT_SENT;
+               EnableMsgOut( pACB, pSRB );
+           }
+           else
+               pSRB->SRBState = SRB_DATA_XFER;
+       }
+    }
+    pSRB->ScsiPhase = SCSI_NOP0;
+    bval = pDCB->UnitSCSIID;
+    outb( bval, ioport+Scsi_Dest_ID);
+    bval = pDCB->SyncPeriod;
+    outb(bval, ioport+Sync_Period);
+    bval = pDCB->SyncOffset;
+    outb( bval, ioport+Sync_Offset);
+    bval = pDCB->CtrlR1;
+    outb(bval, ioport+CtrlReg1);
+    bval = pDCB->CtrlR3;
+    outb(bval, ioport+CtrlReg3);
+    bval = pDCB->CtrlR4;       /* ; Glitch eater */
+    outb(bval, ioport+CtrlReg4);
+    bval = MSG_ACCEPTED_CMD;   /* ;to rls the /ACK signal */
+    outb(bval, ioport+ScsiCmd);
+}
+
+
+static void
+SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+    PSRB   psrb;
+    UCHAR  bval, bval1, i, j, status;
+    PSCSICMD pcmd;
+    PSCSI_INQDATA  ptr;
+    USHORT disable_tag;
+    ULONG  flags;
+    PSGL   ptr2;
+    ULONG  swlval;
+
+    pcmd = pSRB->pcmd;
+    status = pSRB->TargetStatus;
+    if(pSRB->SRBFlag & AUTO_REQSENSE)
+    {
+       pSRB->SRBFlag &= ~AUTO_REQSENSE;
+       pSRB->AdaptStatus = 0;
+       pSRB->TargetStatus = SCSI_STAT_CHECKCOND;
+       if(status == SCSI_STAT_CHECKCOND)
+       {
+           pcmd->result = DID_BAD_TARGET << 16;
+           goto ckc_e;
+       }
+       if(pSRB->RetryCnt == 0)
+       {
+           *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+           pSRB->TotalXferredLen = pSRB->Segment1[1];
+           if( (pSRB->TotalXferredLen) &&
+               (pSRB->TotalXferredLen >= pcmd->underflow) )
+               pcmd->result |= (DID_OK << 16);
+           else
+               pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+                               SCSI_STAT_CHECKCOND;
+#ifdef DC390_DEBUG0
+       printk("Cmd=%2x,Result=%8x,XferL=%8x,",pSRB->CmdBlock[0],
+               (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);
+#endif
+           goto ckc_e;
+       }
+       else
+       {
+           pSRB->RetryCnt--;
+           pSRB->AdaptStatus = 0;
+           pSRB->TargetStatus = 0;
+           *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+           *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1];
+           if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+           {
+               pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+                               SCSI_STAT_CHECKCOND;
+               goto ckc_e;
+           }
+           pcmd->result |= (DRIVER_SENSE << 24);
+           pSRB->SGcount                = (UCHAR) pSRB->Segment1[0];
+           pSRB->ScsiCmdLen             = (UCHAR) (pSRB->Segment1[0] >> 8);
+           pSRB->SGIndex = 0;
+           pSRB->TotalXferredLen = 0;
+           pSRB->SGToBeXferLen = 0;
+           if( pcmd->use_sg )
+               pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+           else if( pcmd->request_buffer )
+           {
+               pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+               pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+               pSRB->Segmentx.length = pcmd->request_bufflen;
+           }
+           if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+               RewaitSRB( pDCB, pSRB );
+           return;
+       }
+    }
+    if( status )
+    {
+       if( status == SCSI_STAT_CHECKCOND)
+       {
+           if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) )
+           {
+               bval = pSRB->SGcount;
+               swlval = 0;
+               ptr2 = pSRB->pSegmentList;
+               for( i=pSRB->SGIndex; i < bval; i++)
+               {
+                   swlval += ptr2->length;
+                   ptr2++;
+               }
+#ifdef DC390_DEBUG0
+               printk("XferredLen=%8x,NotXferLen=%8x,",
+                       (UINT) pSRB->TotalXferredLen, (UINT) swlval);
+#endif
+           }
+           RequestSense( pACB, pDCB, pSRB );
+           return;
+       }
+       else if( status == SCSI_STAT_QUEUEFULL )
+       {
+           bval = (UCHAR) pDCB->GoingSRBCnt;
+           bval--;
+           pDCB->MaxCommand = bval;
+           RewaitSRB( pDCB, pSRB );
+           pSRB->AdaptStatus = 0;
+           pSRB->TargetStatus = 0;
+           return;
+       }
+       else if(status == SCSI_STAT_SEL_TIMEOUT)
+       {
+           pSRB->AdaptStatus = H_SEL_TIMEOUT;
+           pSRB->TargetStatus = 0;
+           pcmd->result = DID_BAD_TARGET << 16;
+       }
+       else
+       {
+           pSRB->AdaptStatus = 0;
+           if( pSRB->RetryCnt )
+           {
+               pSRB->RetryCnt--;
+               pSRB->TargetStatus = 0;
+               pSRB->SGIndex = 0;
+               pSRB->TotalXferredLen = 0;
+               pSRB->SGToBeXferLen = 0;
+               if( pcmd->use_sg )
+                   pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+               else if( pcmd->request_buffer )
+               {
+                   pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+                   pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+                   pSRB->Segmentx.length = pcmd->request_bufflen;
+               }
+               if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+                   RewaitSRB( pDCB, pSRB );
+               return;
+           }
+           else
+           {
+               pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) |
+                              (ULONG) status;
+           }
+       }
+    }
+    else
+    {
+       status = pSRB->AdaptStatus;
+       if(status & H_OVER_UNDER_RUN)
+       {
+           pSRB->TargetStatus = 0;
+           pcmd->result |= (DID_OK << 16) | (pSRB->EndMessage << 8);
+       }
+       else if( pSRB->SRBStatus & PARITY_ERROR)
+       {
+           pcmd->result |= (DID_PARITY << 16) | (pSRB->EndMessage << 8);
+       }
+       else                   /* No error */
+       {
+           pSRB->AdaptStatus = 0;
+           pSRB->TargetStatus = 0;
+           pcmd->result |= (DID_OK << 16);
+       }
+    }
+
+ckc_e:
+    if( pACB->scan_devices )
+    {
+       if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+       {
+           if(pcmd->result != (DID_OK << 16))
+           {
+               if( pcmd->result & SCSI_STAT_CHECKCOND )
+               {
+                   goto RTN_OK;
+               }
+               else
+               {
+                   pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
+                   pPrevDCB->pNextDCB = pACB->pLinkDCB;
+                   if( (pcmd->target == pACB->max_id) &&
+                      ((pcmd->lun == 0) || (pcmd->lun == pACB->max_lun)) )
+                   {
+                       pACB->scan_devices = 0;
+                   }
+               }
+           }
+           else
+           {
+RTN_OK:
+               pPrevDCB->pNextDCB = pDCB;
+               pDCB->pNextDCB = pACB->pLinkDCB;
+               if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) )
+                   pACB->scan_devices = END_SCAN;
+           }
+       }
+       else if( pSRB->CmdBlock[0] == INQUIRY )
+       {
+           if( (pcmd->target == pACB->max_id) &&
+               (pcmd->lun == pACB->max_lun) )
+           {
+               pACB->scan_devices = 0;
+           }
+           ptr = (PSCSI_INQDATA) (pcmd->request_buffer);
+           if( pcmd->use_sg )
+               ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address);
+           bval1 = ptr->DevType & SCSI_DEVTYPE;
+           if(bval1 == SCSI_NODEV)
+           {
+               pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
+               pPrevDCB->pNextDCB = pACB->pLinkDCB;
+           }
+           else
+           {
+               pACB->DeviceCnt++;
+               pPrevDCB = pDCB;
+               pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390_DCB ));
+               pDCB->DevType = bval1;
+               if(bval1 == TYPE_DISK || bval1 == TYPE_MOD)
+               {
+                   if( (((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) &&
+                       (ptr->Flags & SCSI_INQ_CMDQUEUE) &&
+                       (pDCB->DevMode & TAG_QUEUING_) &&
+                       (pDCB->DevMode & EN_DISCONNECT_) )
+                   {
+                       disable_tag = 0;
+                       for(i=0; i<BADDEVCNT; i++)
+                       {
+                           for(j=0; j<28; j++)
+                           {
+                               if( ((PUCHAR)ptr)[8+j] != baddevname1[i][j])
+                                   break;
+                           }
+                           if(j == 28)
+                           {
+                               disable_tag = 1;
+                               break;
+                           }
+                       }
+
+                       if( !disable_tag )
+                       {
+                           pDCB->MaxCommand = pACB->TagMaxNum;
+                           pDCB->SyncMode |= EN_TAG_QUEUING;
+                           pDCB->TagMask = 0;
+                       }
+                       else
+                       {
+                           pDCB->SyncMode |= EN_ATN_STOP;
+                       }
+                   }
+               }
+           }
+       }
+    }
+
+    save_flags( flags );
+    cli();
+/*  ReleaseSRB( pDCB, pSRB ); */
+
+    if(pSRB == pDCB->pGoingSRB )
+    {
+       pDCB->pGoingSRB = pSRB->pNextSRB;
+    }
+    else
+    {
+       psrb = pDCB->pGoingSRB;
+       while( psrb->pNextSRB != pSRB )
+           psrb = psrb->pNextSRB;
+       psrb->pNextSRB = pSRB->pNextSRB;
+       if( pSRB == pDCB->pGoingLast )
+           pDCB->pGoingLast = psrb;
+    }
+    pSRB->pNextSRB = pACB->pFreeSRB;
+    pACB->pFreeSRB = pSRB;
+    pDCB->GoingSRBCnt--;
+
+    DoWaitingSRB( pACB );
+    restore_flags(flags);
+
+/*  Notify cmd done */
+    pcmd->scsi_done( pcmd );
+
+    if( pDCB->QIORBCnt )
+       DoNextCmd( pACB, pDCB );
+    return;
+}
+
+
+static void
+DoingSRB_Done( PACB pACB )
+{
+    PDCB  pDCB, pdcb;
+    PSRB  psrb, psrb2;
+    USHORT  cnt, i;
+    PSCSICMD pcmd;
+
+    pDCB = pACB->pLinkDCB;
+    pdcb = pDCB;
+    do
+    {
+       cnt = pdcb->GoingSRBCnt;
+       psrb = pdcb->pGoingSRB;
+       for( i=0; i<cnt; i++)
+       {
+           psrb2 = psrb->pNextSRB;
+           pcmd = psrb->pcmd;
+           pcmd->result = DID_RESET << 16;
+
+/*         ReleaseSRB( pDCB, pSRB ); */
+
+           psrb->pNextSRB = pACB->pFreeSRB;
+           pACB->pFreeSRB = psrb;
+
+           pcmd->scsi_done( pcmd );
+           psrb  = psrb2;
+       }
+       pdcb->GoingSRBCnt = 0;;
+       pdcb->pGoingSRB = NULL;
+       pdcb->TagMask = 0;
+       pdcb = pdcb->pNextDCB;
+    }
+    while( pdcb != pDCB );
+}
+
+
+static void
+DC390_ResetSCSIBus( PACB pACB )
+{
+    USHORT ioport;
+    UCHAR  bval;
+    ULONG  flags;
+
+    save_flags(flags);
+    cli();
+    pACB->ACBFlag |= RESET_DEV;
+    ioport = pACB->IOPortBase;
+
+    bval = DMA_IDLE_CMD;
+    outb(bval,ioport+DMA_Cmd);
+
+    bval = RST_SCSI_BUS_CMD;
+    outb(bval,ioport+ScsiCmd);
+
+    restore_flags(flags);
+    return;
+}
+
+
+static void
+DC390_ScsiRstDetect( PACB pACB )
+{
+    ULONG wlval, flags;
+    USHORT ioport;
+    UCHAR  bval;
+
+#ifdef DC390_DEBUG0
+    printk("RST_DETEC");
+#endif
+    save_flags(flags);
+    sti();
+    wlval = jiffies + HZ;
+    while( jiffies < wlval );   /* delay 1 sec */
+
+    cli();
+    ioport = pACB->IOPortBase;
+    bval = DMA_IDLE_CMD;
+    outb(bval,ioport+DMA_Cmd);
+    bval = CLEAR_FIFO_CMD;
+    outb(bval,ioport+ScsiCmd);
+
+    if( pACB->ACBFlag & RESET_DEV )
+       pACB->ACBFlag |= RESET_DONE;
+    else
+    {
+       pACB->ACBFlag |= RESET_DETECT;
+
+       ResetDevParam( pACB );
+/*     DoingSRB_Done( pACB ); ???? */
+       RecoverSRB( pACB );
+       pACB->pActiveDCB = NULL;
+       pACB->ACBFlag = 0;
+       DoWaitingSRB( pACB );
+    }
+    restore_flags(flags);
+    return;
+}
+
+
+static void
+RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+    PSCSICMD  pcmd;
+
+    pSRB->SRBFlag |= AUTO_REQSENSE;
+    pSRB->Segment0[0] = *((PULONG) &(pSRB->CmdBlock[0]));
+    pSRB->Segment0[1] = *((PULONG) &(pSRB->CmdBlock[4]));
+    pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount);
+    pSRB->Segment1[1] = pSRB->TotalXferredLen;
+    pSRB->AdaptStatus = 0;
+    pSRB->TargetStatus = 0;
+
+    pcmd = pSRB->pcmd;
+
+    pSRB->Segmentx.address = (PUCHAR) &(pcmd->sense_buffer);
+    pSRB->Segmentx.length = sizeof(pcmd->sense_buffer);
+    pSRB->pSegmentList = &pSRB->Segmentx;
+    pSRB->SGcount = 1;
+    pSRB->SGIndex = 0;
+
+    *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003;
+    pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
+    *((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(pcmd->sense_buffer);
+    pSRB->ScsiCmdLen = 6;
+
+    pSRB->TotalXferredLen = 0;
+    pSRB->SGToBeXferLen = 0;
+    if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+       RewaitSRB( pDCB, pSRB );
+}
+
+
+static void
+EnableMsgOut2( PACB pACB, PSRB pSRB )
+{
+    USHORT ioport;
+    UCHAR  bval;
+
+    ioport = pACB->IOPortBase;
+    pSRB->MsgCnt = 1;
+    bval = SET_ATN_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+
+static void
+EnableMsgOut( PACB pACB, PSRB pSRB )
+{
+    pSRB->MsgOutBuf[0] = MSG_ABORT;
+    EnableMsgOut2( pACB, pSRB );
+}
+
+
+static void
+DC390_InvalidCmd( PACB pACB )
+{
+   UCHAR bval;
+   USHORT ioport;
+   PSRB   pSRB;
+
+    pSRB = pACB->pActiveDCB->pActiveSRB;
+    if( pSRB->SRBState & (SRB_START_+SRB_MSGOUT) )
+    {
+       ioport = pACB->IOPortBase;
+       bval = CLEAR_FIFO_CMD;
+       outb(bval,(ioport+ScsiCmd));
+    }
+}
+
index 47db3557c983b9942b5b707d144cce27e778beeb..ca5cd6ada64a0ea0957d2cb944541610697bd02e 100644 (file)
@@ -58,8 +58,8 @@
  *  Time out in seconds for disks and Magneto-opticals (which are slower).
  */
 
-#define SD_TIMEOUT (15 * HZ)
-#define SD_MOD_TIMEOUT (15 * HZ)
+#define SD_TIMEOUT (20 * HZ)
+#define SD_MOD_TIMEOUT (25 * HZ)
 
 #define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \
                                SC->device->type != TYPE_MOD)
diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c
new file mode 100644 (file)
index 0000000..f5ceb13
--- /dev/null
@@ -0,0 +1,1928 @@
+/***********************************************************************
+ *     FILE NAME : TMSCSIM.C                                          *
+ *          BY   : C.L. Huang,  ching@tekram.com.tw                   *
+ *     Description: Device Driver for Tekram DC-390(T) PCI SCSI       *
+ *                  Bus Master Host Adapter                           *
+ * (C)Copyright 1995-1996 Tekram Technology Co., Ltd.                 *
+ ***********************************************************************/
+/*     Minor enhancements and bugfixes by                              *
+ *     Kurt Garloff <K.Garloff@ping.de>                                *
+ ***********************************************************************/
+/*     HISTORY:                                                        *
+ *                                                                     *
+ *     REV#    DATE    NAME    DESCRIPTION                             *
+ *     1.00  04/24/96  CLH     First release                           *
+ *     1.01  06/12/96  CLH     Fixed bug of Media Change for Removable *
+ *                             Device, scan all LUN. Support Pre2.0.10 *
+ *     1.02  06/18/96  CLH     Fixed bug of Command timeout ...        *
+ *     1.03  09/25/96  KG      Added tmscsim_proc_info()               *
+ *     1.04  10/11/96  CLH     Updating for support KV 2.0.x           *
+ *     1.05  10/18/96  KG      Fixed bug in DC390_abort(null ptr deref)*
+ *     1.06  10/25/96  KG      Fixed module support                    *
+ *     1.07  11/09/96  KG      Fixed tmscsim_proc_info()               *
+ *     1.08  11/18/96  KG      Fixed null ptr in DC390_Disconnect()    *
+ *     1.09  11/30/96  KG      Added register the allocated IO space   *
+ *     1.10  12/05/96  CLH     Modified tmscsim_proc_info(), and reset *
+ *                             pending interrupt in DC390_detect()     *
+ ***********************************************************************/
+
+
+#define DC390_DEBUG
+
+#define SCSI_MALLOC
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/config.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */
+#include "../block/blk.h"
+#else
+#include <linux/blk.h>
+#endif
+
+#include "scsi.h"
+#include "hosts.h"
+#include "tmscsim.h"
+#include "constants.h"
+#include "sd.h"
+#include <linux/stat.h>
+
+#include "dc390.h"
+
+#define PCI_DEVICE_ID_AMD53C974        PCI_DEVICE_ID_AMD_SCSI
+
+
+#ifndef  VERSION_ELF_1_2_13
+struct proc_dir_entry  proc_scsi_tmscsim ={
+       PROC_SCSI_DC390T, 7 ,"tmscsim",
+       S_IFDIR | S_IRUGO | S_IXUGO, 2
+       };
+#endif
+
+static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+
+static void SetXferRate( PACB pACB, PDCB pDCB );
+static void DC390_Disconnect( PACB pACB );
+static void DC390_Reselect( PACB pACB );
+static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void DoingSRB_Done( PACB pACB );
+static void DC390_ScsiRstDetect( PACB pACB );
+static void DC390_ResetSCSIBus( PACB pACB );
+static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void EnableMsgOut2( PACB pACB, PSRB pSRB );
+static void EnableMsgOut( PACB pACB, PSRB pSRB );
+static void DC390_InvalidCmd( PACB pACB );
+
+int    DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index );
+void   DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd );
+
+#ifdef MODULE
+static int DC390_release(struct Scsi_Host *host);
+static int DC390_shutdown (struct Scsi_Host *host);
+#endif
+
+
+static PSHT    pSHT_start = NULL;
+static PSH     pSH_start = NULL;
+static PSH     pSH_current = NULL;
+static PACB    pACB_start= NULL;
+static PACB    pACB_current = NULL;
+static PDCB    pPrevDCB = NULL;
+static USHORT  adapterCnt = 0;
+static USHORT  InitialTime = 0;
+static USHORT  CurrSyncOffset = 0;
+static ULONG   mech1addr;
+static UCHAR   mech2bus, mech2Agent, mech2CfgSPenR;
+
+static PVOID DC390_phase0[]={
+       DC390_DataOut_0,
+       DC390_DataIn_0,
+       DC390_Command_0,
+       DC390_Status_0,
+       DC390_Nop_0,
+       DC390_Nop_0,
+       DC390_MsgOut_0,
+       DC390_MsgIn_0,
+       DC390_Nop_1
+       };
+
+static PVOID DC390_phase1[]={
+       DC390_DataOutPhase,
+       DC390_DataInPhase,
+       DC390_CommandPhase,
+       DC390_StatusPhase,
+       DC390_Nop_0,
+       DC390_Nop_0,
+       DC390_MsgOutPhase,
+       DC390_MsgInPhase,
+       DC390_Nop_1,
+       };
+
+UCHAR  eepromBuf[MAX_ADAPTER_NUM][128];
+
+
+UCHAR  clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20};
+
+UCHAR  baddevname1[2][28] ={
+       "SEAGATE ST3390N         9546",
+       "HP      C3323-300       4269"};
+
+#define BADDEVCNT      2
+
+
+/***********************************************************************
+ *
+ *
+ *
+ **********************************************************************/
+static void
+QLinkcmd( PSCSICMD cmd, PDCB pDCB )
+{
+    ULONG  flags;
+    PSCSICMD  pcmd;
+
+    save_flags(flags);
+    cli();
+
+    if( !pDCB->QIORBCnt )
+    {
+       pDCB->pQIORBhead = cmd;
+       pDCB->pQIORBtail = cmd;
+       pDCB->QIORBCnt++;
+       cmd->next = NULL;
+    }
+    else
+    {
+       pcmd = pDCB->pQIORBtail;
+       pcmd->next = cmd;
+       pDCB->pQIORBtail = cmd;
+       pDCB->QIORBCnt++;
+       cmd->next = NULL;
+    }
+
+    restore_flags(flags);
+}
+
+
+static PSCSICMD
+Getcmd( PDCB pDCB )
+{
+    ULONG  flags;
+    PSCSICMD  pcmd;
+
+    save_flags(flags);
+    cli();
+
+    pcmd = pDCB->pQIORBhead;
+    pDCB->pQIORBhead = pcmd->next;
+    pcmd->next = NULL;
+    pDCB->QIORBCnt--;
+
+    restore_flags(flags);
+    return( pcmd );
+}
+
+
+static PSRB
+GetSRB( PACB pACB )
+{
+    ULONG  flags;
+    PSRB   pSRB;
+
+    save_flags(flags);
+    cli();
+
+    pSRB = pACB->pFreeSRB;
+    if( pSRB )
+    {
+       pACB->pFreeSRB = pSRB->pNextSRB;
+       pSRB->pNextSRB = NULL;
+    }
+    restore_flags(flags);
+    return( pSRB );
+}
+
+
+static void
+RewaitSRB0( PDCB pDCB, PSRB pSRB )
+{
+    PSRB   psrb1;
+    ULONG  flags;
+
+    save_flags(flags);
+    cli();
+
+    if( (psrb1 = pDCB->pWaitingSRB) )
+    {
+       pSRB->pNextSRB = psrb1;
+       pDCB->pWaitingSRB = pSRB;
+    }
+    else
+    {
+       pSRB->pNextSRB = NULL;
+       pDCB->pWaitingSRB = pSRB;
+       pDCB->pWaitLast = pSRB;
+    }
+    restore_flags(flags);
+}
+
+
+static void
+RewaitSRB( PDCB pDCB, PSRB pSRB )
+{
+    PSRB   psrb1;
+    ULONG  flags;
+    UCHAR  bval;
+
+    save_flags(flags);
+    cli();
+    pDCB->GoingSRBCnt--;
+    psrb1 = pDCB->pGoingSRB;
+    if( pSRB == psrb1 )
+    {
+       pDCB->pGoingSRB = psrb1->pNextSRB;
+    }
+    else
+    {
+       while( pSRB != psrb1->pNextSRB )
+           psrb1 = psrb1->pNextSRB;
+       psrb1->pNextSRB = pSRB->pNextSRB;
+       if( pSRB == pDCB->pGoingLast )
+           pDCB->pGoingLast = psrb1;
+    }
+    if( (psrb1 = pDCB->pWaitingSRB) )
+    {
+       pSRB->pNextSRB = psrb1;
+       pDCB->pWaitingSRB = pSRB;
+    }
+    else
+    {
+       pSRB->pNextSRB = NULL;
+       pDCB->pWaitingSRB = pSRB;
+       pDCB->pWaitLast = pSRB;
+    }
+
+    bval = pSRB->TagNumber;
+    pDCB->TagMask &= (~(1 << bval));     /* Free TAG number */
+    restore_flags(flags);
+}
+
+
+static void
+DoWaitingSRB( PACB pACB )
+{
+    ULONG  flags;
+    PDCB   ptr, ptr1;
+    PSRB   pSRB;
+
+    save_flags(flags);
+    cli();
+
+    if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) )
+    {
+       ptr = pACB->pDCBRunRobin;
+       if( !ptr )
+       {
+           ptr = pACB->pLinkDCB;
+           pACB->pDCBRunRobin = ptr;
+       }
+       ptr1 = ptr;
+       for( ;ptr1; )
+       {
+           pACB->pDCBRunRobin = ptr1->pNextDCB;
+           if( !( ptr1->MaxCommand > ptr1->GoingSRBCnt ) ||
+               !( pSRB = ptr1->pWaitingSRB ) )
+           {
+               if(pACB->pDCBRunRobin == ptr)
+                   break;
+               ptr1 = ptr1->pNextDCB;
+           }
+           else
+           {
+               if( !DC390_StartSCSI(pACB, ptr1, pSRB) )
+               {
+                   ptr1->GoingSRBCnt++;
+                   if( ptr1->pWaitLast == pSRB )
+                   {
+                       ptr1->pWaitingSRB = NULL;
+                       ptr1->pWaitLast = NULL;
+                   }
+                   else
+                   {
+                       ptr1->pWaitingSRB = pSRB->pNextSRB;
+                   }
+                   pSRB->pNextSRB = NULL;
+
+                   if( ptr1->pGoingSRB )
+                       ptr1->pGoingLast->pNextSRB = pSRB;
+                   else
+                       ptr1->pGoingSRB = pSRB;
+                   ptr1->pGoingLast = pSRB;
+               }
+               break;
+           }
+       }
+    }
+    restore_flags(flags);
+    return;
+}
+
+
+static void
+SRBwaiting( PDCB pDCB, PSRB pSRB)
+{
+    if( pDCB->pWaitingSRB )
+    {
+       pDCB->pWaitLast->pNextSRB = pSRB;
+       pDCB->pWaitLast = pSRB;
+       pSRB->pNextSRB = NULL;
+    }
+    else
+    {
+       pDCB->pWaitingSRB = pSRB;
+       pDCB->pWaitLast = pSRB;
+    }
+}
+
+
+static void
+SendSRB( PSCSICMD pcmd, PACB pACB, PSRB pSRB )
+{
+    ULONG  flags;
+    PDCB   pDCB;
+
+    save_flags(flags);
+    cli();
+
+    pDCB = pSRB->pSRBDCB;
+    if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) ||
+       (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) )
+    {
+       SRBwaiting(pDCB, pSRB);
+       goto SND_EXIT;
+    }
+
+    if( pDCB->pWaitingSRB )
+    {
+       SRBwaiting(pDCB, pSRB);
+/*     pSRB = GetWaitingSRB(pDCB); */
+       pSRB = pDCB->pWaitingSRB;
+       pDCB->pWaitingSRB = pSRB->pNextSRB;
+       pSRB->pNextSRB = NULL;
+    }
+
+    if( !DC390_StartSCSI(pACB, pDCB, pSRB) )
+    {
+       pDCB->GoingSRBCnt++;
+       if( pDCB->pGoingSRB )
+       {
+           pDCB->pGoingLast->pNextSRB = pSRB;
+           pDCB->pGoingLast = pSRB;
+       }
+       else
+       {
+           pDCB->pGoingSRB = pSRB;
+           pDCB->pGoingLast = pSRB;
+       }
+    }
+    else
+       RewaitSRB0( pDCB, pSRB );
+
+SND_EXIT:
+    restore_flags(flags);
+    return;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390_queue_command (Scsi_Cmnd *cmd,
+ *                                            void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ *         a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ ***********************************************************************/
+
+int
+DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+{
+    USHORT ioport, i;
+    Scsi_Cmnd *pcmd;
+    struct Scsi_Host *psh;
+    PACB   pACB;
+    PDCB   pDCB;
+    PSRB   pSRB;
+    ULONG  flags;
+    PUCHAR ptr,ptr1;
+
+    psh = cmd->host;
+    pACB = (PACB ) psh->hostdata;
+    ioport = pACB->IOPortBase;
+
+#ifdef DC390_DEBUG0
+/*  if(pACB->scan_devices) */
+       printk("Cmd=%2x,ID=%d,LUN=%d,",cmd->cmnd[0],cmd->target,cmd->lun);
+#endif
+
+    if( (pACB->scan_devices == END_SCAN) && (cmd->cmnd[0] != INQUIRY) )
+    {
+       pACB->scan_devices = 0;
+       pPrevDCB->pNextDCB = pACB->pLinkDCB;
+    }
+    else if( (pACB->scan_devices) && (cmd->cmnd[0] == 8) )
+    {
+       pACB->scan_devices = 0;
+       pPrevDCB->pNextDCB = pACB->pLinkDCB;
+    }
+
+    if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) )
+    {
+/*     printk("DC390: Ignore target %d lun %d\n",
+               cmd->target, cmd->lun); */
+       cmd->result = (DID_BAD_TARGET << 16);
+       done(cmd);
+       return( 0 );
+    }
+
+    if( (pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+    {
+       if( pACB->DeviceCnt < MAX_DEVICES )
+       {
+           pACB->DCBmap[cmd->target] |= (1 << cmd->lun);
+           pDCB = pACB->pDCB_free;
+#ifdef DC390_DEBUG0
+           printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
+#endif
+           DC390_initDCB( pACB, pDCB, cmd );
+       }
+       else    /* ???? */
+       {
+/*         printk("DC390: Ignore target %d lun %d\n",
+                   cmd->target, cmd->lun); */
+           cmd->result = (DID_BAD_TARGET << 16);
+           done(cmd);
+           return(0);
+       }
+    }
+    else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+    {
+/*     printk("DC390: Ignore target %d lun %d\n",
+               cmd->target, cmd->lun); */
+       cmd->result = (DID_BAD_TARGET << 16);
+       done(cmd);
+       return(0);
+    }
+    else
+    {
+       pDCB = pACB->pLinkDCB;
+       while( (pDCB->UnitSCSIID != cmd->target) ||
+              (pDCB->UnitSCSILUN != cmd->lun) )
+       {
+           pDCB = pDCB->pNextDCB;
+       }
+#ifdef DC390_DEBUG0
+           printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
+#endif
+    }
+
+    cmd->scsi_done = done;
+    cmd->result = 0;
+
+    save_flags(flags);
+    cli();
+
+    if( pDCB->QIORBCnt )
+    {
+       QLinkcmd( cmd, pDCB );
+       pcmd = Getcmd( pDCB );
+    }
+    else
+       pcmd = cmd;
+
+    pSRB = GetSRB( pACB );
+
+    if( !pSRB )
+    {
+       QLinkcmd( pcmd, pDCB );
+       restore_flags(flags);
+       return(0);
+    }
+
+/*  BuildSRB(pSRB); */
+
+    pSRB->pSRBDCB = pDCB;
+    pSRB->pcmd = pcmd;
+    ptr = (PUCHAR) pSRB->CmdBlock;
+    ptr1 = (PUCHAR) pcmd->cmnd;
+    pSRB->ScsiCmdLen = pcmd->cmd_len;
+    for(i=0; i< pcmd->cmd_len; i++)
+    {
+       *ptr = *ptr1;
+       ptr++;
+       ptr1++;
+    }
+    if( pcmd->use_sg )
+    {
+       pSRB->SGcount = (UCHAR) pcmd->use_sg;
+       pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+    }
+    else if( pcmd->request_buffer )
+    {
+       pSRB->SGcount = 1;
+       pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+       pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+       pSRB->Segmentx.length = pcmd->request_bufflen;
+    }
+    else
+       pSRB->SGcount = 0;
+
+    pSRB->SGIndex = 0;
+    pSRB->AdaptStatus = 0;
+    pSRB->TargetStatus = 0;
+    pSRB->MsgCnt = 0;
+    if( pDCB->DevType != TYPE_TAPE )
+       pSRB->RetryCnt = 1;
+    else
+       pSRB->RetryCnt = 0;
+    pSRB->SRBStatus = 0;
+    pSRB->SRBFlag = 0;
+    pSRB->SRBState = 0;
+    pSRB->TotalXferredLen = 0;
+    pSRB->SGPhysAddr = 0;
+    pSRB->SGToBeXferLen = 0;
+    pSRB->ScsiPhase = 0;
+    pSRB->EndMessage = 0;
+    SendSRB( pcmd, pACB, pSRB );
+
+    restore_flags(flags);
+    return(0);
+}
+
+
+static void
+DoNextCmd( PACB pACB, PDCB pDCB )
+{
+    Scsi_Cmnd *pcmd;
+    PSRB   pSRB;
+    ULONG  flags;
+    PUCHAR ptr,ptr1;
+    USHORT i;
+
+
+    if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) )
+       return;
+    save_flags(flags);
+    cli();
+
+    pcmd = Getcmd( pDCB );
+    pSRB = GetSRB( pACB );
+    if( !pSRB )
+    {
+       QLinkcmd( pcmd, pDCB );
+       restore_flags(flags);
+       return;
+    }
+
+    pSRB->pSRBDCB = pDCB;
+    pSRB->pcmd = pcmd;
+    ptr = (PUCHAR) pSRB->CmdBlock;
+    ptr1 = (PUCHAR) pcmd->cmnd;
+    pSRB->ScsiCmdLen = pcmd->cmd_len;
+    for(i=0; i< pcmd->cmd_len; i++)
+    {
+       *ptr = *ptr1;
+       ptr++;
+       ptr1++;
+    }
+    if( pcmd->use_sg )
+    {
+       pSRB->SGcount = (UCHAR) pcmd->use_sg;
+       pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+    }
+    else if( pcmd->request_buffer )
+    {
+       pSRB->SGcount = 1;
+       pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+       pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+       pSRB->Segmentx.length = pcmd->request_bufflen;
+    }
+    else
+       pSRB->SGcount = 0;
+
+    pSRB->SGIndex = 0;
+    pSRB->AdaptStatus = 0;
+    pSRB->TargetStatus = 0;
+    pSRB->MsgCnt = 0;
+    if( pDCB->DevType != TYPE_TAPE )
+       pSRB->RetryCnt = 1;
+    else
+       pSRB->RetryCnt = 0;
+    pSRB->SRBStatus = 0;
+    pSRB->SRBFlag = 0;
+    pSRB->SRBState = 0;
+    pSRB->TotalXferredLen = 0;
+    pSRB->SGPhysAddr = 0;
+    pSRB->SGToBeXferLen = 0;
+    pSRB->ScsiPhase = 0;
+    pSRB->EndMessage = 0;
+    SendSRB( pcmd, pACB, pSRB );
+
+    restore_flags(flags);
+    return;
+}
+
+
+/***********************************************************************
+ * Function:
+ *   DC390_bios_param
+ *
+ * Description:
+ *   Return the disk geometry for the given SCSI device.
+ ***********************************************************************/
+#ifdef VERSION_ELF_1_2_13
+int DC390_bios_param(Disk *disk, int devno, int geom[])
+#else
+int DC390_bios_param(Disk *disk, kdev_t devno, int geom[])
+#endif
+{
+    int heads, sectors, cylinders;
+    PACB pACB;
+
+    pACB = (PACB) disk->device->host->hostdata;
+    heads = 64;
+    sectors = 32;
+    cylinders = disk->capacity / (heads * sectors);
+
+    if ( cylinders > 1024)
+    {
+      heads = 255;
+      sectors = 63;
+      cylinders = disk->capacity / (255 * 63);
+    }
+
+    geom[0] = heads;
+    geom[1] = sectors;
+    geom[2] = cylinders;
+
+    return (0);
+}
+
+
+/***********************************************************************
+ * Function : int DC390_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Abort an errant SCSI command
+ *
+ * Inputs : cmd - command to abort
+ *
+ * Returns : 0 on success, -1 on failure.
+ ***********************************************************************/
+
+int
+DC390_abort (Scsi_Cmnd *cmd)
+{
+    ULONG flags;
+    PACB  pACB;
+    PDCB  pDCB, pdcb;
+    PSRB  pSRB, psrb;
+    USHORT count, i;
+    PSCSICMD  pcmd, pcmd1;
+    int   status;
+
+
+#ifdef DC390_DEBUG0
+    printk("DC390 : Abort Cmd.");
+#endif
+
+    save_flags(flags);
+    cli();
+
+    pACB = (PACB) cmd->host->hostdata;
+    pDCB = pACB->pLinkDCB;
+    pdcb = pDCB;
+    while( (pDCB->UnitSCSIID != cmd->target) ||
+          (pDCB->UnitSCSILUN != cmd->lun) )
+    {
+       pDCB = pDCB->pNextDCB;
+       if( pDCB == pdcb )
+           goto  NOT_RUN;
+    }
+
+    if( pDCB->QIORBCnt )
+    {
+       pcmd = pDCB->pQIORBhead;
+       if( pcmd == cmd )
+       {
+           pDCB->pQIORBhead = pcmd->next;
+           pcmd->next = NULL;
+           pDCB->QIORBCnt--;
+           status = SCSI_ABORT_SUCCESS;
+           goto  ABO_X;
+       }
+       for( count = pDCB->QIORBCnt, i=0; i<count-1; i++)
+       {
+           if( pcmd->next == cmd )
+           {
+               pcmd1 = pcmd->next;
+               pcmd->next = pcmd1->next;
+               pcmd1->next = NULL;
+               pDCB->QIORBCnt--;
+               status = SCSI_ABORT_SUCCESS;
+               goto  ABO_X;
+           }
+           else
+           {
+               pcmd = pcmd->next;
+           }
+       }
+    }
+
+    pSRB = pDCB->pWaitingSRB;
+    if( !pSRB )
+       goto  ON_GOING;
+    if( pSRB->pcmd == cmd )
+    {
+       pDCB->pWaitingSRB = pSRB->pNextSRB;
+       goto  IN_WAIT;
+    }
+    else
+    {
+       psrb = pSRB;
+       if( !(psrb->pNextSRB) )
+           goto ON_GOING;
+       while( psrb->pNextSRB->pcmd != cmd )
+       {
+           psrb = psrb->pNextSRB;
+           if( !(psrb->pNextSRB) )
+               goto ON_GOING;
+       }
+       pSRB = psrb->pNextSRB;
+       psrb->pNextSRB = pSRB->pNextSRB;
+       if( pSRB == pDCB->pWaitLast )
+           pDCB->pWaitLast = psrb; /* No check for psrb == NULL ? */
+IN_WAIT:
+       pSRB->pNextSRB = pACB->pFreeSRB;
+       pACB->pFreeSRB = pSRB;
+       cmd->next = NULL;
+       status = SCSI_ABORT_SUCCESS;
+       goto  ABO_X;
+    }
+
+ON_GOING:
+    pSRB = pDCB->pGoingSRB;
+    for( count = pDCB->GoingSRBCnt, i=0; i<count; i++)
+    {
+       if( pSRB->pcmd != cmd )
+           pSRB = pSRB->pNextSRB;
+       else
+       {
+           if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) )
+           {
+               status = SCSI_ABORT_BUSY;
+               goto  ABO_X;
+           }
+           else
+           {
+               status = SCSI_ABORT_SNOOZE;
+               goto  ABO_X;
+           }
+       }
+    }
+
+NOT_RUN:
+    status = SCSI_ABORT_NOT_RUNNING;
+
+ABO_X:
+    cmd->result = DID_ABORT << 16;
+    cmd->scsi_done(cmd);
+    restore_flags(flags);
+    return( status );
+}
+
+
+static void
+ResetDevParam( PACB pACB )
+{
+    PDCB   pDCB, pdcb;
+
+    pDCB = pACB->pLinkDCB;
+    if( pDCB == NULL )
+       return;
+    pdcb = pDCB;
+    do
+    {
+       pDCB->SyncMode &= ~SYNC_NEGO_DONE;
+       pDCB->SyncPeriod = 0;
+       pDCB->SyncOffset = 0;
+       pDCB->CtrlR3 = FAST_CLK;
+       pDCB->CtrlR4 &= NEGATE_REQACKDATA;
+       pDCB->CtrlR4 |= EATER_25NS;
+       pDCB = pDCB->pNextDCB;
+    }
+    while( pdcb != pDCB );
+}
+
+
+static void
+RecoverSRB( PACB pACB )
+{
+    PDCB   pDCB, pdcb;
+    PSRB   psrb, psrb2;
+    USHORT cnt, i;
+
+    pDCB = pACB->pLinkDCB;
+    if( pDCB == NULL )
+       return;
+    pdcb = pDCB;
+    do
+    {
+       cnt = pdcb->GoingSRBCnt;
+       psrb = pdcb->pGoingSRB;
+       for (i=0; i<cnt; i++)
+       {
+           psrb2 = psrb;
+           psrb = psrb->pNextSRB;
+/*         RewaitSRB( pDCB, psrb ); */
+           if( pdcb->pWaitingSRB )
+           {
+               psrb2->pNextSRB = pdcb->pWaitingSRB;
+               pdcb->pWaitingSRB = psrb2;
+           }
+           else
+           {
+               pdcb->pWaitingSRB = psrb2;
+               pdcb->pWaitLast = psrb2;
+               psrb2->pNextSRB = NULL;
+           }
+       }
+       pdcb->GoingSRBCnt = 0;
+       pdcb->pGoingSRB = NULL;
+       pdcb->TagMask = 0;
+       pdcb = pdcb->pNextDCB;
+    }
+    while( pdcb != pDCB );
+}
+
+
+/***********************************************************************
+ * Function : int DC390_reset (Scsi_Cmnd *cmd, ...)
+ *
+ * Purpose : perform a hard reset on the SCSI bus
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * Returns : 0 on success.
+ ***********************************************************************/
+
+#ifdef VERSION_2_0_0
+int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags)
+#else
+int DC390_reset (Scsi_Cmnd *cmd)
+#endif
+{
+    USHORT   ioport;
+    unsigned long flags;
+    PACB  pACB;
+    UCHAR    bval;
+    USHORT  i;
+
+
+#ifdef DC390_DEBUG1
+    printk("DC390: RESET,");
+#endif
+
+    pACB = (PACB ) cmd->host->hostdata;
+    ioport = pACB->IOPortBase;
+    save_flags(flags);
+    cli();
+    bval = inb(ioport+CtrlReg1);
+    bval |= DIS_INT_ON_SCSI_RST;
+    outb(bval,ioport+CtrlReg1);  /* disable interrupt */
+    DC390_ResetSCSIBus( pACB );
+    for( i=0; i<500; i++ )
+       udelay(1000);
+    bval = inb(ioport+CtrlReg1);
+    bval &= ~DIS_INT_ON_SCSI_RST;
+    outb(bval,ioport+CtrlReg1); /* re-enable interrupt */
+
+    bval = DMA_IDLE_CMD;
+    outb(bval,ioport+DMA_Cmd);
+    bval = CLEAR_FIFO_CMD;
+    outb(bval,ioport+ScsiCmd);
+
+    ResetDevParam( pACB );
+    DoingSRB_Done( pACB );
+    pACB->pActiveDCB = NULL;
+
+    pACB->ACBFlag = 0;
+    DoWaitingSRB( pACB );
+
+    restore_flags(flags);
+#ifdef DC390_DEBUG1
+    printk("DC390: RESET1,");
+#endif
+    return( SCSI_RESET_SUCCESS );
+}
+
+
+#include "scsiiom.c"
+
+
+/***********************************************************************
+ * Function : static void DC390_initDCB
+ *
+ * Purpose :  initialize the internal structures for a given DCB
+ *
+ * Inputs : cmd - pointer to this scsi cmd request block structure
+ *
+ ***********************************************************************/
+void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd )
+{
+    PEEprom    prom;
+    UCHAR      bval;
+    USHORT     index;
+
+    if( pACB->DeviceCnt == 0 )
+    {
+       pACB->pLinkDCB = pDCB;
+       pACB->pDCBRunRobin = pDCB;
+       pDCB->pNextDCB = pDCB;
+       pPrevDCB = pDCB;
+    }
+    else
+       pPrevDCB->pNextDCB = pDCB;
+
+    pDCB->pDCBACB = pACB;
+    pDCB->QIORBCnt = 0;
+    pDCB->UnitSCSIID = cmd->target;
+    pDCB->UnitSCSILUN = cmd->lun;
+    pDCB->pWaitingSRB = NULL;
+    pDCB->pGoingSRB = NULL;
+    pDCB->GoingSRBCnt = 0;
+    pDCB->pActiveSRB = NULL;
+    pDCB->TagMask = 0;
+    pDCB->MaxCommand = 1;
+    pDCB->AdaptIndex = pACB->AdapterIndex;
+    index = pACB->AdapterIndex;
+    pDCB->DCBFlag = 0;
+
+    prom = (PEEprom) &eepromBuf[index][cmd->target << 2];
+    pDCB->DevMode = prom->EE_MODE1;
+    pDCB->AdpMode = eepromBuf[index][EE_MODE2];
+
+    if( pDCB->DevMode & EN_DISCONNECT_ )
+       bval = 0xC0;
+    else
+       bval = 0x80;
+    bval |= cmd->lun;
+    pDCB->IdentifyMsg = bval;
+
+    pDCB->SyncMode = 0;
+    if( pDCB->DevMode & SYNC_NEGO_ )
+    {
+       if( !(cmd->lun) || CurrSyncOffset )
+           pDCB->SyncMode = SYNC_ENABLE;
+    }
+
+    pDCB->SyncPeriod = 0;
+    pDCB->SyncOffset = 0;
+    pDCB->NegoPeriod = (clock_period1[prom->EE_SPEED] * 25) >> 2;
+
+    pDCB->CtrlR1 = pACB->AdaptSCSIID;
+    if( pDCB->DevMode & PARITY_CHK_ )
+       pDCB->CtrlR1 |= PARITY_ERR_REPO;
+
+    pDCB->CtrlR3 = FAST_CLK;
+
+    pDCB->CtrlR4 = EATER_25NS;
+    if( pDCB->AdpMode & ACTIVE_NEGATION)
+       pDCB->CtrlR4 |= NEGATE_REQACKDATA;
+}
+
+
+/***********************************************************************
+ * Function : static void DC390_initSRB
+ *
+ * Purpose :  initialize the internal structures for a given SRB
+ *
+ * Inputs : psrb - pointer to this scsi request block structure
+ *
+ ***********************************************************************/
+void DC390_initSRB( PSRB psrb )
+{
+#ifndef VERSION_ELF_1_2_13
+#ifdef DC390_DEBUG0
+   printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb));
+#endif
+       psrb->PhysSRB = virt_to_bus( psrb );
+#else
+       psrb->PhysSRB = (ULONG) psrb;
+#endif
+}
+
+
+void DC390_linkSRB( PACB pACB )
+{
+    USHORT  count, i;
+    PSRB    psrb;
+
+    count = pACB->SRBCount;
+
+    for( i=0; i< count; i++)
+    {
+       if( i != count - 1)
+           pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1];
+       else
+           pACB->SRB_array[i].pNextSRB = NULL;
+       psrb = (PSRB) &pACB->SRB_array[i];
+       DC390_initSRB( psrb );
+    }
+}
+
+
+/***********************************************************************
+ * Function : static void DC390_initACB
+ *
+ * Purpose :  initialize the internal structures for a given SCSI host
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+{
+    PACB    pACB;
+    USHORT  i;
+
+    psh->can_queue = MAX_CMD_QUEUE;
+    psh->cmd_per_lun = MAX_CMD_PER_LUN;
+    psh->this_id = (int) eepromBuf[index][EE_ADAPT_SCSI_ID];
+    psh->io_port = io_port;
+    psh->n_io_port = 0x80;
+    psh->irq = Irq;
+
+    pACB = (PACB) psh->hostdata;
+
+#ifndef VERSION_ELF_1_2_13
+    psh->max_id = 8;
+#ifdef CONFIG_SCSI_MULTI_LUN
+    if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+       psh->max_lun = 8;
+    else
+#endif
+       psh->max_lun = 1;
+#endif
+
+    pACB->max_id = 7;
+    if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] )
+       pACB->max_id--;
+#ifdef CONFIG_SCSI_MULTI_LUN
+    if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+       pACB->max_lun = 7;
+    else
+#endif
+       pACB->max_lun = 0;
+
+    pACB->pScsiHost = psh;
+    pACB->IOPortBase = (USHORT) io_port;
+    pACB->pLinkDCB = NULL;
+    pACB->pDCBRunRobin = NULL;
+    pACB->pActiveDCB = NULL;
+    pACB->pFreeSRB = pACB->SRB_array;
+    pACB->SRBCount = MAX_SRB_CNT;
+    pACB->AdapterIndex = index;
+    pACB->status = 0;
+    pACB->AdaptSCSIID = eepromBuf[index][EE_ADAPT_SCSI_ID];
+    pACB->HostID_Bit = (1 << pACB->AdaptSCSIID);
+    pACB->AdaptSCSILUN = 0;
+    pACB->DeviceCnt = 0;
+    pACB->IRQLevel = Irq;
+    pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2;
+    pACB->ACBFlag = 0;
+    pACB->scan_devices = 1;
+    pACB->Gmode2 = eepromBuf[index][EE_MODE2];
+    if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+       pACB->LUNchk = 1;
+    pACB->pDCB_free = &pACB->DCB_array[0];
+    DC390_linkSRB( pACB );
+    pACB->pTmpSRB = &pACB->TmpSRB;
+    DC390_initSRB( pACB->pTmpSRB );
+    for(i=0; i<MAX_SCSI_ID; i++)
+       pACB->DCBmap[i] = 0;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390_initAdapter
+ *
+ * Purpose :  initialize the SCSI chip ctrl registers
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+{
+    USHORT ioport;
+    UCHAR  bval;
+    PACB   pACB, pacb;
+    USHORT used_irq = 0;
+
+    pacb = pACB_start;
+    if( pacb != NULL )
+    {
+       for ( ; (pacb != (PACB) -1) ; )
+       {
+           if( pacb->IRQLevel == Irq )
+           {
+               used_irq = 1;
+               break;
+           }
+           else
+               pacb = pacb->pNextACB;
+       }
+    }
+
+    if( !used_irq )
+    {
+#ifdef VERSION_ELF_1_2_13
+       if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim"))
+#else
+       if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim", NULL))
+#endif
+       {
+           printk("DC390: register IRQ error!\n");
+           return( -1 );
+       }
+    }
+
+    request_region(io_port,psh->n_io_port,"tmscsim");
+
+    ioport = (USHORT) io_port;
+
+    pACB = (PACB) psh->hostdata;
+    bval = SEL_TIMEOUT;                /* 250ms selection timeout */
+    outb(bval,ioport+Scsi_TimeOut);
+
+    bval = CLK_FREQ_40MHZ;             /* Conversion factor = 0 , 40MHz clock */
+    outb(bval,ioport+Clk_Factor);
+
+    bval = NOP_CMD;                    /* NOP cmd - clear command register */
+    outb(bval,ioport+ScsiCmd);
+
+    bval = EN_FEATURE+EN_SCSI2_CMD;    /* Enable Feature and SCSI-2 */
+    outb(bval,ioport+CtrlReg2);
+
+    bval = FAST_CLK;                   /* fast clock */
+    outb(bval,ioport+CtrlReg3);
+
+    bval = EATER_25NS;
+    if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION )
+        bval |= NEGATE_REQACKDATA;
+    outb(bval,ioport+CtrlReg4);
+
+    bval = DIS_INT_ON_SCSI_RST;        /* Disable SCSI bus reset interrupt */
+    outb(bval,ioport+CtrlReg1);
+
+    return(0);
+}
+
+
+void
+DC390_EnableCfg( USHORT mechnum, UCHAR regval )
+{
+    ULONG wlval;
+
+    if(mechnum == 2)
+    {
+       outb(mech2bus, PCI_CFG2_FORWARD_REG);
+       outb(mech2CfgSPenR, PCI_CFG2_ENABLE_REG);
+    }
+    else
+    {
+       regval &= 0xFC;
+       wlval = mech1addr;
+       wlval |= (((ULONG)regval) & 0xff);
+       outl(wlval, PCI_CFG1_ADDRESS_REG);
+    }
+}
+
+
+void
+DC390_DisableCfg( USHORT mechnum )
+{
+
+    if(mechnum == 2)
+       outb(0, PCI_CFG2_ENABLE_REG);
+    else
+       outl(0, PCI_CFG1_ADDRESS_REG);
+}
+
+
+UCHAR
+DC390_inByte( USHORT mechnum, UCHAR regval )
+{
+    UCHAR bval;
+    ULONG wval;
+    ULONG flags;
+
+    save_flags(flags);
+    cli();
+    DC390_EnableCfg( mechnum, regval );
+    if(mechnum == 2)
+    {
+       wval = mech2Agent;
+       wval <<= 8;
+       wval |= ((USHORT) regval) & 0xff;
+       bval = inb(wval);
+    }
+    else
+    {
+       regval &= 3;
+       bval = inb(PCI_CFG1_DATA_REG | regval);
+    }
+    DC390_DisableCfg(mechnum);
+    restore_flags(flags);
+    return(bval);
+}
+
+
+USHORT
+DC390_inWord( USHORT mechnum, UCHAR regval )
+{
+    USHORT wval;
+    ULONG flags;
+
+    save_flags(flags);
+    cli();
+    DC390_EnableCfg(mechnum,regval);
+    if(mechnum == 2)
+    {
+       wval = mech2Agent;
+       wval <<= 8;
+       wval |= regval;
+       wval = inw(wval);
+    }
+    else
+    {
+       regval &= 3;
+       wval = inw(PCI_CFG1_DATA_REG | regval);
+    }
+    DC390_DisableCfg(mechnum);
+    restore_flags(flags);
+    return(wval);
+}
+
+
+ULONG
+DC390_inDword(USHORT mechnum, UCHAR regval )
+{
+    ULONG wlval;
+    ULONG flags;
+    USHORT wval;
+
+    save_flags(flags);
+    cli();
+    DC390_EnableCfg(mechnum,regval);
+    if(mechnum == 2)
+    {
+       wval = mech2Agent;
+       wval <<= 8;
+       wval |= regval;
+       wlval = inl(wval);
+    }
+    else
+    {
+       wlval = inl(PCI_CFG1_DATA_REG);
+    }
+    DC390_DisableCfg(mechnum);
+    restore_flags(flags);
+    return(wlval);
+}
+
+
+void
+DC390_OutB(USHORT mechnum, UCHAR regval, UCHAR bval )
+{
+
+    USHORT wval;
+    ULONG  flags;
+
+    save_flags(flags);
+    cli();
+    DC390_EnableCfg(mechnum,regval);
+    if(mechnum == 2)
+    {
+       wval = mech2Agent;
+       wval <<= 8;
+       wval |= regval;
+       outb(bval, wval);
+    }
+    else
+    {
+       regval &= 3;
+       outb(bval, PCI_CFG1_DATA_REG | regval);
+    }
+    DC390_DisableCfg(mechnum);
+    restore_flags(flags);
+}
+
+
+void
+DC390_EnDisableCE( UCHAR mode, USHORT mechnum, PUCHAR regval )
+{
+
+    UCHAR bval;
+
+    bval = 0;
+    if(mode == ENABLE_CE)
+       *regval = 0xc0;
+    else
+       *regval = 0x80;
+    DC390_OutB(mechnum,*regval,bval);
+    if(mode == DISABLE_CE)
+       DC390_OutB(mechnum,*regval,bval);
+    udelay(160);
+}
+
+
+void
+DC390_EEpromOutDI( USHORT mechnum, PUCHAR regval, USHORT Carry )
+{
+    UCHAR bval;
+
+    bval = 0;
+    if(Carry)
+    {
+       bval = 0x40;
+       *regval = 0x80;
+       DC390_OutB(mechnum,*regval,bval);
+    }
+    udelay(160);
+    bval |= 0x80;
+    DC390_OutB(mechnum,*regval,bval);
+    udelay(160);
+    bval = 0;
+    DC390_OutB(mechnum,*regval,bval);
+    udelay(160);
+}
+
+
+UCHAR
+DC390_EEpromInDO( USHORT mechnum )
+{
+    UCHAR bval,regval;
+
+    regval = 0x80;
+    bval = 0x80;
+    DC390_OutB(mechnum,regval,bval);
+    udelay(160);
+    bval = 0x40;
+    DC390_OutB(mechnum,regval,bval);
+    udelay(160);
+    regval = 0x0;
+    bval = DC390_inByte(mechnum,regval);
+    if(bval == 0x22)
+       return(1);
+    else
+       return(0);
+}
+
+
+USHORT
+EEpromGetData1( USHORT mechnum )
+{
+    UCHAR i;
+    UCHAR carryFlag;
+    USHORT wval;
+
+    wval = 0;
+    for(i=0; i<16; i++)
+    {
+       wval <<= 1;
+       carryFlag = DC390_EEpromInDO(mechnum);
+       wval |= carryFlag;
+    }
+    return(wval);
+}
+
+
+void
+DC390_Prepare( USHORT mechnum, PUCHAR regval, UCHAR EEpromCmd )
+{
+    UCHAR i,j;
+    USHORT carryFlag;
+
+    carryFlag = 1;
+    j = 0x80;
+    for(i=0; i<9; i++)
+    {
+       DC390_EEpromOutDI(mechnum,regval,carryFlag);
+       carryFlag = (EEpromCmd & j) ? 1 : 0;
+       j >>= 1;
+    }
+}
+
+
+void
+DC390_ReadEEprom( USHORT mechnum, USHORT index )
+{
+    UCHAR   regval,cmd;
+    PUSHORT ptr;
+    USHORT  i;
+
+    ptr = (PUSHORT) &eepromBuf[index][0];
+    cmd = EEPROM_READ;
+    for(i=0; i<0x40; i++)
+    {
+       DC390_EnDisableCE(ENABLE_CE, mechnum, &regval);
+       DC390_Prepare(mechnum, &regval, cmd);
+       *ptr = EEpromGetData1(mechnum);
+       ptr++;
+       cmd++;
+       DC390_EnDisableCE(DISABLE_CE,mechnum,&regval);
+    }
+}
+
+
+USHORT
+DC390_CheckEEpromCheckSum( USHORT MechNum, USHORT index )
+{
+    USHORT wval, rc, *ptr;
+    UCHAR  i;
+
+    DC390_ReadEEprom( MechNum, index );
+    wval = 0;
+    ptr = (PUSHORT) &eepromBuf[index][0];
+    for(i=0; i<128 ;i+=2, ptr++)
+       wval += *ptr;
+    if( wval == 0x1234 )
+       rc = 0;
+    else
+       rc = -1;
+    return( rc );
+}
+
+
+USHORT
+DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum )
+{
+    USHORT devnum;
+
+    devnum = BusDevFunNum;
+
+    if(Mechnum == 2)
+    {
+       if(devnum & 0x80)
+            return(-1);
+       mech2bus = (UCHAR)((devnum & 0xff00) >> 8);       /* Bus num */
+       mech2Agent = ((UCHAR)(devnum & 0xff)) >> 3;       /* Dev num */
+       mech2Agent |= 0xc0;
+       mech2CfgSPenR = ((UCHAR)(devnum & 0xff)) & 0x07;  /* Fun num */
+       mech2CfgSPenR = (mech2CfgSPenR << 1) | 0x20;
+    }
+    else       /* use mech #1 method */
+    {
+       mech1addr = 0x80000000 | ((ULONG)devnum << 8);
+    }
+    return(0);
+}
+
+/***********************************************************************
+ * Function : static int DC390_init (struct Scsi_Host *host)
+ *
+ * Purpose :  initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure/
+ *
+ * Preconditions : when this function is called, the chip_type
+ *     field of the pACB structure MUST have been set.
+ ***********************************************************************/
+
+static int
+DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)
+{
+    PSH   psh;
+    PACB  pACB;
+
+    if( !DC390_CheckEEpromCheckSum( MechNum, index) )
+    {
+       psh = scsi_register( psht, sizeof(DC390_ACB) );
+       if( !psh )
+           return( -1 );
+       if( !pSH_start )
+       {
+           pSH_start = psh;
+           pSH_current = psh;
+       }
+       else
+       {
+           pSH_current->next = psh;
+           pSH_current = psh;
+       }
+
+#ifdef DC390_DEBUG0
+       printk("DC390: pSH = %8x,", (UINT) psh);
+       printk("DC390: Index %02i,", index);
+#endif
+
+       DC390_initACB( psh, io_port, Irq, index );
+       if( !DC390_initAdapter( psh, io_port, Irq, index ) )
+       {
+           pACB = (PACB) psh->hostdata;
+           if( !pACB_start )
+           {
+               pACB_start = pACB;
+               pACB_current = pACB;
+               pACB->pNextACB = (PACB) -1;
+           }
+           else
+           {
+               pACB_current->pNextACB = pACB;
+               pACB_current = pACB;
+               pACB->pNextACB = (PACB)  -1;
+           }
+
+#ifdef DC390_DEBUG0
+       printk("DC390: pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n",
+             (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array);
+       printk("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n",
+             sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) );
+#endif
+
+       }
+       else
+       {
+           pSH_start = NULL;
+           scsi_unregister( psh );
+           return( -1 );
+       }
+       return( 0 );
+    }
+    else
+    {
+       printk("DC390_init: EEPROM reading error!\n");
+       return( -1 );
+    }
+}
+
+
+/***********************************************************************
+ * Function : int DC390_detect(Scsi_Host_Template *psht)
+ *
+ * Purpose : detects and initializes AMD53C974 SCSI chips
+ *          that were autoprobed, overridden on the LILO command line,
+ *          or specified at compile time.
+ *
+ * Inputs : psht - template for this SCSI adapter
+ *
+ * Returns : number of host adapters detected
+ *
+ ***********************************************************************/
+
+int
+DC390_detect(Scsi_Host_Template *psht)
+{
+#ifdef FOR_PCI_OK
+    UCHAR   pci_bus, pci_device_fn;
+    int     error = 0;
+    USHORT  chipType = 0;
+    USHORT  i;
+#endif
+
+    UCHAR   irq;
+    UCHAR   istatus;
+#ifndef VERSION_ELF_1_2_13
+    UINT    io_port;
+#else
+    ULONG   io_port;
+#endif
+    USHORT  adaptCnt = 0;      /* Number of boards detected */
+    USHORT  pci_index = 0;     /* Device index to PCI BIOS calls */
+    USHORT  MechNum, BusDevFunNum;
+    ULONG   wlval;
+
+#ifndef VERSION_ELF_1_2_13
+    psht->proc_dir = &proc_scsi_tmscsim;
+#endif
+
+    InitialTime = 1;
+    pSHT_start = psht;
+    pACB_start = NULL;
+
+    MechNum = 1;
+    for( ; (MechNum < 3) && (!adaptCnt); MechNum++)
+    {
+       BusDevFunNum = 0;
+       for (; adaptCnt < MAX_ADAPTER_NUM ;)
+       {
+           if( !DC390_ToMech( MechNum, BusDevFunNum) )
+           {
+                wlval = DC390_inDword( MechNum, PCI_VENDOR_ID);
+                if(wlval == ( (PCI_DEVICE_ID_AMD53C974 << 16)+
+                               PCI_VENDOR_ID_AMD) )
+                {
+                   io_port =DC390_inDword(MechNum,PCI_BASE_ADDRESS_0) & 0xFFFE;
+                   irq = DC390_inByte( MechNum, PCI_INTERRUPT_LINE);
+#ifdef DC390_DEBUG0
+               printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
+#endif
+                   if( !DC390_init(psht, io_port, irq, pci_index, MechNum) )
+                   {
+                       adaptCnt++;
+                       pci_index++;
+                       istatus = inb( (USHORT)io_port+INT_Status );    /* Reset Pending INT */
+#ifdef DC390_DEBUG0
+               printk("DC390: Mech=%2x,\n",(UCHAR) MechNum);
+#endif
+                   }
+                }
+           }
+           if( BusDevFunNum != 0xfff8 )
+               BusDevFunNum += 8;          /* next device # */
+           else
+               break;
+       }
+    }
+
+#ifdef FOR_PCI_OK
+    if ( pcibios_present() )
+    {
+       for (i = 0; i < MAX_ADAPTER_NUM; ++i)
+       {
+           if( !pcibios_find_device( PCI_VENDOR_ID_AMD,
+                               PCI_DEVICE_ID_AMD53C974,
+                               pci_index, &pci_bus, &pci_device_fn) )
+           {
+               chipType = PCI_DEVICE_ID_AMD53C974;
+               pci_index++;
+           }
+
+           if( chipType )
+           {
+
+               error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                                 PCI_BASE_ADDRESS_0, &io_port);
+               error |= pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                 PCI_INTERRUPT_LINE, &irq);
+               if( error )
+               {
+                   printk("DC390_detect: reading configuration registers error!\n");
+                   InitialTime = 0;
+                   return( 0 );
+               }
+
+               (USHORT) io_port = (USHORT) io_port & 0xFFFE;
+#ifdef DC390_DEBUG0
+               printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
+#endif
+               if( !DC390_init(psht, io_port, irq, i) )
+                   adaptCnt++;
+               chipType = 0;
+           }
+           else
+               break;
+       }
+    }
+#endif
+
+    InitialTime = 0;
+    adapterCnt = adaptCnt;
+    return( adaptCnt );
+}
+
+
+#ifndef VERSION_ELF_1_2_13
+
+/********************************************************************
+ * Function: tmscsim_set_info()
+ *
+ * Purpose: Set adapter info (!)
+ *
+ * Not yet implemented
+ *
+ *******************************************************************/
+
+int tmscsim_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+  return(-ENOSYS);  /* Currently this is a no-op */
+}
+
+/********************************************************************
+ * Function: tmscsim_proc_info(char* buffer, char **start,
+ *                          off_t offset, int length, int hostno, int inout)
+ *
+ * Purpose: return SCSI Adapter/Device Info
+ *
+ * Input: buffer: Pointer to a buffer where to write info
+ *       start :
+ *       offset:
+ *       hostno: Host adapter index
+ *       inout : Read (=0) or set(!=0) info
+ *
+ * Output: buffer: contains info
+ *        length; length of info in buffer
+ *
+ * return value: length
+ *
+ ********************************************************************/
+
+/* KG: proc_info taken from driver aha152x.c */
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, ## args)
+
+#define YESNO(YN)\
+if (YN) SPRINTF(" Yes ");\
+else SPRINTF(" No  ")
+
+int tmscsim_proc_info(char *buffer, char **start,
+                     off_t offset, int length, int hostno, int inout)
+{
+  int dev, spd, spd1;
+  char *pos = buffer;
+  PSH shpnt;
+  PACB acbpnt;
+  PDCB dcbpnt;
+  unsigned long flags;
+/*  Scsi_Cmnd *ptr; */
+
+  acbpnt = pACB_start;
+
+  while(acbpnt != (PACB)-1)
+     {
+       shpnt = acbpnt->pScsiHost;
+       if (shpnt->host_no == hostno) break;
+       acbpnt = acbpnt->pNextACB;
+     }
+
+  if (acbpnt == (PACB)-1) return(-ESRCH);
+  if(!shpnt) return(-ESRCH);
+
+  if(inout) // Has data been written to the file ?
+    return(tmscsim_set_info(buffer, length, shpnt));
+
+  SPRINTF("Tekram DC390(T) PCI SCSI Host Adadpter, ");
+  SPRINTF("Driver Version 1.10, 1996/12/05\n");
+
+  save_flags(flags);
+  cli();
+
+  SPRINTF("SCSI Host Nr %i, ", shpnt->host_no);
+  SPRINTF("DC390 Adapter Nr %i\n", acbpnt->AdapterIndex);
+  SPRINTF("IOPortBase 0x%04x, ", acbpnt->IOPortBase);
+  SPRINTF("IRQLevel 0x%02x\n", acbpnt->IRQLevel);
+
+  SPRINTF("MaxID %i, MaxLUN %i, ",acbpnt->max_id, acbpnt->max_lun);
+  SPRINTF("AdapterID %i, AdapterLUN %i\n", acbpnt->AdaptSCSIID, acbpnt->AdaptSCSILUN);
+
+  SPRINTF("TagMaxNum %i, Status %i\n", acbpnt->TagMaxNum, acbpnt->status);
+
+  SPRINTF("Nr of attached devices: %i\n", acbpnt->DeviceCnt);
+
+  SPRINTF("Un ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs\n");
+
+  dcbpnt = acbpnt->pLinkDCB;
+  for (dev = 0; dev < acbpnt->DeviceCnt; dev++)
+     {
+      SPRINTF("%02i %02i  %02i ", dev, dcbpnt->UnitSCSIID, dcbpnt->UnitSCSILUN);
+      YESNO(dcbpnt->DevMode & PARITY_CHK_);
+      YESNO(dcbpnt->SyncMode & SYNC_NEGO_DONE);
+      YESNO(dcbpnt->DevMode & EN_DISCONNECT_);
+      YESNO(dcbpnt->DevMode & SEND_START_);
+      YESNO(dcbpnt->SyncMode & EN_TAG_QUEUING);
+      SPRINTF("  %03i ns ", (dcbpnt->NegoPeriod) << 2);
+      if (dcbpnt->SyncOffset & 0x0f)
+      {
+        spd = 1000/(dcbpnt->NegoPeriod <<2);
+        spd1 = 1000%(dcbpnt->NegoPeriod <<2);
+        spd1 = (spd1 * 10)/(dcbpnt->NegoPeriod <<2);
+        SPRINTF("   %2i.%1i M      %02i\n", spd, spd1, (dcbpnt->SyncOffset & 0x0f));
+      }
+      else SPRINTF("\n");
+      /* Add more info ...*/
+      dcbpnt = dcbpnt->pNextDCB;
+     }
+
+  restore_flags(flags);
+  *start = buffer + offset;
+
+  if (pos - buffer < offset)
+    return 0;
+  else if (pos - buffer - offset < length)
+    return pos - buffer - offset;
+  else
+    return length;
+}
+#endif /* VERSION_ELF_1_2_13 */
+
+
+#ifdef MODULE
+
+/***********************************************************************
+ * Function : static int DC390_shutdown (struct Scsi_Host *host)
+ *
+ * Purpose : does a clean (we hope) shutdown of the SCSI chip.
+ *          Use prior to dumping core, unloading the driver, etc.
+ *
+ * Returns : 0 on success
+ ***********************************************************************/
+static int
+DC390_shutdown (struct Scsi_Host *host)
+{
+    UCHAR    bval;
+    USHORT   ioport;
+    unsigned long flags;
+    PACB pACB = (PACB)(host->hostdata);
+
+    ioport = (unsigned int) pACB->IOPortBase;
+
+    save_flags (flags);
+    cli();
+
+/*  pACB->soft_reset(host); */
+
+#ifdef DC390_DEBUG0
+    printk("DC390: shutdown,");
+#endif
+
+    bval = inb(ioport+CtrlReg1);
+    bval |= DIS_INT_ON_SCSI_RST;
+    outb(bval,ioport+CtrlReg1);  /* disable interrupt */
+    DC390_ResetSCSIBus( pACB );
+
+    restore_flags (flags);
+    return( 0 );
+}
+
+
+int DC390_release(struct Scsi_Host *host)
+{
+    int irq_count;
+    struct Scsi_Host *tmp;
+
+    DC390_shutdown (host);
+
+    if (host->irq != IRQ_NONE)
+    {
+       for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next)
+       {
+           if ( tmp->irq == host->irq )
+               ++irq_count;
+       }
+       if (irq_count == 1)
+        {
+#ifdef DC390_DEBUG0
+           printk("DC390: Free IRQ %i.",host->irq);
+#endif
+#ifndef VERSION_ELF_1_2_13
+           free_irq(host->irq,NULL);
+#else
+           free_irq(host->irq);
+#endif
+        }
+    }
+
+    release_region(host->io_port,host->n_io_port);
+
+    return( 1 );
+}
+
+Scsi_Host_Template driver_template = DC390_T;
+#include "scsi_module.c"
+#endif /* def MODULE */
+
diff --git a/drivers/scsi/tmscsim.h b/drivers/scsi/tmscsim.h
new file mode 100644 (file)
index 0000000..361c488
--- /dev/null
@@ -0,0 +1,680 @@
+/***********************************************************************
+;*     File Name : TMSCSIM.H                                          *
+;*                 TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter  *
+;*                 Device Driver                                      *
+;***********************************************************************/
+
+#ifndef TMSCSIM_H
+#define TMSCSIM_H
+
+#define IRQ_NONE 255
+
+typedef unsigned char  UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned long  ULONG;
+typedef unsigned int   UINT;
+
+typedef UCHAR          *PUCHAR;
+typedef USHORT         *PUSHORT;
+typedef ULONG          *PULONG;
+typedef Scsi_Host_Template  *PSHT;
+typedef struct Scsi_Host    *PSH;
+typedef Scsi_Device    *PSCSIDEV;
+typedef Scsi_Cmnd      *PSCSICMD;
+typedef void           *PVOID;
+typedef struct scatterlist  *PSGL, SGL;
+
+
+/*;-----------------------------------------------------------------------*/
+typedef  struct  _SyncMsg
+{
+UCHAR          ExtendMsg;
+UCHAR          ExtMsgLen;
+UCHAR          SyncXferReq;
+UCHAR          Period;
+UCHAR          ReqOffset;
+} SyncMsg;
+/*;-----------------------------------------------------------------------*/
+typedef  struct  _Capacity
+{
+ULONG          BlockCount;
+ULONG          BlockLength;
+} Capacity;
+/*;-----------------------------------------------------------------------*/
+typedef  struct  _SGentry
+{
+ULONG          SGXferDataPtr;
+ULONG          SGXferDataLen;
+} SGentry;
+
+typedef  struct  _SGentry1
+{
+ULONG          SGXLen;
+ULONG          SGXPtr;
+} SGentry1, *PSGE;
+
+
+#define MAX_ADAPTER_NUM        4
+#define MAX_DEVICES            10
+#define MAX_SG_LIST_BUF        16
+#define MAX_CMD_QUEUE          20
+#define MAX_CMD_PER_LUN        8
+#define MAX_SCSI_ID            8
+#define MAX_SRB_CNT            MAX_CMD_QUEUE+4
+#define END_SCAN               2
+
+#define SEL_TIMEOUT            153     /* 250 ms selection timeout (@ 40 MHz) */
+
+/*
+;-----------------------------------------------------------------------
+; SCSI Request Block
+;-----------------------------------------------------------------------
+*/
+struct _SRB
+{
+UCHAR          CmdBlock[12];
+
+struct _SRB    *pNextSRB;
+struct _DCB    *pSRBDCB;
+PSCSICMD       pcmd;
+PSGL           pSegmentList;
+
+ULONG          PhysSRB;
+ULONG          TotalXferredLen;
+ULONG          SGPhysAddr;     /*;a segment starting address */
+ULONG          SGToBeXferLen;  /*; to be xfer length */
+
+SGL            Segmentx;       /* make a one entry of S/G list table */
+
+PUCHAR         pMsgPtr;
+USHORT         SRBState;
+USHORT         Revxx2;         /* ??? */
+
+UCHAR          MsgInBuf[6];
+UCHAR          MsgOutBuf[6];
+
+UCHAR          AdaptStatus;
+UCHAR          TargetStatus;
+UCHAR          MsgCnt;
+UCHAR          EndMessage;
+UCHAR          TagNumber;
+UCHAR          SGcount;
+UCHAR          SGIndex;
+UCHAR          IORBFlag;       /*;81h-Reset, 2-retry */
+
+UCHAR          SRBStatus;
+UCHAR          RetryCnt;
+UCHAR          SRBFlag;        /*; b0-AutoReqSense,b6-Read,b7-write */
+                               /*; b4-settimeout,b5-Residual valid */
+UCHAR          ScsiCmdLen;
+UCHAR          ScsiPhase;
+UCHAR          Reserved3[3];   /*;for dword alignment */
+ULONG          Segment0[2];
+ULONG          Segment1[2];
+};
+
+typedef  struct  _SRB   DC390_SRB, *PSRB;
+
+/*
+;-----------------------------------------------------------------------
+; Device Control Block
+;-----------------------------------------------------------------------
+*/
+struct _DCB
+{
+struct _DCB    *pNextDCB;
+struct _ACB    *pDCBACB;
+
+PSCSICMD       pQIORBhead;
+PSCSICMD       pQIORBtail;
+PSCSICMD       AboIORBhead;
+PSCSICMD       AboIORBtail;
+USHORT         QIORBCnt;
+USHORT         AboIORBcnt;
+
+PSRB           pWaitingSRB;
+PSRB           pWaitLast;
+PSRB           pGoingSRB;
+PSRB           pGoingLast;
+PSRB           pActiveSRB;
+USHORT         GoingSRBCnt;
+USHORT         WaitSRBCnt;     /* ??? */
+
+ULONG          TagMask;
+
+USHORT         MaxCommand;
+USHORT         AdaptIndex;     /*; UnitInfo struc start */
+USHORT         UnitIndex;      /*; nth Unit on this card */
+UCHAR          UnitSCSIID;     /*; SCSI Target ID  (SCSI Only) */
+UCHAR          UnitSCSILUN;    /*; SCSI Log.  Unit (SCSI Only) */
+
+UCHAR          IdentifyMsg;
+UCHAR          CtrlR1;
+UCHAR          CtrlR3;
+UCHAR          CtrlR4;
+
+UCHAR          InqDataBuf[8];
+UCHAR          CapacityBuf[8];
+UCHAR          DevMode;
+UCHAR          AdpMode;
+UCHAR          SyncMode;       /*; 0:async mode */
+UCHAR          NegoPeriod;     /*;for nego. */
+UCHAR          SyncPeriod;     /*;for reg. */
+UCHAR          SyncOffset;     /*;for reg. and nego.(low nibble) */
+UCHAR          UnitCtrlFlag;
+UCHAR          DCBFlag;
+UCHAR          DevType;
+UCHAR          Reserved2[3];   /*;for dword alignment */
+};
+
+typedef  struct  _DCB   DC390_DCB, *PDCB;
+/*
+;-----------------------------------------------------------------------
+; Adapter Control Block
+;-----------------------------------------------------------------------
+*/
+struct _ACB
+{
+ULONG          PhysACB;
+PSH            pScsiHost;
+struct _ACB    *pNextACB;
+USHORT         IOPortBase;
+USHORT         Revxx1;         /* ??? */
+
+PDCB           pLinkDCB;
+PDCB           pDCBRunRobin;
+PDCB           pActiveDCB;
+PDCB           pDCB_free;
+PSRB           pFreeSRB;
+PSRB           pTmpSRB;
+USHORT         SRBCount;
+USHORT         AdapterIndex;   /*; nth Adapter this driver */
+USHORT         max_id;
+USHORT         max_lun;
+
+UCHAR          msgin123[4];
+UCHAR          status;
+UCHAR          AdaptSCSIID;    /*; Adapter SCSI Target ID */
+UCHAR          AdaptSCSILUN;   /*; Adapter SCSI LUN */
+UCHAR          DeviceCnt;
+UCHAR          IRQLevel;
+UCHAR          TagMaxNum;
+UCHAR          ACBFlag;
+UCHAR          Gmode2;
+UCHAR          LUNchk;
+UCHAR          scan_devices;
+UCHAR          HostID_Bit;
+UCHAR          Reserved1[1];   /*;for dword alignment */
+UCHAR          DCBmap[MAX_SCSI_ID];
+DC390_DCB      DCB_array[MAX_DEVICES];         /* +74h,  Len=3E8 */
+DC390_SRB      SRB_array[MAX_SRB_CNT];         /* +45Ch, Len=  */
+DC390_SRB      TmpSRB;
+};
+
+typedef  struct  _ACB   DC390_ACB, *PACB;
+
+/*;-----------------------------------------------------------------------*/
+
+
+#define BIT31  0x80000000
+#define BIT30  0x40000000
+#define BIT29  0x20000000
+#define BIT28  0x10000000
+#define BIT27  0x08000000
+#define BIT26  0x04000000
+#define BIT25  0x02000000
+#define BIT24  0x01000000
+#define BIT23  0x00800000
+#define BIT22  0x00400000
+#define BIT21  0x00200000
+#define BIT20  0x00100000
+#define BIT19  0x00080000
+#define BIT18  0x00040000
+#define BIT17  0x00020000
+#define BIT16  0x00010000
+#define BIT15  0x00008000
+#define BIT14  0x00004000
+#define BIT13  0x00002000
+#define BIT12  0x00001000
+#define BIT11  0x00000800
+#define BIT10  0x00000400
+#define BIT9   0x00000200
+#define BIT8   0x00000100
+#define BIT7   0x00000080
+#define BIT6   0x00000040
+#define BIT5   0x00000020
+#define BIT4   0x00000010
+#define BIT3   0x00000008
+#define BIT2   0x00000004
+#define BIT1   0x00000002
+#define BIT0   0x00000001
+
+/*;---UnitCtrlFlag */
+#define UNIT_ALLOCATED BIT0
+#define UNIT_INFO_CHANGED BIT1
+#define FORMATING_MEDIA BIT2
+#define UNIT_RETRY     BIT3
+
+/*;---UnitFlags */
+#define DASD_SUPPORT   BIT0
+#define SCSI_SUPPORT   BIT1
+#define ASPI_SUPPORT   BIT2
+
+/*;----SRBState machine definition */
+#define SRB_FREE       0
+#define SRB_WAIT       BIT0
+#define SRB_READY      BIT1
+#define SRB_MSGOUT     BIT2    /*;arbitration+msg_out 1st byte*/
+#define SRB_MSGIN      BIT3
+#define SRB_MSGIN_MULTI BIT4
+#define SRB_COMMAND    BIT5
+#define SRB_START_     BIT6    /*;arbitration+msg_out+command_out*/
+#define SRB_DISCONNECT BIT7
+#define SRB_DATA_XFER  BIT8
+#define SRB_XFERPAD    BIT9
+#define SRB_STATUS     BIT10
+#define SRB_COMPLETED  BIT11
+#define SRB_ABORT_SENT BIT12
+#define DO_SYNC_NEGO   BIT13
+#define SRB_UNEXPECT_RESEL BIT14
+
+/*;---ACBFlag */
+#define RESET_DEV      BIT0
+#define RESET_DETECT   BIT1
+#define RESET_DONE     BIT2
+
+/*;---DCBFlag */
+#define ABORT_DEV_     BIT0
+
+/*;---SRBstatus */
+#define SRB_OK         BIT0
+#define ABORTION       BIT1
+#define OVER_RUN       BIT2
+#define UNDER_RUN      BIT3
+#define PARITY_ERROR   BIT4
+#define SRB_ERROR      BIT5
+
+/*;---SRBFlag */
+#define DATAOUT        BIT7
+#define DATAIN         BIT6
+#define RESIDUAL_VALID BIT5
+#define ENABLE_TIMER   BIT4
+#define RESET_DEV0     BIT2
+#define ABORT_DEV      BIT1
+#define AUTO_REQSENSE  BIT0
+
+/*;---Adapter status */
+#define H_STATUS_GOOD   0
+#define H_SEL_TIMEOUT   0x11
+#define H_OVER_UNDER_RUN 0x12
+#define H_UNEXP_BUS_FREE 0x13
+#define H_TARGET_PHASE_F 0x14
+#define H_INVALID_CCB_OP 0x16
+#define H_LINK_CCB_BAD  0x17
+#define H_BAD_TARGET_DIR 0x18
+#define H_DUPLICATE_CCB  0x19
+#define H_BAD_CCB_OR_SG  0x1A
+#define H_ABORT         0x0FF
+
+/*; SCSI Status byte codes*/
+#define SCSI_STAT_GOOD         0x0     /*;  Good status */
+#define SCSI_STAT_CHECKCOND    0x02    /*;  SCSI Check Condition */
+#define SCSI_STAT_CONDMET      0x04    /*;  Condition Met */
+#define SCSI_STAT_BUSY         0x08    /*;  Target busy status */
+#define SCSI_STAT_INTER        0x10    /*;  Intermediate status */
+#define SCSI_STAT_INTERCONDMET 0x14    /*;  Intermediate condition met */
+#define SCSI_STAT_RESCONFLICT  0x18    /*;  Reservation conflict */
+#define SCSI_STAT_CMDTERM      0x22    /*;  Command Terminated */
+#define SCSI_STAT_QUEUEFULL    0x28    /*;  Queue Full */
+
+#define SCSI_STAT_UNEXP_BUS_F  0xFD    /*;  Unexpect Bus Free */
+#define SCSI_STAT_BUS_RST_DETECT 0xFE  /*;  Scsi Bus Reset detected */
+#define SCSI_STAT_SEL_TIMEOUT  0xFF    /*;  Selection Time out */
+
+/*;---Sync_Mode */
+#define SYNC_DISABLE   0
+#define SYNC_ENABLE    BIT0
+#define SYNC_NEGO_DONE BIT1
+#define WIDE_ENABLE    BIT2
+#define WIDE_NEGO_DONE BIT3
+#define EN_TAG_QUEUING BIT4
+#define EN_ATN_STOP    BIT5
+
+#define SYNC_NEGO_OFFSET 15
+
+/*;---SCSI bus phase*/
+#define SCSI_DATA_OUT  0
+#define SCSI_DATA_IN   1
+#define SCSI_COMMAND   2
+#define SCSI_STATUS_   3
+#define SCSI_NOP0      4
+#define SCSI_NOP1      5
+#define SCSI_MSG_OUT   6
+#define SCSI_MSG_IN    7
+
+/*;----SCSI MSG BYTE*/
+#define MSG_COMPLETE           0x00
+#define MSG_EXTENDED           0x01
+#define MSG_SAVE_PTR           0x02
+#define MSG_RESTORE_PTR        0x03
+#define MSG_DISCONNECT         0x04
+#define MSG_INITIATOR_ERROR    0x05
+#define MSG_ABORT              0x06
+#define MSG_REJECT_            0x07
+#define MSG_NOP                0x08
+#define MSG_PARITY_ERROR       0x09
+#define MSG_LINK_CMD_COMPL     0x0A
+#define MSG_LINK_CMD_COMPL_FLG 0x0B
+#define MSG_BUS_RESET          0x0C
+#define MSG_ABORT_TAG          0x0D
+#define MSG_SIMPLE_QTAG        0x20
+#define MSG_HEAD_QTAG          0x21
+#define MSG_ORDER_QTAG         0x22
+#define MSG_IDENTIFY           0x80
+#define MSG_HOST_ID            0x0C0
+
+/*;----SCSI STATUS BYTE*/
+#define STATUS_GOOD            0x00
+#define CHECK_CONDITION_       0x02
+#define STATUS_BUSY            0x08
+#define STATUS_INTERMEDIATE    0x10
+#define RESERVE_CONFLICT       0x18
+
+/* cmd->result */
+#define STATUS_MASK_           0xFF
+#define MSG_MASK               0xFF00
+#define RETURN_MASK            0xFF0000
+
+/*
+**  Inquiry Data format
+*/
+
+typedef struct _SCSIInqData { /* INQ */
+
+       UCHAR    DevType;               /* Periph Qualifier & Periph Dev Type*/
+       UCHAR    RMB_TypeMod;           /* rem media bit & Dev Type Modifier */
+       UCHAR    Vers;                  /* ISO, ECMA, & ANSI versions        */
+       UCHAR    RDF;                   /* AEN, TRMIOP, & response data format*/
+       UCHAR    AddLen;                /* length of additional data         */
+       UCHAR    Res1;                  /* reserved                          */
+       UCHAR    Res2;                  /* reserved                          */
+       UCHAR    Flags;                 /* RelADr,Wbus32,Wbus16,Sync,etc.    */
+       UCHAR    VendorID[8];           /* Vendor Identification             */
+       UCHAR    ProductID[16];         /* Product Identification            */
+       UCHAR    ProductRev[4];         /* Product Revision                  */
+
+
+} SCSI_INQDATA, *PSCSI_INQDATA;
+
+
+/*  Inquiry byte 0 masks */
+
+
+#define SCSI_DEVTYPE       0x1F      /* Peripheral Device Type             */
+#define SCSI_PERIPHQUAL     0xE0      /* Peripheral Qualifier              */
+
+
+/*  Inquiry byte 1 mask */
+
+#define SCSI_REMOVABLE_MEDIA  0x80    /* Removable Media bit (1=removable)  */
+
+
+/*  Peripheral Device Type definitions */
+
+#define SCSI_DASD               0x00      /* Direct-access Device         */
+#define SCSI_SEQACESS           0x01      /* Sequential-access device     */
+#define SCSI_PRINTER            0x02      /* Printer device               */
+#define SCSI_PROCESSOR          0x03      /* Processor device             */
+#define SCSI_WRITEONCE          0x04      /* Write-once device            */
+#define SCSI_CDROM              0x05      /* CD-ROM device                */
+#define SCSI_SCANNER            0x06      /* Scanner device               */
+#define SCSI_OPTICAL            0x07      /* Optical memory device        */
+#define SCSI_MEDCHGR            0x08      /* Medium changer device        */
+#define SCSI_COMM               0x09      /* Communications device        */
+#define SCSI_NODEV              0x1F      /* Unknown or no device type    */
+
+/*
+** Inquiry flag definitions (Inq data byte 7)
+*/
+
+#define SCSI_INQ_RELADR       0x80    /* device supports relative addressing*/
+#define SCSI_INQ_WBUS32       0x40    /* device supports 32 bit data xfers  */
+#define SCSI_INQ_WBUS16       0x20    /* device supports 16 bit data xfers  */
+#define SCSI_INQ_SYNC        0x10    /* device supports synchronous xfer   */
+#define SCSI_INQ_LINKED       0x08    /* device supports linked commands    */
+#define SCSI_INQ_CMDQUEUE     0x02    /* device supports command queueing   */
+#define SCSI_INQ_SFTRE       0x01    /* device supports soft resets */
+
+
+/*
+;==========================================================
+; EEPROM byte offset
+;==========================================================
+*/
+typedef  struct  _EEprom
+{
+UCHAR  EE_MODE1;
+UCHAR  EE_SPEED;
+UCHAR  xx1;
+UCHAR  xx2;
+} EEprom, *PEEprom;
+
+#define EE_ADAPT_SCSI_ID 64
+#define EE_MODE2       65
+#define EE_DELAY       66
+#define EE_TAG_CMD_NUM 67
+
+/*; EE_MODE1 bits definition*/
+#define PARITY_CHK_    BIT0
+#define SYNC_NEGO_     BIT1
+#define EN_DISCONNECT_ BIT2
+#define SEND_START_    BIT3
+#define TAG_QUEUING_   BIT4
+
+/*; EE_MODE2 bits definition*/
+#define MORE2_DRV      BIT0
+#define GREATER_1G     BIT1
+#define RST_SCSI_BUS   BIT2
+#define ACTIVE_NEGATION BIT3
+#define NO_SEEK        BIT4
+#define LUN_CHECK      BIT5
+
+#define ENABLE_CE      1
+#define DISABLE_CE     0
+#define EEPROM_READ    0x80
+
+/*
+;==========================================================
+;      AMD 53C974 Registers bit Definition
+;==========================================================
+*/
+/*
+;====================
+; SCSI Register
+;====================
+*/
+
+/*; Command Reg.(+0CH) */
+#define DMA_COMMAND            BIT7
+#define NOP_CMD                0
+#define CLEAR_FIFO_CMD         1
+#define RST_DEVICE_CMD         2
+#define RST_SCSI_BUS_CMD       3
+#define INFO_XFER_CMD          0x10
+#define INITIATOR_CMD_CMPLTE   0x11
+#define MSG_ACCEPTED_CMD       0x12
+#define XFER_PAD_BYTE          0x18
+#define SET_ATN_CMD            0x1A
+#define RESET_ATN_CMD          0x1B
+#define SELECT_W_ATN           0x42
+#define SEL_W_ATN_STOP         0x43
+#define EN_SEL_RESEL           0x44
+#define SEL_W_ATN2             0x46
+#define DATA_XFER_CMD          INFO_XFER_CMD
+
+
+/*; SCSI Status Reg.(+10H) */
+#define INTERRUPT              BIT7
+#define ILLEGAL_OP_ERR         BIT6
+#define PARITY_ERR             BIT5
+#define COUNT_2_ZERO           BIT4
+#define GROUP_CODE_VALID       BIT3
+#define SCSI_PHASE_MASK        (BIT2+BIT1+BIT0)
+
+/*; Interrupt Status Reg.(+14H) */
+#define SCSI_RESET             BIT7
+#define INVALID_CMD            BIT6
+#define DISCONNECTED           BIT5
+#define SERVICE_REQUEST        BIT4
+#define SUCCESSFUL_OP          BIT3
+#define RESELECTED             BIT2
+#define SEL_ATTENTION          BIT1
+#define SELECTED               BIT0
+
+/*; Internal State Reg.(+18H) */
+#define SYNC_OFFSET_FLAG       BIT3
+#define INTRN_STATE_MASK       (BIT2+BIT1+BIT0)
+
+/*; Clock Factor Reg.(+24H) */
+#define CLK_FREQ_40MHZ         0
+#define CLK_FREQ_35MHZ         (BIT2+BIT1+BIT0)
+#define CLK_FREQ_30MHZ         (BIT2+BIT1)
+#define CLK_FREQ_25MHZ         (BIT2+BIT0)
+#define CLK_FREQ_20MHZ         BIT2
+#define CLK_FREQ_15MHZ         (BIT1+BIT0)
+#define CLK_FREQ_10MHZ         BIT1
+
+/*; Control Reg. 1(+20H) */
+#define EXTENDED_TIMING        BIT7
+#define DIS_INT_ON_SCSI_RST    BIT6
+#define PARITY_ERR_REPO        BIT4
+#define SCSI_ID_ON_BUS         (BIT2+BIT1+BIT0)
+
+/*; Control Reg. 2(+2CH) */
+#define EN_FEATURE             BIT6
+#define EN_SCSI2_CMD           BIT3
+
+/*; Control Reg. 3(+30H) */
+#define ID_MSG_CHECK           BIT7
+#define EN_QTAG_MSG            BIT6
+#define EN_GRP2_CMD            BIT5
+#define FAST_SCSI              BIT4    /* ;10MB/SEC */
+#define FAST_CLK               BIT3    /* ;25 - 40 MHZ */
+
+/*; Control Reg. 4(+34H) */
+#define EATER_12NS             0
+#define EATER_25NS             BIT7
+#define EATER_35NS             BIT6
+#define EATER_0NS              (BIT7+BIT6)
+#define NEGATE_REQACKDATA      BIT2
+#define NEGATE_REQACK          BIT3
+/*
+;====================
+; DMA Register
+;====================
+*/
+/*; DMA Command Reg.(+40H) */
+#define READ_DIRECTION         BIT7
+#define WRITE_DIRECTION        0
+#define EN_DMA_INT             BIT6
+#define MAP_TO_MDL             BIT5
+#define DIAGNOSTIC             BIT4
+#define DMA_IDLE_CMD           0
+#define DMA_BLAST_CMD          BIT0
+#define DMA_ABORT_CMD          BIT1
+#define DMA_START_CMD          (BIT1+BIT0)
+
+/*; DMA Status Reg.(+54H) */
+#define PCI_MS_ABORT           BIT6
+#define BLAST_COMPLETE         BIT5
+#define SCSI_INTERRUPT         BIT4
+#define DMA_XFER_DONE          BIT3
+#define DMA_XFER_ABORT         BIT2
+#define DMA_XFER_ERROR         BIT1
+#define POWER_DOWN             BIT0
+
+/*
+; DMA SCSI Bus and Ctrl.(+70H)
+;EN_INT_ON_PCI_ABORT
+*/
+
+/*
+;==========================================================
+; SCSI Chip register address offset
+;==========================================================
+*/
+#define CtcReg_Low     0x00
+#define CtcReg_Mid     0x04
+#define ScsiFifo       0x08
+#define ScsiCmd        0x0C
+#define Scsi_Status    0x10
+#define INT_Status     0x14
+#define Sync_Period    0x18
+#define Sync_Offset    0x1C
+#define CtrlReg1       0x20
+#define Clk_Factor     0x24
+#define CtrlReg2       0x2C
+#define CtrlReg3       0x30
+#define CtrlReg4       0x34
+#define CtcReg_High    0x38
+#define DMA_Cmd        0x40
+#define DMA_XferCnt    0x44
+#define DMA_XferAddr   0x48
+#define DMA_Wk_ByteCntr 0x4C
+#define DMA_Wk_AddrCntr 0x50
+#define DMA_Status     0x54
+#define DMA_MDL_Addr   0x58
+#define DMA_Wk_MDL_Cntr 0x5C
+#define DMA_ScsiBusCtrl 0x70
+
+#define StcReg_Low     CtcReg_Low
+#define StcReg_Mid     CtcReg_Mid
+#define Scsi_Dest_ID   Scsi_Status
+#define Scsi_TimeOut   INT_Status
+#define Intern_State   Sync_Period
+#define Current_Fifo   Sync_Offset
+#define StcReg_High    CtcReg_High
+
+#define am_target      Scsi_Status
+#define am_timeout     INT_Status
+#define am_seq_step    Sync_Period
+#define am_fifo_count  Sync_Offset
+
+
+#define DC390_read8(address)                          \
+       inb(DC390_ioport + (address)))
+
+#define DC390_read16(address)                         \
+       inw(DC390_ioport + (address)))
+
+#define DC390_read32(address)                         \
+       inl(DC390_ioport + (address)))
+
+#define DC390_write8(address,value)                   \
+       outb((value), DC390_ioport + (address)))
+
+#define DC390_write16(address,value)                  \
+       outw((value), DC390_ioport + (address)))
+
+#define DC390_write32(address,value)                  \
+       outl((value), DC390_ioport + (address)))
+
+
+/* Configuration method #1 */
+#define PCI_CFG1_ADDRESS_REG           0xcf8
+#define PCI_CFG1_DATA_REG              0xcfc
+#define PCI_CFG1_ENABLE                0x80000000
+#define PCI_CFG1_TUPPLE(bus, device, function, register)               \
+       (PCI_CFG1_ENABLE | (((bus) << 16) & 0xff0000) |                 \
+       (((device) << 11) & 0xf800) | (((function) << 8) & 0x700)|      \
+       (((register) << 2) & 0xfc))
+
+/* Configuration method #2 */
+#define PCI_CFG2_ENABLE_REG            0xcf8
+#define PCI_CFG2_FORWARD_REG           0xcfa
+#define PCI_CFG2_ENABLE                0x0f0
+#define PCI_CFG2_TUPPLE(function)                                      \
+       (PCI_CFG2_ENABLE | (((function) << 1) & 0xe))
+
+
+#endif /* TMSCSIM_H */
index 14135464fd42ed44cf486fce8b7daf679ae912fd..3d9b4118c7fa82552f3349aba3d749b7a72a9cea 100644 (file)
@@ -1144,8 +1144,13 @@ static void ultrastor_interrupt(int irq, void *dev_id, struct pt_regs *regs)
     else
        printk("US14F: interrupt: unexpected interrupt\n");
 
-    if (config.slot ? inb(config.icm_address - 1) : (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+    if (config.slot ? inb(config.icm_address - 1) :
+       (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+#if (ULTRASTOR_DEBUG & UD_MULTI_CMD)
       printk("Ux4F: multiple commands completed\n");
+#else
+      ;
+#endif
 
 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
     printk("USx4F: interrupt: returning\n");
index 8e3b0c73fa104f7404d42da2ce5e3f4b348c4d9c..7e27be87f1846f8e541623febdf4433c73c4bf87 100644 (file)
@@ -133,13 +133,7 @@ static int parse_options(char *options, struct iso9660_options * popt)
                          !strcmp(this_char,"uid") ||
                          !strcmp(this_char,"gid"))) {
                  char * vpnt = value;
-                 unsigned int ivalue;
-                 ivalue = 0;
-                 while(*vpnt){
-                   if(*vpnt <  '0' || *vpnt > '9') break;
-                   ivalue = ivalue * 10 + (*vpnt - '0');
-                   vpnt++;
-                 }
+                 unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
                  if (*vpnt) return 0;
                  switch(*this_char) {
                  case 'b':
@@ -498,7 +492,7 @@ int isofs_bmap(struct inode * inode,int block)
            if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
              {
 
-               printk("_isofs_bmap: block>= EOF(%d, %d)", block, 
+               printk("_isofs_bmap: block>= EOF(%d, %ld)", block, 
                       inode->i_size);
              }
            return 0;
index 3904b587fd299dd0fd9f81dd210b00a5194b118c..3778eeb48e54eac01333b55c69827869f6e03003 100644 (file)
@@ -400,10 +400,10 @@ smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info)
 
        SMB_INOP(dir)->nused += 1;
 
-       /* We have to link the new inode_info into the doubly linked
-          list of inode_infos to make a complete linear search
-          possible. */
-
+       /*
+        * We have to link the new inode_info into the doubly linked
+        * list of inode_infos to make a complete linear search possible.
+        */
        root = &(SMB_SERVER(dir)->root);
 
        new_inode_info->prev = root;
@@ -413,13 +413,15 @@ smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info)
 
        if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info))))
        {
-               new_inode_info->next->prev = new_inode_info->prev;
-               new_inode_info->prev->next = new_inode_info->next;
-               SMB_INOP(dir)->nused -= 1;
-
                printk("smb_iget: iget failed!");
+               /*
+                * If we blocked in iget(), another task may have referenced
+                * the info structure ... clean up with smb_free_inode_info.
+                */
+               smb_free_inode_info(new_inode_info);
                return NULL;
        }
+
        return inode;
 }
 
@@ -660,13 +662,18 @@ smb_lookup(struct inode *dir, const char *name, int len,
        {
                goto in_tree;
        }
+
+       if (new_inode_info == NULL)
+       {
+               iput(dir);
+               return -ENOMEM;
+       }
        new_inode_info->finfo = finfo;
 
        DPRINTK("attr: %x\n", finfo.attr);
 
        if ((*result = smb_iget(dir, new_inode_info)) == NULL)
        {
-               smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
                iput(dir);
                return -EACCES;
        }
@@ -719,7 +726,6 @@ smb_create(struct inode *dir, const char *name, int len, int mode,
 
        if ((*result = smb_iget(dir, new_inode_info)) == NULL)
        {
-               smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
                iput(dir);
                return error;
        }
index dccea0ed0659b184f84df99417dec66bfda5e359..8d772fb152ecee346ad632bed05ea92ec1993530 100644 (file)
@@ -59,7 +59,8 @@ smb_read_inode(struct inode *inode)
        {
                /* Ok, now we're in trouble. The inode info is not
                   there. What should we do now??? */
-               printk("smb_read_inode: inode info not found\n");
+               printk("smb_read_inode: inode %ld info not found\n",
+                       inode->i_ino);
                return;
        }
        inode_info->state = SMB_INODE_VALID;
@@ -92,24 +93,43 @@ smb_read_inode(struct inode *inode)
 static void
 smb_put_inode(struct inode *inode)
 {
-       struct smb_dirent *finfo = SMB_FINFO(inode);
        struct smb_server *server = SMB_SERVER(inode);
        struct smb_inode_info *info = SMB_INOP(inode);
+       struct smb_dirent *finfo;
+       __u32 mtime = inode->i_mtime;
+
+       if (inode->i_count > 1) {
+               printk("smb_put_inode: in use device %s, inode %ld count=%d\n",
+                       kdevname(inode->i_dev), inode->i_ino, inode->i_count);
+               return;
+       }
 
        if (S_ISDIR(inode->i_mode))
        {
                smb_invalid_dir_cache(inode->i_ino);
        }
-       if (finfo->opened != 0)
-       {
-               if (smb_proc_close(server, finfo->fileid, inode->i_mtime))
+       clear_inode(inode);
+
+       /*
+        * We don't want the inode to be reused as free if we block here,
+        * so temporarily increment i_count.
+        */
+       inode->i_count++;
+       if (info) {
+               finfo = &info->finfo;
+               if (finfo->opened != 0)
                {
-                       /* We can't do anything but complain. */
-                       DPRINTK("smb_put_inode: could not close\n");
+                       if (smb_proc_close(server, finfo->fileid, mtime))
+                       {
+                               /* We can't do anything but complain. */
+                               printk("smb_put_inode: could not close\n");
+                       }
                }
-       }
-       smb_free_inode_info(info);
-       clear_inode(inode);
+               smb_free_inode_info(info);
+       } else
+               printk("smb_put_inode: no inode info??\n");
+
+       inode->i_count--;
 }
 
 static void
index ed56be1afbb0f3489d6adbd27e29d624025e2a89..ef65be7ce2eecae92b1311a0eaabbd1db23cb8d3 100644 (file)
@@ -1685,6 +1685,7 @@ smb_proc_reconnect(struct smb_server *server)
        if (server->packet != NULL)
        {
                smb_vfree(server->packet);
+               server->packet = NULL;
                server->packet_size = 0;
        }
        server->packet = smb_vmalloc(max_xmit);
@@ -1920,6 +1921,7 @@ smb_proc_reconnect(struct smb_server *server)
 
        /* Now make a new packet with the correct size. */
        smb_vfree(server->packet);
+       server->packet = NULL;
 
        server->packet = smb_vmalloc(server->max_xmit);
        if (server->packet == NULL)
index 1c7587b4264b72e3427b9a13566cbbd7e62008e6..64ec6fec52fcf193b7648ec6f6744e59f1b14b84 100644 (file)
@@ -338,6 +338,8 @@ smb_receive(struct smb_server *server)
                DPRINTK("smb_receive: Increase packet size from %d to %d\n",
                        server->packet_size, len + 4);
                smb_vfree(server->packet);
+               server->packet = NULL;
+
                server->packet_size = 0;
                server->packet = smb_vmalloc(len + 4);
                if (server->packet == NULL)
index 522718965cef454a700e9ce7661b5854754ded8a..a15fa255249917c2db1d193cc08fb17a02d2f9e8 100644 (file)
@@ -33,7 +33,7 @@
  * SHMMAX <= (PAGE_SIZE << _SHM_IDX_BITS).
  */
 
-#define SHMMAX 0x1000000               /* max shared seg size (bytes) */
+#define SHMMAX 0x2000000               /* max shared seg size (bytes) */
 #define SHMMIN 1 /* really PAGE_SIZE */        /* min shared seg size (bytes) */
 #define SHMMNI (1<<_SHM_ID_BITS)       /* max num of segs system wide */
 #define SHMALL                         /* max shm system wide (pages) */ \
index 51b319891ca8ceddc42bd67158305486e2e5b59e..e05912b5df62d5fa9101949ef9538203dc9addbd 100644 (file)
@@ -68,6 +68,16 @@ asmlinkage int printk(const char * fmt, ...)
  */
 #define fsuser() (current->fsuid == 0)
 
+/*
+ *      Display an IP address in readable format. 
+ */
+  
+#define NIPQUAD(addr) \
+        (((addr) >> 0)  & 0xff), \
+        (((addr) >> 8)  & 0xff), \
+        (((addr) >> 16) & 0xff), \
+        (((addr) >> 24) & 0xff)
+
 #endif /* __KERNEL__ */
 
 #define SI_LOAD_SHIFT  16
index bb56a64f799f0219dabb9bca51761c179659eae4..67ff7d6f43865b3c05fbabd041f1c30f30ea08c5 100644 (file)
@@ -128,6 +128,7 @@ enum scsi_directory_inos {
        PROC_SCSI_QLOGICISP,
        PROC_SCSI_SEAGATE,
        PROC_SCSI_T128,
+       PROC_SCSI_DC390T,
        PROC_SCSI_NCR53C7xx,
        PROC_SCSI_NCR53C8XX,
        PROC_SCSI_ULTRASTOR,
index b0d63dbb4292844fcb8e826d1badb24566b6e3a0..81472a8640da52331d3a1eabede295d6fd8a0ade 100644 (file)
@@ -93,6 +93,7 @@ extern void gdth_setup(char *str, int *ints);
 extern void aic7xxx_setup(char *str, int *ints);
 extern void AM53C974_setup(char *str, int *ints);
 extern void BusLogic_Setup(char *str, int *ints);
+extern void ncr53c8xx_setup(char *str, int *ints);
 extern void eata2x_setup(char *str, int *ints);
 extern void u14_34f_setup(char *str, int *ints);
 extern void fdomain_setup(char *str, int *ints);
@@ -331,6 +332,9 @@ struct {
 #ifdef CONFIG_SCSI_BUSLOGIC
        { "BusLogic=", BusLogic_Setup},
 #endif
+#ifdef CONFIG_SCSI_NCR53C8XX
+       { "ncr53c8xx=", ncr53c8xx_setup},
+#endif
 #ifdef CONFIG_SCSI_EATA
        { "eata=", eata2x_setup},
 #endif
index 8ee973b5bc931a9af059db39173d7c4a133ca6bf..ff93009ebc0bf4fc58bb97eda89414e46af2ccce 100644 (file)
@@ -139,7 +139,7 @@ static ctl_table kern_table[] = {
 #ifdef CONFIG_ROOT_NFS
        {KERN_NFSRNAME, "nfs-root-name", nfs_root_name, NFS_ROOT_NAME_LEN,
         0644, NULL, &proc_dostring, &sysctl_string },
-       {KERN_NFSRNAME, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
+       {KERN_NFSRADDRS, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
         0644, NULL, &proc_dostring, &sysctl_string },
 #endif
 #ifdef CONFIG_BINFMT_JAVA
index 43e0a15ebeb4083746e3e302f36ce2f1569733d3..a7cb1b8a071733caabb3882e777bdb91a2f8b97e 100644 (file)
@@ -1920,9 +1920,9 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
        if (tip != dev->pa_addr && net_alias_has(skb->dev)) 
        {
                /*
-                *      net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
+                *      net_alias_dev_rx32 returns main dev if it fails to found other.
                 */
-               dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+               dev = net_alias_dev_rx32(dev, AF_INET, sip, tip);
 
                if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
                {
index 1ad978f1286d7e2fdce99090199ae1d30444a78d..cb22de9afafb2c1200d36daa3ebc665b94d36ab6 100644 (file)
@@ -749,7 +749,7 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi
                                break;
                }
                if(icmph->code>NR_ICMP_UNREACH) /* Invalid type */
-                       return;
+                       goto flush_it;
        }
        
        /*
index 5ade4be77807bbc7b687e5e3894afb7b8a13a7cf..1bd7275ae9fc2cacc5b4b3d9cb50a81106a72e3e 100644 (file)
@@ -297,13 +297,13 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 
        /*
         *      Try to select closest <src,dst> alias device, if any.
-        *      net_alias_dev_rcv_sel32 returns main device if it 
+        *      net_alias_dev_rx32 returns main device if it 
         *      fails to found other.
         */
 
 #ifdef CONFIG_NET_ALIAS
        if (iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) {
-               skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr);
+               skb->dev = dev = net_alias_dev_rx32(skb->dev, AF_INET, iph->saddr, iph->daddr);
        }
 #endif
 
index aca9aa5200e47781f1660d4a86ddeedbddf0c244..0f5402f765408c015866c2b8095dfadf316e1419 100644 (file)
@@ -618,7 +618,8 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
                        /* Only let this warning get printed once a minute. */
                        if (jiffies - warning_time > HZ*60) {
                                warning_time = jiffies;
-                               printk(KERN_INFO "Warning: possible SYN flooding on port %d. Sending cookies.\n", ntohs(th->dest));
+                               printk(KERN_INFO "Warning: possible SYN flood from %d.%d.%d.%d on %d.%d.%d.%d:%d.  Sending cookies.\n", 
+                                       NIPQUAD(saddr), NIPQUAD(daddr), ntohs(th->dest));
                        }
 #ifdef CONFIG_RST_COOKIES
                        tcp_send_synack_probe(daddr, saddr, th, &tcp_prot,
index 8ee8995daa502bfb1fa53066baf743247683689d..25b86279d24b81c9627fcbf82431313a973b0a21 100644 (file)
@@ -31,6 +31,7 @@
 // * Only resolves operands of jump and call instructions.
 
 #include <fstream.h>
+#include <strstream.h>
 #include <iomanip.h>
 #include <stdio.h>
 #include <string.h>
@@ -184,9 +185,23 @@ NameList::decode(unsigned char* code, long eip_addr)
     
     char buf[1024];
     int lines = 0;
+    int eip_seen = 0;
+    long offset;
     while (fgets(buf, sizeof(buf), objdump_FILE)) {
+       if (eip_seen && buf[4] == ':') {
+           // assume objdump from binutils 2.8..., reformat to old style
+           offset = strtol(buf, 0, 16);
+           char newbuf[sizeof(buf)];
+           memset(newbuf, '\0', sizeof(newbuf));
+           ostrstream ost(newbuf, sizeof(newbuf));
+           ost.width(8);
+           ost << offset;
+           ost << " <_EIP+" << offset << ">: " << &buf[6] << ends;
+           strcpy(buf, newbuf);
+       }
        if (!strnequ(&buf[9], "<_EIP", 5))
            continue;
+       eip_seen = 1;
        if (strstr(buf, " is out of bounds"))
            break;
        lines++;
@@ -195,19 +210,28 @@ NameList::decode(unsigned char* code, long eip_addr)
            cout << buf;
            continue;
        }
-       long offset = strtol(buf, 0, 16);
-       char* bp_0 = strchr(buf, '>') + 2;
+       offset = strtol(buf, 0, 16);
+       char* bp_0 = strchr(buf, '>');
        KSym* ksym = find(eip_addr + offset);
+       if (bp_0)
+           bp_0 += 2;
+       else
+           bp_0 = strchr(buf, ':');
        if (ksym)
            cout << *ksym << ' ';
-       char* bp = bp_0;
+       char *bp_1 = strstr(bp_0, "\t");        // objdump from binutils 2.8...
+       if (bp_1)
+           ++bp_1;
+       else
+           bp_1 = bp_0;
+       char *bp = bp_1;
        while (!isspace(*bp))
            bp++;
        while (isspace(*bp))
            bp++;
-       if (*bp != '0') {
+       if (!isxdigit(*bp)) {
            cout << bp_0;
-       } else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn
+       } else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn
            long rel_addr = strtol(bp, 0, 16);
            ksym = find(eip_addr + rel_addr);
            if (ksym) {