]> git.neil.brown.name Git - history.git/commitdiff
Import 1.3.59 1.3.59
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:31 +0000 (15:10 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:31 +0000 (15:10 -0500)
42 files changed:
Documentation/networking/vortex.txt [new file with mode: 0644]
Makefile
arch/alpha/kernel/Makefile
arch/i386/defconfig
drivers/block/ll_rw_blk.c
drivers/char/apm_bios.c
drivers/char/busmouse.c
drivers/char/selection.h
drivers/net/3c59x.c [new file with mode: 0644]
drivers/net/Config.in
drivers/net/Makefile
drivers/net/Space.c
drivers/net/ibmtr.c
drivers/scsi/AM53C974.c [new file with mode: 0644]
drivers/scsi/AM53C974.h [new file with mode: 0644]
drivers/scsi/Config.in
drivers/scsi/Makefile
drivers/scsi/README.AM53C974 [new file with mode: 0644]
drivers/scsi/aha152x.c
drivers/scsi/aha152x.h
drivers/scsi/hosts.c
drivers/sound/sb16_dsp.c
drivers/sound/sb_dsp.c
fs/buffer.c
fs/proc/procfs_syms.c
fs/proc/root.c
include/linux/blk.h
include/linux/fs.h
include/linux/locks.h
include/linux/mm.h
include/linux/proc_fs.h
include/linux/swap.h
init/main.c
kernel/ksyms.c
mm/filemap.c
mm/page_alloc.c
mm/page_io.c
mm/swap.c
mm/swapfile.c
mm/vmscan.c
scripts/Configure
scripts/depend.awk

diff --git a/Documentation/networking/vortex.txt b/Documentation/networking/vortex.txt
new file mode 100644 (file)
index 0000000..413aadf
--- /dev/null
@@ -0,0 +1,35 @@
+This document describes the usage and errata of the 3Com "Vortex" device
+driver for Linux.
+
+This driver supports the following hardware:
+       3c590, 3c592, 3c595, 3c597
+
+When loaded as a module the following variables may be set:
+ name   type   description
+ debug  int    The debug message level, 0 (no messages) to 6 (wordy).
+ options int[] The media type override and card operation settings
+               (See list below.)
+
+An example of loading the vortex module is
+       insmod 3c59x.o debug=1 options=0,,12
+This sets the debug message level to minimal messages, sets the first card to
+the 10baseT transceiver, the second to the EEPROM-set transceiver, and the
+third card to operate in full-duplex mode using its 100baseTx transceiver.
+(Note: card ordering is set by the PCI BIOS.)
+
+Possible media type settings
+       0       10baseT
+       1       10Mbs AUI
+       2       undefined
+       3       10base2 (BNC)
+       4       100base-TX
+       5       100base-FX
+       6       MII (not yet available)
+       7       <Use default setting>
+       
+       8       Full-duplex bit
+               8 10baseT full-duplex
+               12 100baseTx full-duplex 
+       16      Bus-master enable bit (experimental use only!)
+
+Details of the device driver implementation are at the top of the source file.
index 7ff17758f9543f7d2239b88f21657dfc4e1c5f9f..514743e6d405b8ba7528ca4cdf27d15411766d01 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 58
+SUBLEVEL = 59
 
 ARCH = i386
 
index 99fddc9ed430f57abee3c8ab34f055c002cdda6e..72170729cf92df3f6340c9832764fafedd3cee7c 100644 (file)
@@ -16,7 +16,7 @@ all: kernel.o head.o
 
 O_TARGET := kernel.o
 O_OBJS   := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \
-           bios32.o ptrace.o time.o apecs.o lca.o ksyms.c
+           bios32.o ptrace.o time.o apecs.o lca.o ksyms.o
 
 all: kernel.o head.o
 
index d227d2f7781802b15a36cba3833aa61a26b3860b..5a7aed016148a6b6c93e4193074df833fcdb727f 100644 (file)
@@ -6,7 +6,7 @@
 # Loadable module support
 #
 CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
+# CONFIG_MODVERSIONS is not set
 # CONFIG_KERNELD is not set
 
 #
@@ -88,6 +88,7 @@ CONFIG_NET_VENDOR_3COM=y
 # CONFIG_EL1 is not set
 # CONFIG_EL2 is not set
 CONFIG_EL3=y
+# CONFIG_VORTEX is not set
 # CONFIG_NET_ISA is not set
 # CONFIG_NET_EISA is not set
 # CONFIG_NET_POCKET is not set
index ae396ded64e62bc6b96edd609a513a5ee8ed8653..576fcb43cf259d93c654c58a5ff97ac2fe4bf97e 100644 (file)
@@ -436,16 +436,19 @@ static void make_request(int major,int rw, struct buffer_head * bh)
        add_request(major+blk_dev,req);
 }
 
+/*
+ * Swap partitions are now read via brw_page.  ll_rw_page is an
+ * asynchronous function now --- we must call wait_on_page afterwards
+ * if synchronous IO is required.  
+ */
 void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer)
 {
-       struct request * req;
        unsigned int major = MAJOR(dev);
-       unsigned long sector = page * (PAGE_SIZE / 512);
-       struct semaphore sem = MUTEX_LOCKED;
-
+       int block = page;
+       
        if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
                printk("Trying to read nonexistent block-device %s (%ld)\n",
-                      kdevname(dev), sector);
+                      kdevname(dev), page);
                return;
        }
        switch (rw) {
@@ -461,19 +464,10 @@ void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer)
                default:
                        panic("ll_rw_page: bad block dev cmd, must be R/W");
        }
-       req = get_request_wait(NR_REQUEST, dev);
-/* fill up the request-info, and add it to the queue */
-       req->cmd = rw;
-       req->errors = 0;
-       req->sector = sector;
-       req->nr_sectors = PAGE_SIZE / 512;
-       req->current_nr_sectors = PAGE_SIZE / 512;
-       req->buffer = buffer;
-       req->sem = &sem;
-       req->bh = NULL;
-       req->next = NULL;
-       add_request(major+blk_dev,req);
-       down(&sem);
+       if (mem_map[MAP_NR(buffer)].locked)
+               panic ("ll_rw_page: page already locked");
+       mem_map[MAP_NR(buffer)].locked = 1;
+       brw_page(rw, (unsigned long) buffer, dev, &block, PAGE_SIZE, 0);
 }
 
 /* This function can be used to request a number of buffers from a block
index 5e24d1bc3c602ce954caaba2aac99ae5cf76d325..92bd21d459b2a223d4c7ef78ab2925d1d5e660cb 100644 (file)
  * January 1996, Rik Faith (faith@cs.unc.edu):
  *    Make /proc/apm easy to format (bump driver version)
  *
+ * History:
+ *    0.6b: first version in official kernel, Linux 1.3.46
+ *    0.7: changed /proc/apm format, Linux 1.3.58
+ *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
  *
  * Reference:
  *
@@ -110,29 +114,40 @@ extern unsigned long get_cmos_time(void);
  */
 
 /*
- * define to have debug messages
+ * Define to have debug messages.
  */
 #undef APM_DEBUG
 
 /*
- * define to always call the APM BIOS busy routine even if the clock was
- * not slowed by the idle routine
+ * Define to always call the APM BIOS busy routine even if the clock was
+ * not slowed by the idle routine.
  */
 #define ALWAYS_CALL_BUSY
 
 /*
- * define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
- * should turn interrupts on before it does a 'hlt')
+ * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
+ * should turn interrupts on before it does a 'hlt').
  */
 #define APM_NOINTS
 
 /*
- * define to make the APM BIOS calls zero all data segment registers (do
- * that an incorrect BIOS implementation will cause a kernel panic if it
- * tries to write to arbitrary memory)
+ * Define to make the APM BIOS calls zero all data segment registers (do
+ * that if an incorrect BIOS implementation will cause a kernel panic if it
+ * tries to write to arbitrary memory).
  */
 #define APM_ZERO_SEGS
 
+/*
+ * Define to make all set_limit calls use 64k limits.  The APM 1.1 BIOS is
+ * supposed to provide limit information that it recognizes.  Many machines
+ * do this correctly, but many others do not restrict themselves to their
+ * claimed limit.  When this happens, they will cause a segmentation
+ * violation in the kernel at boot time.  Most BIOS's, however, will
+ * respect a 64k limit, so we use that.  If you want to be pedantic and
+ * hold your BIOS to its claims, then undefine this.
+ */
+#define APM_RELAX_SEGMENTS
+
 /*
  * Need to poll the APM BIOS every second
  */
@@ -193,27 +208,27 @@ extern unsigned long get_cmos_time(void);
 #define APM_SET_CPU_IDLE(error) \
        APM_BIOS_CALL(al) \
        : "=a" (error) \
-       : "0" (0x5305) \
+       : "a" (0x5305) \
        APM_BIOS_CALL_END
 #endif
 
 #define APM_SET_CPU_BUSY(error) \
        APM_BIOS_CALL(al) \
        : "=a" (error) \
-       : "0" (0x5306) \
+       : "a" (0x5306) \
        APM_BIOS_CALL_END
 
 #define APM_SET_POWER_STATE(state, error) \
        APM_BIOS_CALL(al) \
        : "=a" (error) \
-       : "0" (0x5307), "b" (0x0001), "c" (state) \
+       : "a" (0x5307), "b" (0x0001), "c" (state) \
        APM_BIOS_CALL_END
 
 #ifdef CONFIG_APM_DISPLAY_BLANK
 #define APM_SET_DISPLAY_POWER_STATE(state, error) \
        APM_BIOS_CALL(al) \
        : "=a" (error) \
-       : "0" (0x5307), "b" (0x01ff), "c" (state) \
+       : "a" (0x5307), "b" (0x01ff), "c" (state) \
        APM_BIOS_CALL_END
 #endif
 
@@ -221,32 +236,32 @@ extern unsigned long get_cmos_time(void);
 #define APM_ENABLE_POWER_MANAGEMENT(device, error) \
        APM_BIOS_CALL(al) \
        : "=a" (error) \
-       : "0" (0x5308), "b" (device), "c" (1) \
+       : "a" (0x5308), "b" (device), "c" (1) \
        APM_BIOS_CALL_END
 #endif
 
 #define APM_GET_POWER_STATUS(bx, cx, dx, error) \
        APM_BIOS_CALL(al) \
        : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
-       : "0" (0x530a), "1" (1) \
+       : "a" (0x530a), "b" (1) \
        APM_BIOS_CALL_END
 
 #define APM_GET_EVENT(event, error)    \
        APM_BIOS_CALL(al) \
        : "=a" (error), "=b" (event) \
-       : "0" (0x530b) \
+       : "a" (0x530b) \
        APM_BIOS_CALL_END
 
 #define APM_DRIVER_VERSION(ver, ax, error) \
        APM_BIOS_CALL(bl) \
        : "=a" (ax), "=b" (error) \
-       : "0" (0x530e), "1" (0), "c" (ver) \
+       : "a" (0x530e), "b" (0), "c" (ver) \
        APM_BIOS_CALL_END
 
 #define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
        APM_BIOS_CALL(al) \
        : "=a" (error) \
-       : "0" (0x530f), "b" (device), "c" (1) \
+       : "a" (0x530f), "b" (device), "c" (1) \
        APM_BIOS_CALL_END
 
 /*
@@ -292,7 +307,7 @@ static struct apm_bios_struct *     user_list = NULL;
 
 static struct timer_list       apm_timer;
 
-static char                    driver_version[] = "0.7";/* no spaces */
+static char                    driver_version[] = "0.8";/* no spaces */
 
 #ifdef APM_DEBUG
 static char *  apm_event_name[] = {
@@ -1025,13 +1040,18 @@ static int apm_setup(void)
                set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
                set_limit(gdt[APM_DS >> 3], 64 * 1024);
        } else {
-               set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
-               /* This is not clear from the spec, but at least one
-                  machine needs CS_16 to be a 64k segment, and the DEC
-                  Hinote Ultra CT475 (and others?) needs DS to be a 64k
-                  segment. */
+#ifdef APM_RELAX_SEGMENTS
+               /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
+               set_limit(gdt[APM_CS >> 3], 64 * 1024);
+               /* For some unknown machine. */
                set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+               /* For the DEC Hinote Ultra CT475 (and others?) */
                set_limit(gdt[APM_DS >> 3], 64 * 1024);
+#else
+               set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
+               set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+               set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
+#endif
                apm_bios_info.version = 0x0101;
                error = apm_driver_version(&apm_bios_info.version);
                if (error != 0)
index 8ebc156b43937ad9f8e56bd6e6eaea5d42ae1fd9..c509dfe4f628a9407c3e590d16ca646c320f1a3f 100644 (file)
@@ -65,7 +65,6 @@ static void mouse_interrupt(int irq, struct pt_regs *regs)
        char dx, dy;
        unsigned char buttons;
 
-       MSE_INT_OFF();
        outb(MSE_READ_X_LOW, MSE_CONTROL_PORT);
        dx = (inb(MSE_DATA_PORT) & 0xf);
        outb(MSE_READ_X_HIGH, MSE_CONTROL_PORT);
@@ -171,6 +170,7 @@ static int read_mouse(struct inode * inode, struct file * file, char * buffer, i
        int dx;
        int dy;
        unsigned char buttons; 
+       /* long flags; */
 
        if (count < 3)
                return -EINVAL;
@@ -186,7 +186,8 @@ static int read_mouse(struct inode * inode, struct file * file, char * buffer, i
         * so paging in put_user() does not effect mouse tracking.
         */
 
-       MSE_INT_OFF();
+       /* save_flags(flags); cli(); */
+       disable_irq(mouse_irq);
        dx = mouse.dx;
        dy = mouse.dy;
        if (dx < -127)
@@ -201,7 +202,8 @@ static int read_mouse(struct inode * inode, struct file * file, char * buffer, i
        mouse.dx -= dx;
        mouse.dy -= dy;
        mouse.ready = 0;
-       MSE_INT_ON();
+       enable_irq(mouse_irq);
+       /* restore_flags(flags); */
 
        put_user(buttons | 0x80, buffer);
        put_user((char)dx, buffer + 1);
index 9220933c2428fff35c1d2e7f4e3e9dfdc3c4128b..4dc9598d18113a7ec28bd595ce97f3ba19d6f98e 100644 (file)
@@ -77,12 +77,9 @@ extern unsigned long video_mem_term;
  * of the VC's backing store, or the "shadow screen" memory where the screen
  * contents are kept, as the TGA frame buffer is *not* char/attr cells.
  *
- * The "(unsigned long) addr < video_mem_term" tests for an Alpha kernel
- *  virtual address less than the end of the "shadow scrren" memory. This
- *  indicates we really want to write to the screen, so, we do... :-)
- *
- * NOTE: we must guarantee that video_mem_term is less than *any* VC's backing
- * store; to do that, we must allocate it earlier than any VC's are done.
+ * We must test for an Alpha kernel virtual address that falls within
+ *  the "shadow screen" memory. This condition indicates we really want 
+ *  to write to the screen, so, we do... :-)
  *
  * NOTE also: there's only *TWO* operations: to put/get a character/attribute.
  *  All the others needed by VGA support go away, as Not Applicable for TGA.
@@ -94,7 +91,8 @@ static inline void scr_writew(unsigned short val, unsigned short * addr)
         * if so, then render the char/attr onto the real screen.
         */
         *addr = val;
-        if ((unsigned long)addr < video_mem_term) {
+        if ((unsigned long)addr < video_mem_term &&
+           (unsigned long)addr >= video_mem_base) {
                 tga_blitc(val, (unsigned long) addr);
         }
 }
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
new file mode 100644 (file)
index 0000000..fde6012
--- /dev/null
@@ -0,0 +1,1033 @@
+/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */
+/*
+       Written 1995 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 3Com "Vortex" series ethercards.  Members of
+       the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
+       EtherLink.  It also works with the 10Mbs-only 3c590 PCI EtherLink III.
+
+       The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+       Center of Excellence in Space Data and Information Sciences
+          Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static char *version = "3c59x.c:v0.11 1/21/96 becker@cesdis.gsfc.nasa.gov\n";
+
+/* "Knobs" that turn on special features. */
+/* Use bus master transfers instead of programmed-I/O for the Tx process.
+   This is disabled by default! */
+#define VORTEX_BUS_MASTER
+
+/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
+#define VORTEX_DEBUG 1
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/timer.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* The total size is twice that of the original EtherLinkIII series: the
+   runtime register window, window 1, is now always mapped in. */
+#define VORTEX_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry tc59x_drv =
+{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef VORTEX_DEBUG
+int vortex_debug = VORTEX_DEBUG;
+#else
+int vortex_debug = 1;
+#endif
+
+static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0};
+static const char *product_names[] = {
+       "3c590 Vortex 10Mbps",
+       "3c595 Vortex 100baseTX",
+       "3c595 Vortex 100baseT4",
+       "3c595 Vortex 100base-MII",
+       "EISA Vortex 3c597",
+};
+#define DEMON_INDEX 5                  /* Caution!  Must be consistent with above! */
+
+/*
+                               Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to
+10/100baseT adapter.  It also works with the 3c590, a similar product
+with only a 10Mbs interface.
+
+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
+physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't
+support it.
+
+III. Driver operation
+
+The 3c59x series use an interface that's very similar to the previous 3c5x9
+series.  The primary interface is two programmed-I/O FIFOs, with an
+alternate single-contiguous-region bus-master transfer (see next).
+
+One extension that is advertised in a very large font is that the adapters
+are capable of being bus masters.  Unfortunately this capability is only for
+a single contiguous region making it less useful than the list of transfer
+regions available with the DEC Tulip or AMD PCnet.  Given the significant
+performance impact of taking an extra interrupt for each transfer, using
+DMA transfers is a win only with large blocks.
+
+IIIC. 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.
+
+IV. Notes
+
+Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
+3c590 and 3c595 boards.
+The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
+the not-yet-released (3/95) EISA version is called "Demon".  According to
+Terry these names come from rides at the local amusement park.
+
+The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes!
+This driver only supports ethernet packets because of the skbuff allocation
+limit of 4K.
+*/
+
+#define TCOM_VENDOR_ID 0x10B7          /* 3Com's manufacturer's ID. */
+
+/* Operational defintions.
+   These are not used by other compilation units and thus are not
+   exported in a ".h" file.
+
+   First the windows.  There are eight register windows, with the command
+   and status registers available in each.
+   */
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+/* The top five bits written to EL3_CMD are a command, the lower
+   11 bits are the parameter, if applicable.
+   Note that 11 parameters bits was fine for ethernet, but the new chip
+   can handle FDDI lenght frames (~4500 octets) and now parameters count
+   32-bit 'Dwords' rather than octets. */
+
+enum vortex_cmd {
+       TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+       RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+       TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+       FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+       SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+       SetTxThreshold = 18<<11, SetTxStart = 19<<11,
+       StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11,
+       StatsDisable = 22<<11, StopCoax = 23<<11,};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+       RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
+
+/* Bits in the general status register. */
+enum vortex_status {
+       IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+       TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+       IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8,
+       DMAInProgress = 1<<11,                  /* DMA controller is still busy.*/
+       CmdInProgress = 1<<12,                  /* EL3_CMD is still busy.*/
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+   On the Vortex this window is always mapped at offsets 0x10-0x1f. */
+enum Window1 {
+       TX_FIFO = 0x10,  RX_FIFO = 0x10,  RxErrors = 0x14,
+       RxStatus = 0x18,  Timer=0x1A, TxStatus = 0x1B,
+       TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
+};
+enum Window0 {
+       Wn0EepromCmd = 10,              /* Window 0: EEPROM command register. */
+};
+enum Win0_EEPROM_bits {
+       EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
+       EEPROM_EWENB = 0x30,            /* Enable erasing/writing for 10 msec. */
+       EEPROM_EWDIS = 0x00,            /* Disable EWENB before 10 msec timeout. */
+};
+/* EEPROM locations. */
+enum eeprom_offset {
+       PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3,
+       EtherLink3ID=7, IFXcvrIO=8, IRQLine=9,
+       NodeAddr01=10, NodeAddr23=11, NodeAddr45=12,
+       DriverTune=13, Checksum=15};
+
+enum Window3 {                 /* Window 3: MAC/config bits. */
+       Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+union wn3_config {
+       int i;
+       struct w3_config_fields {
+               unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
+               int pad8:8;
+               unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
+               int pad24:8;
+       } u;
+};
+
+enum Window4 {
+       Wn4_Media = 0x0A,               /* Window 4: Various transcvr/media bits. */
+};
+enum Win4_Media_bits {
+       Media_TP = 0x00C0,              /* Enable link beat and jabber for 10baseT. */
+};
+enum Window7 {                                 /* Window 7: Bus Master control. */
+       Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
+};
+
+struct vortex_private {
+       char devname[8];                        /* "ethN" string, also for kernel debug. */
+       const char *product_name;
+       struct device *next_module;
+       struct enet_statistics stats;
+#ifdef VORTEX_BUS_MASTER
+       struct sk_buff *tx_skb;         /* Packet being eaten by bus master ctrl.  */
+#endif
+       struct timer_list timer;        /* Media selection timer. */
+       int options;                            /* User-settable driver options (none yet). */
+       unsigned int media_override:3, full_duplex:1, bus_master:1, autoselect:1;
+};
+
+static char *if_names[] = {
+       "10baseT", "10Mbs AUI", "undefined", "10base2",
+       "100baseTX", "100baseFX", "MII", "undefined"};
+
+static int vortex_scan(struct device *dev);
+static int vortex_found_device(struct device *dev, int ioaddr, int irq,
+                                                          int product_index, int options);
+static int vortex_probe1(struct device *dev);
+static int vortex_open(struct device *dev);
+static void vortex_timer(unsigned long arg);
+static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
+static int vortex_rx(struct device *dev);
+static void vortex_interrupt(int irq, struct pt_regs *regs);
+static int vortex_close(struct device *dev);
+static void update_stats(int addr, struct device *dev);
+static struct enet_statistics *vortex_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+\f
+
+/* Unlike the other PCI cards the 59x cards don't need a large contiguous
+   memory region, so making the driver a loadable module is feasible.
+
+   Unfortuneately maximizing the shared code between the integrated and
+   module version of the driver results in a complicated set of initialization
+   procedures.
+   init_module() -- modules /  tc59x_init()  -- built-in
+               The wrappers for vortex_scan()
+   vortex_scan()                The common routine that scans for PCI and EISA cards
+   vortex_found_device() Allocate a device structure when we find a card.
+                                       Different versions exist for modules and built-in.
+   vortex_probe1()             Fill in the device structure -- this is seperated
+                                       so that the modules code can put it in dev->init.
+*/
+/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+/* Note: this is the only limit on the number of cards supported!! */
+int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+
+#ifdef MODULE
+static int debug = -1;
+/* A list of all installed Vortex devices, for removing the driver module. */
+static struct device *root_vortex_dev = NULL;
+
+int
+init_module(void)
+{
+       int cards_found;
+
+       if (debug >= 0)
+               vortex_debug = debug;
+       if (vortex_debug)
+               printk(version);
+
+       root_vortex_dev = NULL;
+       cards_found = vortex_scan(0);
+       return cards_found < 0 ? cards_found : 0;
+}
+
+#else
+unsigned long tc59x_probe(struct device *dev)
+{
+       int cards_found = 0;
+
+       cards_found = vortex_scan(dev);
+
+       if (vortex_debug > 0  &&  cards_found)
+               printk(version);
+
+       return cards_found ? 0 : -ENODEV;
+}
+#endif  /* not MODULE */
+
+static int vortex_scan(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;
+                       unsigned int pci_ioaddr;
+                       unsigned short pci_command;
+                       int index;
+
+                       for (index = 0; product_ids[index]; index++) {
+                               if ( ! pcibios_find_device(TCOM_VENDOR_ID, product_ids[index],
+                                                                                  pci_index, &pci_bus,
+                                                                                  &pci_device_fn))
+                                       break;
+                       }
+                       if ( ! product_ids[index])
+                               break;
+
+                       pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                                        PCI_INTERRUPT_LINE, &pci_irq_line);
+                       pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                                                         PCI_BASE_ADDRESS_0, &pci_ioaddr);
+                       /* Remove I/O space marker in bit 0. */
+                       pci_ioaddr &= ~3;
+
+#ifdef VORTEX_BUS_MASTER
+                       /* Get and check the bus-master and latency values.
+                          Some PCI BIOSes fail to set the master-enable bit, and
+                          the latency timer must be set to the maximum value to avoid
+                          data corruption that occurs when the timer expires during
+                          a transfer.  Yes, it's a bug. */
+                       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 != 255) {
+                               printk("  Overriding PCI latency timer (CFLT) setting of %d, new value is 255.\n", pci_latency);
+                               pcibios_write_config_byte(pci_bus, pci_device_fn,
+                                                                                 PCI_LATENCY_TIMER, 255);
+                       }
+#endif  /* VORTEX_BUS_MASTER */
+                       vortex_found_device(dev, pci_ioaddr, pci_irq_line,
+                                                          index, options[cards_found]);
+                       dev = 0;
+                       cards_found++;
+               }
+       }
+
+       /* Now check all slots of the EISA bus. */
+       if (EISA_bus) {
+               static int ioaddr = 0x1000;
+               for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+                       /* Check the standard EISA ID register for an encoded '3Com'. */
+                       if (inw(ioaddr + 0xC80) != 0x6d50)
+                               continue;
+                       /* Check for a product that we support. */
+                       if ((inw(ioaddr + 0xC82) & 0xFFF0) != 0x5970
+                               && (inw(ioaddr + 0xC82) & 0xFFF0) != 0x5920)
+                               continue;
+                       vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
+                                                          DEMON_INDEX, options[cards_found]);
+                       dev = 0;
+                       cards_found++;
+               }
+       }
+
+       return cards_found;
+}
+
+static int vortex_found_device(struct device *dev, int ioaddr, int irq,
+                                                          int product_index, int options)
+{
+       struct vortex_private *vp;
+
+#ifdef MODULE
+       /* Allocate and fill new device structure. */
+       int dev_size = sizeof(struct device) +
+               sizeof(struct vortex_private);
+       
+       dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
+       memset(dev, 0, dev_size);
+       dev->priv = ((void *)dev) + sizeof(struct device);
+       vp = (struct vortex_private *)dev->priv;
+       dev->name = vp->devname; /* An empty string. */
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+       dev->init = vortex_probe1;
+       vp->product_name = product_names[product_index];
+       vp->options = options;
+       if (options >= 0) {
+               vp->media_override = options & 7;
+               vp->full_duplex = (options & 8) ? 1 : 0;
+               vp->bus_master = (options & 16) ? 1 : 0;
+       } else {
+               vp->media_override = 7;
+               vp->full_duplex = 0;
+               vp->bus_master = 0;
+       }
+       ether_setup(dev);
+       vp->next_module = root_vortex_dev;
+       root_vortex_dev = dev;
+       if (register_netdev(dev) != 0)
+               return -EIO;
+#else  /* not a MODULE */
+       if (dev) {
+               dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL);
+               memset(dev->priv, 0, sizeof (struct vortex_private));
+       }
+       dev = init_etherdev(dev, sizeof(struct vortex_private));
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+       vp  = (struct vortex_private *)dev->priv;
+       vp->product_name = product_names[product_index];
+       vp->options = options;
+       vortex_probe1(dev);
+#endif /* MODULE */
+       return 0;
+}
+
+static int vortex_probe1(struct device *dev)
+{
+       int ioaddr = dev->base_addr;
+       struct vortex_private *vp = (struct vortex_private *)dev->priv;
+       int i;
+
+       printk("%s: 3Com %s at %#3x,", dev->name,
+                  vp->product_name, ioaddr);
+
+       /* Read the station address from the EEPROM. */
+       EL3WINDOW(0);
+       for (i = 0; i < 3; i++) {
+               short *phys_addr = (short *)dev->dev_addr;
+               int timer;
+               outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
+               /* Pause for at least 162 us. for the read to take place. */
+               for (timer = 0; timer < 162*4 + 400; timer++) {
+                       SLOW_DOWN_IO;
+                       if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
+                               break;
+               }
+               phys_addr[i] = htons(inw(ioaddr + 12));
+       }
+       for (i = 0; i < 6; i++)
+               printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
+       printk(", IRQ %d\n", dev->irq);
+       /* Tell them about an invalid IRQ. */
+       if (vortex_debug && (dev->irq <= 0 || dev->irq > 15))
+               printk(" *** Warning: this IRQ is unlikely to work!\n");
+
+       {
+               char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"};
+               union wn3_config config;
+               EL3WINDOW(3);
+               config.i = inl(ioaddr + Wn3_Config);
+               if (vortex_debug > 1)
+                       printk("  Internal config register is %4.4x, transceivers %#x.\n",
+                                  config.i, inw(ioaddr + Wn3_Options));
+               printk("  %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+                          8 << config.u.ram_size,
+                          config.u.ram_width ? "word" : "byte",
+                          ram_split[config.u.ram_split],
+                          config.u.autoselect ? "autoselect/" : "",
+                          if_names[config.u.xcvr]);
+               dev->if_port = config.u.xcvr;
+               vp->autoselect = config.u.autoselect;
+       }
+
+       /* We do a request_region() only to register /proc/ioports info. */
+       request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
+
+       /* The 3c59x-specific entries in the device structure. */
+       dev->open = &vortex_open;
+       dev->hard_start_xmit = &vortex_start_xmit;
+       dev->stop = &vortex_close;
+       dev->get_stats = &vortex_get_stats;
+       dev->set_multicast_list = &set_multicast_list;
+#if defined (HAVE_SET_MAC_ADDR) && 0
+       dev->set_mac_address = &set_mac_address;
+#endif
+
+       return 0;
+}
+
+\f
+static int
+vortex_open(struct device *dev)
+{
+       int ioaddr = dev->base_addr;
+       struct vortex_private *vp = (struct vortex_private *)dev->priv;
+       union wn3_config config;
+       int i;
+
+       /* Before initializing select the active media port. */
+       EL3WINDOW(3);
+       if (vp->full_duplex)
+               outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
+       config.i = inl(ioaddr + Wn3_Config);
+
+       if (vp->media_override != 7) {
+               if (vortex_debug > 1)
+                       printk("%s: Media override to transceiver %d (%s).\n",
+                                  dev->name, vp->media_override, if_names[vp->media_override]);
+               config.u.xcvr = vp->media_override;
+               dev->if_port = vp->media_override;
+               outl(config.i, ioaddr + Wn3_Config);
+       }
+
+       if (vortex_debug > 1) {
+               printk("%s: vortex_open() InternalConfig %8.8x.\n",
+                       dev->name, config.i);
+       }
+
+       outw(TxReset, ioaddr + EL3_CMD);
+       for (i = 20; i >= 0 ; i--)
+               if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+                       break;
+
+       outw(RxReset, ioaddr + EL3_CMD);
+       /* Wait a few ticks for the RxReset command to complete. */
+       for (i = 20; i >= 0 ; i--)
+               if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+                       break;
+
+       outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+       if (irq2dev_map[dev->irq] != NULL
+               || (irq2dev_map[dev->irq] = dev) == NULL
+               || dev->irq == 0
+               || request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) {
+               return -EAGAIN;
+       }
+
+       if (vortex_debug > 1) {
+               EL3WINDOW(4);
+               printk("%s: vortex_open() irq %d media status %4.4x.\n",
+                          dev->name, dev->irq, inw(ioaddr + Wn4_Media));
+       }
+
+       /* Set the station address and mask in window 2 each time opened. */
+       EL3WINDOW(2);
+       for (i = 0; i < 6; i++)
+               outb(dev->dev_addr[i], ioaddr + i);
+       for (; i < 12; i+=2)
+               outw(0, ioaddr + i);
+
+       if (dev->if_port == 3)
+               /* Start the thinnet transceiver. We should really wait 50ms...*/
+               outw(StartCoax, ioaddr + EL3_CMD);
+       else if (dev->if_port == 0) {
+               /* 10baseT interface, enabled link beat and jabber check. */
+               EL3WINDOW(4);
+               outw(inw(ioaddr + Wn4_Media) | Media_TP, ioaddr + Wn4_Media);
+       }
+
+       /* Switch to the stats window, and clear all stats by reading. */
+       outw(StatsDisable, ioaddr + EL3_CMD);
+       EL3WINDOW(6);
+       for (i = 0; i < 10; i++)        
+               inb(ioaddr + i);
+       inw(ioaddr + 10);
+       inw(ioaddr + 12);
+       /* New: On the Vortex we must also clear the BadSSD counter. */
+       EL3WINDOW(3);
+       inb(ioaddr + 12);
+
+       /* Switch to register set 7 for normal use. */
+       EL3WINDOW(7);
+
+       /* Accept b-case and phys addr only. */
+       outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+       outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+       dev->tbusy = 0;
+       dev->interrupt = 0;
+       dev->start = 1;
+
+       outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+       outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+       /* Allow status bits to be seen. */
+       outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+       /* Ack all pending events, and set active indicator mask. */
+       outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+                ioaddr + EL3_CMD);
+       outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+                | DMADone, ioaddr + EL3_CMD);
+
+#ifdef MODULE
+       MOD_INC_USE_COUNT;
+#endif
+
+       if (vp->autoselect) {
+               init_timer(&vp->timer);
+               vp->timer.expires = (14*HZ)/10;                         /* 1.4 sec. */
+               vp->timer.data = (unsigned long)dev;
+               vp->timer.function = &vortex_timer;    /* timer handler */
+               add_timer(&vp->timer);
+       }
+       return 0;
+}
+
+static void vortex_timer(unsigned long data)
+{
+       struct device *dev = (struct device *)data;
+       if (vortex_debug > 2)
+               printk("%s: Media selection timer tick happened.\n", dev->name);
+       /* ToDo: active media selection here! */
+}
+
+static int
+vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+       struct vortex_private *vp = (struct vortex_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+
+       /* Transmitter timeout, serious problems. */
+       if (dev->tbusy) {
+               int tickssofar = jiffies - dev->trans_start;
+               if (tickssofar < 40)
+                       return 1;
+               printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+                          dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
+               vp->stats.tx_errors++;
+               /* Issue TX_RESET and TX_START commands. */
+               outw(TxReset, ioaddr + EL3_CMD);
+               {
+                       int i;
+                       for (i = 20; i >= 0 ; i--)
+                               if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+                                       break;
+               }
+               outw(TxEnable, ioaddr + EL3_CMD);
+               dev->trans_start = jiffies;
+               dev->tbusy = 0;
+               return 0;
+       }
+
+       if (skb == NULL || skb->len <= 0) {
+               printk("%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) {
+               printk("%s: Transmitter access conflict.\n", dev->name);
+               return 1;
+       }
+
+       /* Put out the doubleword header... */
+       outl(skb->len, ioaddr + TX_FIFO);
+#ifdef VORTEX_BUS_MASTER
+       if (vp->bus_master) {
+               /* Set the bus-master controller to transfer the packet. */
+               outl((int)(skb->data), ioaddr + Wn7_MasterAddr);
+               outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
+               vp->tx_skb = skb;
+               outw(StartDMADown, ioaddr + EL3_CMD);
+       } else {
+               /* ... and the packet rounded to a doubleword. */
+               outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+               dev_kfree_skb (skb, FREE_WRITE);
+               if (inw(ioaddr + TxFree) > 1536) {
+                       dev->tbusy = 0;
+               } else
+                       /* Interrupt us when the FIFO has room for max-sized packet. */
+                       outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+       }
+#else
+       /* ... and the packet rounded to a doubleword. */
+       outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+       dev_kfree_skb (skb, FREE_WRITE);
+       if (inw(ioaddr + TxFree) > 1536) {
+               dev->tbusy = 0;
+       } else
+               /* Interrupt us when the FIFO has room for max-sized packet. */
+               outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+#endif  /* bus master */
+
+       dev->trans_start = jiffies;
+
+       /* Clear the Tx status stack. */
+       {
+               short tx_status;
+               int i = 4;
+
+               while (--i > 0  &&      (tx_status = inb(ioaddr + TxStatus)) > 0) {
+                       if (tx_status & 0x3C) {         /* A Tx-disabling error occured.  */
+                               if (vortex_debug > 2)
+                                 printk("%s: Tx error, status %2.2x.\n",
+                                                dev->name, tx_status);
+                               if (tx_status & 0x04) vp->stats.tx_fifo_errors++;
+                               if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
+                               if (tx_status & 0x30) {
+                                       int j;
+                                       outw(TxReset, ioaddr + EL3_CMD);
+                                       for (j = 20; j >= 0 ; j--)
+                                               if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+                                                       break;
+                               }
+                               outw(TxEnable, ioaddr + EL3_CMD);
+                       }
+                       outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+               }
+       }
+       return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void vortex_interrupt(int irq, struct pt_regs *regs)
+{
+       struct device *dev = (struct device *)(irq2dev_map[irq]);
+       struct vortex_private *lp;
+       int ioaddr, status;
+       int latency;
+       int i = 0;
+
+       if (dev == NULL) {
+               printk ("vortex_interrupt(): irq %d for unknown device.\n", irq);
+               return;
+       }
+
+       if (dev->interrupt)
+               printk("%s: Re-entering the interrupt handler.\n", dev->name);
+       dev->interrupt = 1;
+
+       ioaddr = dev->base_addr;
+       latency = inb(ioaddr + Timer);
+       lp = (struct vortex_private *)dev->priv;
+
+       status = inw(ioaddr + EL3_STATUS);
+
+       if (vortex_debug > 4)
+               printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name,
+                          status, latency);
+       if ((status & 0xE000) != 0xE000) {
+               static int donedidthis=0;
+               /* Some interrupt controllers store a bogus interrupt from boot-time.
+                  Ignore a single early interrupt, but don't hang the machine for
+                  other interrupt problems. */
+               if (donedidthis++ > 1) {
+                       printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
+                                  dev->name, status, dev->start);
+                       free_irq(dev->irq);
+               }
+       }
+
+       do {
+               if (vortex_debug > 5)
+                               printk("%s: In interrupt loop, status %4.4x.\n",
+                                          dev->name, status);
+               if (status & RxComplete)
+                       vortex_rx(dev);
+
+               if (status & TxAvailable) {
+                       if (vortex_debug > 5)
+                               printk("        TX room bit was handled.\n");
+                       /* There's room in the FIFO for a full-sized packet. */
+                       outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+                       dev->tbusy = 0;
+                       mark_bh(NET_BH);
+               }
+#ifdef VORTEX_BUS_MASTER
+               if (status & DMADone) {
+                       outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
+                       dev->tbusy = 0;
+                       mark_bh(NET_BH);
+               }
+#endif
+               if (status & (AdapterFailure | RxEarly | StatsFull)) {
+                       /* Handle all uncommon interrupts at once. */
+                       if (status & RxEarly) {                         /* Rx early is unused. */
+                               vortex_rx(dev);
+                               outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+                       }
+                       if (status & StatsFull) {       /* Empty statistics. */
+                               static int DoneDidThat = 0;
+                               if (vortex_debug > 4)
+                                       printk("%s: Updating stats.\n", dev->name);
+                               update_stats(ioaddr, dev);
+                               /* DEBUG HACK: Disable statistics as an interrupt source. */
+                               /* This occurs when we have the wrong media type! */
+                               if (DoneDidThat == 0  &&
+                                       inw(ioaddr + EL3_STATUS) & StatsFull) {
+                                       int win, reg;
+                                       printk("%s: Updating stats failed, disabling stats as an"
+                                                  " interrupt source.\n", dev->name);
+                                       for (win = 0; win < 8; win++) {
+                                               EL3WINDOW(win);
+                                               printk("\n Vortex window %d:", win);
+                                               for (reg = 0; reg < 16; reg++)
+                                                       printk(" %2.2x", inb(ioaddr+reg));
+                                       }
+                                       EL3WINDOW(7);
+                                       outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD);
+                                       DoneDidThat++;
+                               }
+                       }
+                       if (status & AdapterFailure) {
+                               /* Adapter failure requires Rx reset and reinit. */
+                               outw(RxReset, ioaddr + EL3_CMD);
+                               /* Set the Rx filter to the current state. */
+                               outw(SetRxFilter | RxStation | RxBroadcast
+                                        | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
+                                        | (dev->flags & IFF_PROMISC ? RxProm : 0),
+                                        ioaddr + EL3_CMD);
+                               outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+                               outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+                       }
+               }
+
+               if (++i > 10) {
+                       printk("%s: Infinite loop in interrupt, status %4.4x.  "
+                                  "Disabling functions (%4.4x).\n",
+                                  dev->name, status, SetStatusEnb | ((~status) & 0xFE));
+                       /* Disable all pending interrupts. */
+                       outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD);
+                       outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+                       break;
+               }
+               /* Acknowledge the IRQ. */
+               outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+
+       } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+
+       if (vortex_debug > 4)
+               printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
+
+       dev->interrupt = 0;
+       return;
+}
+
+static int
+vortex_rx(struct device *dev)
+{
+       struct vortex_private *vp = (struct vortex_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+       int i;
+       short rx_status;
+
+       if (vortex_debug > 5)
+               printk("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+                          inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+       while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
+               if (rx_status & 0x4000) { /* Error, update stats. */
+                       unsigned char rx_error = inb(ioaddr + RxErrors);
+                       if (vortex_debug > 4)
+                               printk(" Rx error: status %2.2x.\n", rx_error);
+                       vp->stats.rx_errors++;
+                       if (rx_error & 0x01)  vp->stats.rx_over_errors++;
+                       if (rx_error & 0x02)  vp->stats.rx_length_errors++;
+                       if (rx_error & 0x04)  vp->stats.rx_frame_errors++;
+                       if (rx_error & 0x08)  vp->stats.rx_crc_errors++;
+                       if (rx_error & 0x10)  vp->stats.rx_length_errors++;
+               } else {
+                       /* The packet length: up to 4.5K!. */
+                       short pkt_len = rx_status & 0x1fff;
+                       struct sk_buff *skb;
+
+                       skb = dev_alloc_skb(pkt_len + 5);
+                       if (vortex_debug > 4)
+                               printk("Receiving packet size %d status %4.4x.\n",
+                                          pkt_len, rx_status);
+                       if (skb != NULL) {
+                               skb->dev = dev;
+                               skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
+                               /* 'skb_put()' points to the start of sk_buff data area. */
+                               insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
+                                        (pkt_len + 3) >> 2);
+                               skb->protocol = eth_type_trans(skb, dev);
+                               netif_rx(skb);
+                               outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+                               /* Wait a limited time to go to next packet. */
+                               for (i = 200; i >= 0; i--)
+                                       if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+                                               break;
+                               vp->stats.rx_packets++;
+                               continue;
+                       } else if (vortex_debug)
+                               printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+                                          dev->name, pkt_len);
+               }
+               vp->stats.rx_dropped++;
+               outw(RxDiscard, ioaddr + EL3_CMD);
+               /* Wait a limited time to skip this packet. */
+               for (i = 200; i >= 0; i--)
+                       if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+                               break;
+       }
+
+       return 0;
+}
+
+static int
+vortex_close(struct device *dev)
+{
+       int ioaddr = dev->base_addr;
+
+       dev->start = 0;
+       dev->tbusy = 1;
+
+       if (vortex_debug > 1)
+               printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
+                          dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
+
+       /* Turn off statistics ASAP.  We update lp->stats below. */
+       outw(StatsDisable, ioaddr + EL3_CMD);
+
+       /* Disable the receiver and transmitter. */
+       outw(RxDisable, ioaddr + EL3_CMD);
+       outw(TxDisable, ioaddr + EL3_CMD);
+
+       if (dev->if_port == 3)
+               /* Turn off thinnet power.  Green! */
+               outw(StopCoax, ioaddr + EL3_CMD);
+       else if (dev->if_port == 0) {
+               /* Disable link beat and jabber, if_port may change ere next open(). */
+               EL3WINDOW(4);
+               outw(inw(ioaddr + Wn4_Media) & ~Media_TP, ioaddr + Wn4_Media);
+       }
+
+       free_irq(dev->irq);
+       /* Mmmm, we should diable all interrupt sources here. */
+       irq2dev_map[dev->irq] = 0;
+
+       update_stats(ioaddr, dev);
+#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+#endif
+
+       return 0;
+}
+
+static struct enet_statistics *
+vortex_get_stats(struct device *dev)
+{
+       struct vortex_private *vp = (struct vortex_private *)dev->priv;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       update_stats(dev->base_addr, dev);
+       restore_flags(flags);
+       return &vp->stats;
+}
+
+/*  Update statistics.
+       Unlike with the EL3 we need not worry about interrupts changing
+       the window setting from underneath us, but we must still guard
+       against a race condition with a StatsUpdate interrupt updating the
+       table.  This is done by checking that the ASM (!) code generated uses
+       atomic updates with '+='.
+       */
+static void update_stats(int ioaddr, struct device *dev)
+{
+       struct vortex_private *vp = (struct vortex_private *)dev->priv;
+
+       /* Unlike the 3c5x9 we need not turn off stats updates while reading. */
+       /* Switch to the stats window, and read everything. */
+       EL3WINDOW(6);
+       vp->stats.tx_carrier_errors             += inb(ioaddr + 0);
+       vp->stats.tx_heartbeat_errors   += inb(ioaddr + 1);
+       /* Multiple collisions. */              inb(ioaddr + 2);
+       vp->stats.collisions                    += inb(ioaddr + 3);
+       vp->stats.tx_window_errors              += inb(ioaddr + 4);
+       vp->stats.rx_fifo_errors                += inb(ioaddr + 5);
+       vp->stats.tx_packets                    += inb(ioaddr + 6);
+       vp->stats.tx_packets                    += (inb(ioaddr + 9)&15) << 8;
+       /* Rx packets   */                              inb(ioaddr + 7);   /* Must read to clear */
+       /* Tx deferrals */                              inb(ioaddr + 8);
+       /* Don't bother with register 9, an extention of registers 6&7.
+          If we do use the 6&7 values the atomic update assumption above
+          is invalid. */
+       inw(ioaddr + 10);       /* Total Rx and Tx octets. */
+       inw(ioaddr + 12);
+       /* New: On the Vortex we must also clear the BadSSD counter. */
+       EL3WINDOW(3);
+       inb(ioaddr + 12);
+
+       /* We change back to window 7 (not 1) with the Vortex. */
+       EL3WINDOW(7);
+       return;
+}
+
+/* There are two version of set_multicast_list() to support both v1.2 and
+   v1.4 kernels. */
+static void
+set_multicast_list(struct device *dev)
+{
+       short ioaddr = dev->base_addr;
+
+       if ((dev->mc_list)  ||  (dev->flags & IFF_ALLMULTI)) {
+               outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+               if (vortex_debug > 3) {
+                       printk("%s: Setting Rx multicast mode, %d addresses.\n",
+                                  dev->name, dev->mc_count);
+               }
+       } else if (dev->flags & IFF_PROMISC) {
+               outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+                        ioaddr + EL3_CMD);
+       } else
+               outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+\f
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+       struct device *next_dev;
+
+       /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+       while (root_vortex_dev) {
+               next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module;
+               unregister_netdev(root_vortex_dev);
+               release_region(root_vortex_dev->base_addr, VORTEX_TOTAL_SIZE);
+               kfree(root_vortex_dev);
+               root_vortex_dev = next_dev;
+       }
+}
+#endif /* MODULE */
+\f
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o 3c59x.o"
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
index 9cbfa39d7cf081240fe090724c4c62b216c48617..b056a2fc12d6d903c10e7ba077d79c708b4259cd 100644 (file)
@@ -29,6 +29,7 @@ if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
     tristate '3c507 support' CONFIG_EL16
   fi
   tristate '3c509/3c579 support' CONFIG_EL3
+  tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
 fi
 bool 'Other ISA cards' CONFIG_NET_ISA
 if [ "$CONFIG_NET_ISA" = "y" ]; then
index bc8da3e895919de6be1f5e335ee04e6f8cdc1549..a766060b138a64db60a00dbacbfa70ab6bdd122f 100644 (file)
@@ -226,6 +226,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_VORTEX),y)
+L_OBJS += 3c59x.o
+else
+  ifeq ($(CONFIG_VORTEX),m)
+  M_OBJS += 3c59x.o
+  endif
+endif
+
 ifeq ($(CONFIG_EEXPRESS),y)
 L_OBJS += eexpress.o
 else
index 5b3ec2ab10daa6d26a63aad25ee654329d36dbf0..6e999a432706d7e80b716127d6ff8bf45b4aeaf6 100644 (file)
@@ -68,6 +68,7 @@ extern int ni52_probe(struct device *);
 extern int ni65_probe(struct device *);
 extern int SK_init(struct device *);
 extern int seeq8005_probe(struct device *);
+extern int tc59x_probe(struct device *);
 
 /* Detachable devices ("pocket adaptors") */
 extern int atp_init(struct device *);
@@ -83,6 +84,9 @@ ethif_probe(struct device *dev)
        return 1;               /* ENXIO */
 
     if (1
+#if defined(CONFIG_VORTEX)
+       && tc59x_probe(dev)
+#endif
 #if defined(CONFIG_SEEQ8005)
        && seeq8005_probe(dev)
 #endif
index 327c262c160ea5fb8232b3776d52aa7475310c08..f67bad89f7ba43f0b50875b284e07f36ed612acb 100644 (file)
@@ -325,7 +325,7 @@ int tok_probe(struct device *dev)
       if (intr==2) irq=10;
       if (intr==3) irq=11;
       while(!(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN));
-      ti->sram=(unsigned char *)((unsigned long)(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN) <<12);
+      ti->sram=(unsigned char *)((unsigned long)(*(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)) <<12);
       ti->global_int_enable=PIOaddr+ADAPTINTREL;
       break;
       
diff --git a/drivers/scsi/AM53C974.c b/drivers/scsi/AM53C974.c
new file mode 100644 (file)
index 0000000..93663ea
--- /dev/null
@@ -0,0 +1,2243 @@
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "AM53C974.h"
+#include "constants.h"
+#include "sd.h"
+
+/* AM53/79C974 (PCscsi) driver release 0.5
+ *
+ * The architecture and much of the code of this device
+ * driver was originally developed by Drew Eckhardt for
+ * the NCR5380. The following copyrights apply:
+ *  For the architecture and all pieces of code which can also be found 
+ *    in the NCR5380 device driver:
+ *   Copyright 1993, Drew Eckhardt
+ *     Visionary Computing 
+ *     (Unix and Linux consulting and custom programming)
+ *     drew@colorado.edu
+ *     +1 (303) 666-5836
+ *
+ *  The AM53C974_nobios_detect code was origininally developed by
+ *   Robin Cutshaw (robin@xfree86.org) and is used here in a 
+ *   slightly modified form.
+ *
+ *  For the remaining code:
+ *    Copyright 1994, D. Frieauff
+ *    EMail: fri@rsx42sun0.dofn.de
+ *    Phone: x49-7545-8-2256 , x49-7541-42305
+ */
+
+/*
+ * $Log: AM53C974.c,v $
+ */
+
+#ifdef AM53C974_DEBUG
+ #define DEB(x) x
+ #ifdef AM53C974_DEBUG_KEYWAIT
+   #define KEYWAIT() AM53C974_keywait()
+  #else
+   #define KEYWAIT()
+  #endif
+ #ifdef AM53C974_DEBUG_INIT
+   #define DEB_INIT(x) x
+  #else
+   #define DEB_INIT(x)
+  #endif
+ #ifdef AM53C974_DEBUG_MSG
+   #define DEB_MSG(x) x
+  #else
+   #define DEB_MSG(x)
+  #endif
+ #ifdef AM53C974_DEB_RESEL
+   #define DEB_RESEL(x) x
+  #else
+   #define DEB_RESEL(x)
+  #endif
+ #ifdef AM53C974_DEBUG_QUEUE
+  #define DEB_QUEUE(x) x
+  #define LIST(x,y) {printk("LINE:%d   Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
+  #define REMOVE(w,x,y,z) {printk("LINE:%d   Removing: %p->%p  %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
+ #else
+  #define DEB_QUEUE(x)
+  #define LIST(x,y)
+  #define REMOVE(w,x,y,z)
+ #endif
+ #ifdef AM53C974_DEBUG_INFO
+   #define DEB_INFO(x) x
+  #else
+   #define DEB_INFO(x)
+  #endif
+ #ifdef AM53C974_DEBUG_LINKED
+   #define DEB_LINKED(x) x
+  #else
+   #define DEB_LINKED(x)
+  #endif
+ #ifdef AM53C974_DEBUG_INTR
+   #define DEB_INTR(x) x
+  #else
+   #define DEB_INTR(x)
+  #endif
+#else
+ #define DEB_INIT(x)
+ #define DEB(x)
+ #define DEB_QUEUE(x)
+ #define LIST(x,y)
+ #define REMOVE(w,x,y,z)
+ #define DEB_INFO(x)
+ #define DEB_LINKED(x)
+ #define DEB_INTR(x)
+ #define DEB_MSG(x)
+ #define DEB_RESEL(x)
+ #define KEYWAIT()
+#endif
+ #ifdef AM53C974_DEBUG_ABORT
+   #define DEB_ABORT(x) x
+  #else
+   #define DEB_ABORT(x)
+  #endif
+
+#ifdef VERBOSE_AM53C974_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define INSIDE(x,l,h) ( ((x) >= (l)) && ((x) <= (h)) )
+
+#ifdef AM53C974_DEBUG
+static void AM53C974_print_pci(struct Scsi_Host *instance);
+static void AM53C974_print_phase(struct Scsi_Host *instance);
+static void AM53C974_print_queues(struct Scsi_Host *instance);
+#endif /* AM53C974_DEBUG */
+static void AM53C974_print(struct Scsi_Host *instance);
+static void AM53C974_keywait(void);
+static int AM53C974_bios_detect(Scsi_Host_Template *tpnt);
+static int AM53C974_nobios_detect(Scsi_Host_Template *tpnt);
+static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config);
+static void AM53C974_config_after_reset(struct Scsi_Host *instance);
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd);
+static __inline__ void run_main(void);
+static void AM53C974_main (void);
+static void AM53C974_intr(int irq, struct pt_regs *regs);
+static void AM53C974_intr_disconnect(struct Scsi_Host *instance); 
+static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg);
+static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target);
+static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target);
+static void AM53C974_information_transfer(struct Scsi_Host *instance, 
+                                          unsigned char statreg, unsigned char isreg,
+                                          unsigned char instreg, unsigned char cfifo,
+                                          unsigned char dmastatus);
+static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd, unsigned char msg);
+static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag);
+static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg);
+static  __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+                                              unsigned long length, char *data);
+static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus, 
+                               unsigned char statreg);
+static void AM53C974_intr_bus_reset(struct Scsi_Host *instance);
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+static struct Scsi_Host *first_host = NULL;    /* Head of list of AMD boards */
+static volatile int main_running = 0;
+static int commandline_current = 0;
+override_t overrides[7] = { {-1, 0, 0, 0}, };   /* LILO overrides */
+
+#ifdef AM53C974_DEBUG
+static int deb_stop = 1;
+
+/**************************************************************************
+ * Function : void AM53C974_print_pci(struct Scsi_Host *instance)
+ *
+ * Purpose : dump the PCI registers for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_pci(struct Scsi_Host *instance)
+{
+int            i;
+unsigned short vendor_id, device_id, command, status, scratch[8];
+unsigned long  class_revision, base; 
+unsigned char  irq, cache_line_size, latency_timer, header_type;
+
+AM53C974_PCIREG_OPEN();
+
+for (i = 0; i < 8; i++) *(scratch + i) = AM53C974_PCIREG_READ_WORD(instance, PCI_SCRATCH_REG_0 + 2*i);
+vendor_id = AM53C974_PCIREG_READ_WORD(instance, PCI_VENDOR_ID);
+device_id = AM53C974_PCIREG_READ_WORD(instance, PCI_DEVICE_ID);
+command   = AM53C974_PCIREG_READ_WORD(instance, PCI_COMMAND);
+status    = AM53C974_PCIREG_READ_WORD(instance, PCI_STATUS);
+class_revision = AM53C974_PCIREG_READ_DWORD(instance, PCI_CLASS_REVISION);
+cache_line_size = AM53C974_PCIREG_READ_BYTE(instance, PCI_CACHE_LINE_SIZE);
+latency_timer = AM53C974_PCIREG_READ_BYTE(instance, PCI_LATENCY_TIMER);
+header_type = AM53C974_PCIREG_READ_BYTE(instance, PCI_HEADER_TYPE);
+base = AM53C974_PCIREG_READ_DWORD(instance, PCI_BASE_ADDRESS_0);
+irq = AM53C974_PCIREG_READ_BYTE(instance, PCI_INTERRUPT_LINE);
+
+AM53C974_PCIREG_CLOSE();
+
+
+printk("------------- start of PCI register dump -------------\n");
+printk("PCI_VENDOR_ID:       0x%x\n", vendor_id);
+printk("PCI_DEVICE_ID:       0x%x\n", device_id);
+printk("PCI_COMMAND:         0x%x\n", command);
+printk("PCI_STATUS:          0x%x\n", status);
+printk("PCI_CLASS_REVISION:  0x%lx\n", class_revision);
+printk("PCI_CACHE_LINE_SIZE: 0x%x\n", cache_line_size);
+printk("PCI_LATENCY_TIMER:   0x%x\n", latency_timer);
+printk("PCI_HEADER_TYPE:     0x%x\n", header_type);
+printk("PCI_BASE_ADDRESS_0:  0x%lx\n", base);
+printk("PCI_INTERRUPT_LINE:  %d\n", irq);
+for (i = 0; i < 8; i++) printk("PCI_SCRATCH_%d:       0x%x\n", i, scratch[i]);
+printk("------------- end of PCI register dump -------------\n\n");
+}
+
+static struct {
+    unsigned char value;
+    char *name;
+} phases[] = {
+{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+{PHASE_RES_0, "RESERVED 0"}, {PHASE_RES_1, "RESERVED 1"}};
+
+/************************************************************************** 
+ * Function : void AM53C974_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_phase(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned char statreg, latched;
+int           i;
+AM53C974_setio(instance);
+
+latched = (AM53C974_read_8(CNTLREG2)) & CNTLREG2_ENF;
+statreg = AM53C974_read_8(STATREG);
+for (i = 0; (phases[i].value != PHASE_RES_1) && 
+     (phases[i].value != (statreg & STATREG_PHASE)); ++i); 
+if (latched)
+   printk("scsi%d : phase %s, latched at end of last command\n", instance->host_no, phases[i].name);
+  else
+   printk("scsi%d : phase %s, real time\n", instance->host_no, phases[i].name);
+}
+
+/**************************************************************************
+ * Function : void AM53C974_print_queues(struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues
+ *
+ * Inputs : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_queues(struct Scsi_Host *instance)
+{
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *ptr;
+
+printk("AM53C974: coroutine is%s running.\n", main_running ? "" : "n't");
+    
+cli();
+    
+if (!hostdata->connected) {
+   printk ("scsi%d: no currently connected command\n", instance->host_no); } 
+  else {
+   print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->connected); }
+if (!hostdata->sel_cmd) {
+   printk ("scsi%d: no currently arbitrating command\n", instance->host_no); } 
+  else {
+   print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->sel_cmd); }
+
+printk ("scsi%d: issue_queue ", instance->host_no);
+if (!hostdata->issue_queue)
+   printk("empty\n");
+  else {
+   printk(":\n");
+   for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble) 
+       print_Scsi_Cmnd (ptr); }
+
+printk ("scsi%d: disconnected_queue ", instance->host_no);
+if (!hostdata->disconnected_queue)
+   printk("empty\n");
+  else {
+   printk(":\n");
+   for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble) 
+       print_Scsi_Cmnd (ptr); }
+    
+sti();
+}
+
+#endif /* AM53C974_DEBUG */
+
+/**************************************************************************
+ * Function : void AM53C974_print(struct Scsi_Host *instance)
+ *
+ * Purpose : dump the chip registers for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned long ctcreg, dmastc, dmaspa, dmawbc, dmawac;
+unsigned char cmdreg, statreg, isreg, cfireg, cntlreg[4], dmacmd, dmastatus;
+AM53C974_setio(instance);
+
+cli();
+ctcreg = AM53C974_read_8(CTCHREG) << 16;
+ctcreg |= AM53C974_read_8(CTCMREG) << 8;
+ctcreg |= AM53C974_read_8(CTCLREG);
+cmdreg = AM53C974_read_8(CMDREG);
+statreg = AM53C974_read_8(STATREG);
+isreg = AM53C974_read_8(ISREG);
+cfireg = AM53C974_read_8(CFIREG);
+cntlreg[0] = AM53C974_read_8(CNTLREG1);
+cntlreg[1] = AM53C974_read_8(CNTLREG2);
+cntlreg[2] = AM53C974_read_8(CNTLREG3);
+cntlreg[3] = AM53C974_read_8(CNTLREG4);
+dmacmd = AM53C974_read_8(DMACMD);
+dmastc = AM53C974_read_32(DMASTC);
+dmaspa = AM53C974_read_32(DMASPA);
+dmawbc = AM53C974_read_32(DMAWBC);
+dmawac = AM53C974_read_32(DMAWAC);
+dmastatus = AM53C974_read_8(DMASTATUS);
+sti();
+
+printk("AM53C974 register dump:\n");
+printk("IO base: 0x%04lx; CTCREG: 0x%04lx; CMDREG: 0x%02x; STATREG: 0x%02x; ISREG: 0x%02x\n",
+       io_port, ctcreg, cmdreg, statreg, isreg);
+printk("CFIREG: 0x%02x; CNTLREG1-4: 0x%02x; 0x%02x; 0x%02x; 0x%02x\n",
+        cfireg, cntlreg[0], cntlreg[1], cntlreg[2], cntlreg[3]);
+printk("DMACMD: 0x%02x; DMASTC: 0x%04lx; DMASPA: 0x%04lx\n", dmacmd, dmastc, dmaspa);
+printk("DMAWBC: 0x%04lx; DMAWAC: 0x%04lx; DMASTATUS: 0x%02x\n", dmawbc, dmawac, dmastatus);
+printk("---------------------------------------------------------\n");
+}
+
+/**************************************************************************
+* Function : void AM53C974_keywait(void)
+*
+* Purpose : wait until a key is pressed, if it was the 'r' key leave singlestep mode;
+*           this function is used for debugging only
+*
+* Input : none
+**************************************************************************/
+static void AM53C974_keywait(void)
+{
+#ifdef AM53C974_DEBUG
+int key;
+
+if (!deb_stop) return;
+#endif
+
+cli();
+while ((inb_p(0x64) & 0x01) != 0x01) ;
+#ifdef AM53C974_DEBUG
+key = inb(0x60);
+if (key == 0x93) deb_stop = 0;  /* don't stop if 'r' was pressed */
+#endif
+sti();
+}
+
+/**************************************************************************
+* Function : AM53C974_setup(char *str, int *ints)
+*
+* Purpose : LILO command line initialization of the overrides array,
+* 
+* Inputs : str - unused, ints - array of integer parameters with ints[0]
+*          equal to the number of ints.
+*
+* NOTE : this function needs to be declared as an external function
+*         in init/main.c and included there in the bootsetups list
+***************************************************************************/
+void AM53C974_setup(char *str, int *ints)
+{
+if (ints[0] < 4) 
+   printk("AM53C974_setup: wrong number of parameters;\n correct syntax is: AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset\n");
+  else {
+   if (commandline_current < (sizeof(overrides) / sizeof(override_t))) {
+      if ((ints[1] < 0) || (ints[1] > 7) ||
+          (ints[2] < 0) || (ints[2] > 7) ||
+          (ints[1] == ints[2]) ||
+          (ints[3] < (DEF_CLK / MAX_PERIOD)) || (ints[3] > (DEF_CLK / MIN_PERIOD)) ||
+          (ints[4] < 0) || (ints[4] > MAX_OFFSET))
+         printk("AM53C974_setup: illegal parameter\n");
+        else {
+         overrides[commandline_current].host_scsi_id = ints[1];
+         overrides[commandline_current].target_scsi_id = ints[2];
+         overrides[commandline_current].max_rate = ints[3];
+         overrides[commandline_current].max_offset = ints[4];
+         commandline_current++; }
+      }
+     else
+      printk("AM53C974_setup: too many overrides\n");
+   }
+}
+
+#if defined (CONFIG_PCI)
+/**************************************************************************
+* Function : int AM53C974_bios_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips with PCI Bios
+*
+* Inputs : tpnt - host template
+* 
+* Returns : number of host adapters detected
+**************************************************************************/
+int AM53C974_bios_detect(Scsi_Host_Template *tpnt)
+{
+int count = 0;        /* number of boards detected */
+int pci_index;
+pci_config_t pci_config;
+
+for (pci_index = 0; pci_index <= 16; ++pci_index) {
+    unsigned char pci_bus, pci_device_fn;
+    if (pcibios_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI, pci_index, &pci_bus, &pci_device_fn) != 0)
+       break;
+
+    pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &pci_config._vendor);
+    pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &pci_config._device);
+    pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_config._command);
+    pcibios_read_config_word(pci_bus, pci_device_fn, PCI_STATUS, &pci_config._status);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &pci_config._class_revision);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CACHE_LINE_SIZE, &pci_config._cache_line_size);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_config._latency_timer);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_HEADER_TYPE, &pci_config._header_type);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_BIST, &pci_config._bist);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_config._base0);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_config._base1);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_config._base2);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_config._base3);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_4, &pci_config._base4);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_5, &pci_config._base5);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_ROM_ADDRESS, &pci_config._baserom);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_config._int_line);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_PIN, &pci_config._int_pin);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MIN_GNT, &pci_config._min_gnt);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MAX_LAT, &pci_config._max_lat);
+    pci_config._pcibus = 0xFFFFFFFF;
+    pci_config._cardnum = 0xFFFFFFFF;
+    /* check whether device is I/O mapped -- should be */
+    if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+    /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+       to set the PCI Master Enable Bit if needed. 
+       (from Mark Stockton <marks@schooner.sys.hou.compaq.com>) */
+    if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+       pci_config._command |= PCI_COMMAND_MASTER;
+       printk("PCI Master Bit has not been set. Setting...\n");
+       pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, pci_config._command); }
+
+    /* everything seems OK now, so initialize */
+    if (AM53C974_init(tpnt, pci_config)) count++ ;
+    }
+return (count);
+}
+#endif
+
+/**************************************************************************
+* Function : int AM53C974_nobios_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips using PCI config 2 
+*
+* Inputs : tpnt - host template
+* 
+* Returns : number of host adapters detected
+*
+* NOTE : This code assumes the controller on PCI bus 0.
+*
+* Origin: Robin Cutshaw (robin@xfree86.org)
+**************************************************************************/
+int AM53C974_nobios_detect(Scsi_Host_Template *tpnt)
+{
+int          count = 0;                /* number of boards detected */
+pci_config_t pci_config;
+
+/* first try PCI config method 1 */
+for (pci_config._pcibus = 0; pci_config._pcibus < 0x10; pci_config._pcibus++) {
+    for (pci_config._cardnum = 0; pci_config._cardnum < 0x20; pci_config._cardnum++) {
+        unsigned long config_cmd;
+       config_cmd = 0x80000000 | (pci_config._pcibus<<16) | (pci_config._cardnum<<11);
+
+        outl(config_cmd, 0xCF8);         /* ioreg 0 */
+        pci_config._device_vendor = inl(0xCFC);
+
+        if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) {
+           outl(config_cmd | PCI_COMMAND, 0xCF8); pci_config._status_command  = inl(0xCFC);
+           outl(config_cmd | PCI_CLASS_REVISION, 0xCF8); pci_config._class_revision = inl(0xCFC);
+           outl(config_cmd | PCI_CACHE_LINE_SIZE, 0xCF8); pci_config._bist_header_latency_cache = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_0, 0xCF8); pci_config._base0 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_1, 0xCF8); pci_config._base1 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_2, 0xCF8); pci_config._base2 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_3, 0xCF8); pci_config._base3 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_4, 0xCF8); pci_config._base4 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_5, 0xCF8); pci_config._base5 = inl(0xCFC);
+           outl(config_cmd | PCI_ROM_ADDRESS, 0xCF8); pci_config._baserom = inl(0xCFC);
+           outl(config_cmd | PCI_INTERRUPT_LINE, 0xCF8); pci_config._max_min_ipin_iline = inl(0xCFC);
+
+           /* check whether device is I/O mapped -- should be */
+           if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+           /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+              to set the PCI Master Enable Bit if needed. 
+              From Mark Stockton <marks@schooner.sys.hou.compaq.com> */
+           if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+              pci_config._command |= PCI_COMMAND_MASTER;
+              printk("Config 1; PCI Master Bit has not been set. Setting...\n");
+              outl(config_cmd | PCI_COMMAND, 0xCF8); outw(pci_config._command, 0xCFC); }
+
+           /* everything seems OK now, so initialize */
+           if (AM53C974_init(tpnt, pci_config)) count++ ;
+           }
+        }
+    }
+outb(0, 0xCF8); /* is this really necessary? */
+
+/* try PCI config method 2, if no device was detected by method 1 */
+if (!count) {
+   AM53C974_PCIREG_OPEN();
+
+   pci_config._pcibus = 0xFFFFFFFF;
+   pci_config._cardnum = 0xFFFFFFFF;
+
+   for (pci_config._ioaddr = 0xC000; pci_config._ioaddr < 0xD000; pci_config._ioaddr += 0x0100) {
+       pci_config._device_vendor = inl(pci_config._ioaddr);
+
+       if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) {
+          pci_config._status_command = inl(pci_config._ioaddr + PCI_COMMAND);
+          pci_config._class_revision = inl(pci_config._ioaddr + PCI_CLASS_REVISION);
+          pci_config._bist_header_latency_cache = inl(pci_config._ioaddr + PCI_CACHE_LINE_SIZE);
+          pci_config._base0 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_0);
+          pci_config._base1 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_1);
+          pci_config._base2 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_2);
+          pci_config._base3 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_3);
+          pci_config._base4 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_4);
+          pci_config._base5 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_5);
+          pci_config._baserom = inl(pci_config._ioaddr + PCI_ROM_ADDRESS);
+          pci_config._max_min_ipin_iline = inl(pci_config._ioaddr + PCI_INTERRUPT_LINE);
+
+          /* check whether device is I/O mapped -- should be */
+          if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+          /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+             to set the PCI Master Enable Bit if needed.
+             From Mark Stockton <marks@schooner.sys.hou.compaq.com> */
+          if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+              pci_config._command |= PCI_COMMAND_MASTER;
+              printk("Config 2; PCI Master Bit has not been set. Setting...\n");
+              outw(pci_config._command, pci_config._ioaddr + PCI_COMMAND); }
+
+          /* everything seems OK now, so initialize */
+          if (AM53C974_init(tpnt, pci_config)) count++ ;
+          }
+       }
+   AM53C974_PCIREG_CLOSE();
+   }
+
+return(count);
+}
+
+/**************************************************************************
+* Function : int AM53C974_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips
+*
+* Inputs : tpnt - host template
+* 
+* Returns : number of host adapters detected
+**************************************************************************/
+int AM53C974_detect(Scsi_Host_Template *tpnt)
+{
+int count;        /* number of boards detected */
+
+#if defined (CONFIG_PCI)
+if (pcibios_present())
+   count = AM53C974_bios_detect(tpnt);
+  else
+#endif
+count = AM53C974_nobios_detect(tpnt);
+return (count);
+}
+
+/**************************************************************************
+* Function : int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config)
+*
+* Purpose : initializes instance and corresponding AM53/79C974 chip,
+*
+* Inputs : tpnt - template, pci_config - PCI configuration,
+* 
+* Returns : 1 on success, 0 on failure.
+* 
+* NOTE: If no override for the controller's SCSI id is given and AM53C974_SCSI_ID 
+*       is not defined we assume that the SCSI address of this controller is correctly
+*       set up by the BIOS (as reflected by contents of register CNTLREG1).
+*       This is the only BIOS assistance we need.
+**************************************************************************/
+static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config)
+{
+AM53C974_local_declare();
+int                      i, j;
+struct Scsi_Host         *instance, *search;
+struct AM53C974_hostdata *hostdata;
+
+#ifdef AM53C974_OPTION_DEBUG_PROBE_ONLY
+   printk ("AM53C974: probe only enabled, aborting initialization\n");
+   return -1;
+#endif
+
+instance = scsi_register(tpnt, sizeof(struct AM53C974_hostdata));
+hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+instance->base = NULL;
+instance->io_port = pci_config._base0 & (pci_config._base0 & 0x1 ? 
+                                         0xFFFFFFFC : 0xFFFFFFF0);
+instance->irq = pci_config._int_line;
+instance->dma_channel = -1;
+AM53C974_setio(instance);
+
+#ifdef AM53C974_SCSI_ID
+instance->this_id = AM53C974_SCSI_ID;
+AM53C974_write_8(CNTLREG1, instance->this_id & CNTLREG1_SID);
+#else
+instance->this_id = AM53C974_read_8(CNTLREG1) & CNTLREG1_SID;
+if (instance->this_id != 7) 
+   printk("scsi%d: WARNING: unusual hostadapter SCSI id %d; please verify!\n", 
+          instance->host_no, instance->this_id);
+#endif
+
+for (i = 0; i < sizeof(hostdata->msgout); i++) {
+    hostdata->msgout[i] = NOP;
+    hostdata->last_message[i] = NOP; }
+for (i = 0; i < 8; i++) {
+    hostdata->busy[i] = 0;
+    hostdata->sync_per[i] = DEF_STP;
+    hostdata->sync_off[i] = 0;
+    hostdata->sync_neg[i] = 0;
+    hostdata->sync_en[i] = DEFAULT_SYNC_NEGOTIATION_ENABLED;
+    hostdata->max_rate[i] = DEFAULT_RATE;
+    hostdata->max_offset[i] = DEFAULT_SYNC_OFFSET; }
+
+/* overwrite defaults by LILO overrides */
+for (i = 0; i < commandline_current; i++) {
+    if (overrides[i].host_scsi_id == instance->this_id) {
+       j = overrides[i].target_scsi_id;
+       hostdata->sync_en[j] = 1;
+       hostdata->max_rate[j] = overrides[i].max_rate;
+       hostdata->max_offset[j] = overrides[i].max_offset; 
+       }
+    }
+
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->issue_queue = NULL;
+hostdata->disconnected_queue = NULL;
+hostdata->in_reset = 0;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+
+/* Set up an interrupt handler if we aren't already sharing an IRQ with another board */
+for (search = first_host; 
+     search && ( ((the_template != NULL) && (search->hostt != the_template)) || 
+                 (search->irq != instance->irq) || (search == instance) );
+     search = search->next);
+if (!search) {
+   if (request_irq(instance->irq, AM53C974_intr, SA_INTERRUPT, "AM53C974")) {
+      printk("scsi%d: IRQ%d not free, detaching\n", instance->host_no, instance->irq);
+      scsi_unregister(instance);
+      return -1; } 
+   }
+  else {
+   printk("scsi%d: using interrupt handler previously installed for scsi%d\n",
+         instance->host_no, search->host_no); }
+
+if (!the_template) {
+   the_template = instance->hostt;
+   first_instance = instance; }
+
+/* do hard reset */
+AM53C974_write_8(CMDREG, CMDREG_RDEV);     /* reset device */
+udelay(5);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); 
+AM53C974_write_8(CMDREG, CMDREG_RBUS);     /* reset SCSI bus */
+udelay(10);
+AM53C974_config_after_reset(instance);
+
+return(0);
+}
+
+/*********************************************************************
+* Function : AM53C974_config_after_reset(struct Scsi_Host *instance) *
+*                                                                    *
+* Purpose : initializes chip registers after reset                   *
+*                                                                    *
+* Inputs : instance - which AM53C974                                 *
+*                                                                    *
+* Returns : nothing                                                  *
+**********************************************************************/
+static void AM53C974_config_after_reset(struct Scsi_Host *instance)
+{
+AM53C974_local_declare(); 
+AM53C974_setio(instance);
+
+/* clear SCSI FIFO */
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+
+/* configure device */
+AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT);
+AM53C974_write_8(STPREG, DEF_STP & STPREG_STP);
+AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+AM53C974_write_8(CLKFREG, DEF_CLKF & CLKFREG_MASK);
+AM53C974_write_8(CNTLREG1, (DEF_ETM<<7) | CNTLREG1_DISR | (DEF_PERE<<4) | instance->this_id);
+AM53C974_write_8(CNTLREG2, (DEF_ENF<<6));
+AM53C974_write_8(CNTLREG3, (DEF_ADIDCHK<<7) | (DEF_FASTSCSI<<4) | (DEF_FASTCLK<<3));
+AM53C974_write_8(CNTLREG4, (DEF_GLITCH<<6) | (DEF_PWD<<5) | (DEF_RAE<<3) | (DEF_RADE<<2) | CNTLREG4_RES);
+}
+
+/***********************************************************************
+* Function : const char *AM53C974_info(struct Scsi_Host *instance)     *
+*                                                                      *
+* Purpose : return device driver information                           *
+*                                                                      *
+* Inputs : instance - which AM53C974                                   *
+*                                                                      *
+* Returns : info string                                                *
+************************************************************************/
+const char *AM53C974_info(struct Scsi_Host *instance)
+{
+static char       info[100];
+
+sprintf(info, "AM53/79C974 PCscsi driver rev. %d.%d; host I/O address: 0x%x; irq: %d\n", 
+        AM53C974_DRIVER_REVISION_MAJOR, AM53C974_DRIVER_REVISION_MINOR,
+        instance->io_port, instance->irq);
+return (info);
+}
+
+/************************************************************************** 
+* Function : int AM53C974_command (Scsi_Cmnd *SCpnt)                      *
+*                                                                         *
+* Purpose : the unqueued SCSI command function, replaced by the           *
+*           AM53C974_queue_command function                               *
+*                                                                         *
+* Inputs : SCpnt - pointer to command structure                           *
+*                                                                         *
+* Returns :status, see hosts.h for details                                *
+***************************************************************************/
+int AM53C974_command(Scsi_Cmnd *SCpnt)
+{
+DEB(printk("AM53C974_command called\n"));
+return 0;
+}
+
+/**************************************************************************
+* Function : void initialize_SCp(Scsi_Cmnd *cmd)                          *
+*                                                                         *
+* Purpose : initialize the saved data pointers for cmd to point to the    *
+*          start of the buffer.                                          *                              
+*                                                                         *
+* Inputs : cmd - Scsi_Cmnd structure to have pointers reset.              *
+*                                                                         *
+* Returns : nothing                                                       *
+**************************************************************************/
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
+{
+if (cmd->use_sg) {
+   cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;
+   cmd->SCp.buffers_residual = cmd->use_sg - 1;
+   cmd->SCp.ptr = (char *)cmd->SCp.buffer->address;
+   cmd->SCp.this_residual = cmd->SCp.buffer->length; }
+  else {
+   cmd->SCp.buffer = NULL;
+   cmd->SCp.buffers_residual = 0;
+   cmd->SCp.ptr = (char *)cmd->request_buffer;
+   cmd->SCp.this_residual = cmd->request_bufflen; }
+}
+
+/**************************************************************************
+* Function : run_main(void)                                               *
+*                                                                         *
+* Purpose : insure that the coroutine is running and will process our     *
+*          request.  main_running is checked/set here (in an inline      *
+*           function rather than in AM53C974_main itself to reduce the    *
+*           chances of stack overflow.                                    *
+*                                                                         *
+*                                                                         *
+* Inputs : none                                                           *
+*                                                                         *
+* Returns : nothing                                                       *
+**************************************************************************/
+static __inline__ void run_main(void)
+{
+cli();
+if (!main_running) {
+   /* main_running is cleared in AM53C974_main once it can't do 
+      more work, and AM53C974_main exits with interrupts disabled. */
+   main_running = 1;
+   AM53C974_main();
+   sti(); }
+  else 
+   sti();
+}
+
+/************************************************************************** 
+* Function : int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+*
+* Purpose : writes SCSI command into AM53C974 FIFO 
+*
+* Inputs : cmd - SCSI command, done - function called on completion, with
+*      a pointer to the command descriptor.
+* 
+* Returns : status, see hosts.h for details
+*
+* Side effects : 
+*      cmd is added to the per instance issue_queue, with minor 
+*      twiddling done to the host specific fields of cmd.  If the 
+*      main coroutine is not running, it is restarted.
+**************************************************************************/
+int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+struct Scsi_Host         *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd                *tmp;
+
+cli();
+DEB_QUEUE(printk(SEPARATOR_LINE));
+DEB_QUEUE(printk("scsi%d: AM53C974_queue_command called\n", instance->host_no));
+DEB_QUEUE(printk("cmd=%02x target=%02x lun=%02x bufflen=%d use_sg = %02x\n", 
+          cmd->cmnd[0], cmd->target, cmd->lun, cmd->request_bufflen, cmd->use_sg));
+
+/* We use the host_scribble field as a pointer to the next command in a queue */
+cmd->host_scribble = NULL;
+cmd->scsi_done = done;
+cmd->result = 0;
+cmd->device->disconnect = 0;
+
+/* Insert the cmd into the issue queue. Note that REQUEST SENSE 
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the 
+ * sense data is only guaranteed to be valid while the condition exists. */
+if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+   LIST(cmd, hostdata->issue_queue);
+   cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+   hostdata->issue_queue = cmd; }
+  else {
+   for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp->host_scribble; 
+       tmp = (Scsi_Cmnd *)tmp->host_scribble);
+   LIST(cmd, tmp);
+   tmp->host_scribble = (unsigned char *)cmd; }
+
+DEB_QUEUE(printk("scsi%d : command added to %s of queue\n", instance->host_no,
+         (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"));
+
+/* Run the coroutine if it isn't already running. */
+run_main();
+return 0;
+}
+
+/**************************************************************************
+ * Function : AM53C974_main (void) 
+ *
+ * Purpose : AM53C974_main is a coroutine that runs as long as more work can 
+ *     be done on the AM53C974 host adapters in a system.  Both 
+ *     AM53C974_queue_command() and AM53C974_intr() will try to start it 
+ *     in case it is not running.
+ * 
+ * NOTE : AM53C974_main exits with interrupts *disabled*, the caller should 
+ *  reenable them.  This prevents reentrancy and kernel stack overflow.
+ **************************************************************************/  
+static void AM53C974_main(void)
+{
+AM53C974_local_declare(); 
+Scsi_Cmnd                *tmp, *prev;
+struct Scsi_Host         *instance;
+struct AM53C974_hostdata *hostdata;
+int                      done;
+
+/* We run (with interrupts disabled) until we're sure that none of 
+ * the host adapters have anything that can be done, at which point 
+ * we set main_running to 0 and exit. */
+
+do {
+   cli(); /* Freeze request queues */
+   done = 1;
+   for (instance = first_instance; instance && instance->hostt == the_template;
+        instance = instance->next) {
+       hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+       AM53C974_setio(instance);
+       /* start to select target if we are not connected and not in the 
+          selection process */
+       if (!hostdata->connected && !hostdata->sel_cmd) {
+          /* Search through the issue_queue for a command destined for a target 
+             that is not busy. */
+          for (tmp = (Scsi_Cmnd *)hostdata->issue_queue, prev = NULL; tmp; 
+               prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+             /*  When we find one, remove it from the issue queue. */
+             if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
+                if (prev) {
+                   REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
+                           (Scsi_Cmnd *)(tmp->host_scribble));
+                   prev->host_scribble = tmp->host_scribble; } 
+                   else {
+                   REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
+                   hostdata->issue_queue = (Scsi_Cmnd *)tmp->host_scribble; }
+                tmp->host_scribble = NULL;
+
+                 /* go into selection mode, disable reselection and wait for
+                    SO interrupt which will continue with the selection process */
+                 hostdata->selecting = 1;
+                 hostdata->sel_cmd = tmp;
+                 AM53C974_write_8(CMDREG, CMDREG_DSR); 
+                 break;                 
+                } /* if target/lun is not busy */
+
+             } /* for */
+          } /* if (!hostdata->connected) */
+         else {                
+          DEB(printk("main: connected; cmd = 0x%lx, sel_cmd = 0x%lx\n",
+                 (long)hostdata->connected, (long)hostdata->sel_cmd));
+         }
+       } /* for instance */
+   } while (!done);
+main_running = 0;
+}
+
+/*********************************************************************
+* Function : AM53C974_intr(int irq, struct pt_regs *regs)            *
+*                                                                    *
+* Purpose : interrupt handler                                        *
+*                                                                    *
+* Inputs : irq - interrupt line, regs - ?                            *
+*                                                                    *
+* Returns : nothing                                                  *
+**********************************************************************/
+static void AM53C974_intr(int irq, struct pt_regs *regs)
+{
+AM53C974_local_declare(); 
+struct Scsi_Host         *instance;
+struct AM53C974_hostdata *hostdata;
+unsigned char            cmdreg, dmastatus, statreg, isreg, instreg, cfifo;
+
+/* find AM53C974 hostadapter responsible for this interrupt */
+for (instance = first_instance; instance; instance = instance->next)
+    if ((instance->irq == irq) && (instance->hostt == the_template)) goto FOUND;
+sti();
+return;
+
+/* found; now decode and process */
+FOUND:
+hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+dmastatus = AM53C974_read_8(DMASTATUS);
+
+DEB_INTR(printk(SEPARATOR_LINE));
+DEB_INTR(printk("AM53C974 interrupt; dmastatus=0x%02x\n", dmastatus));
+KEYWAIT();
+
+/*** DMA related interrupts ***/
+if (hostdata->connected && (dmastatus & (DMASTATUS_ERROR | DMASTATUS_PWDN | 
+                                         DMASTATUS_ABORT))) {
+   /* DMA error or POWERDOWN */
+   printk("scsi%d: DMA error or powerdown; dmastatus: 0x%02x\n",
+          instance->host_no, dmastatus);
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   panic("scsi%d: cannot recover\n", instance->host_no); }
+
+if (hostdata->connected && (dmastatus & DMASTATUS_DONE)) {     
+   /* DMA transfer done */
+   unsigned long residual;
+   cli();
+   if (!(AM53C974_read_8(DMACMD) & DMACMD_DIR)) {
+      do {
+         dmastatus = AM53C974_read_8(DMASTATUS);
+         residual  = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+                    (AM53C974_read_8(CTCHREG) << 16);
+         residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
+         } while (!(dmastatus & DMASTATUS_SCSIINT) && residual);
+      residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+                 (AM53C974_read_8(CTCHREG) << 16);
+      residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
+      }
+     else
+      residual = 0;
+   hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - residual;
+   hostdata->connected->SCp.this_residual = residual;
+
+   AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+   /* if service request missed before, process it now (ugly) */
+   if (hostdata->dma_busy) {
+      hostdata->dma_busy = 0;
+      cmdreg = AM53C974_read_8(CMDREG);
+      statreg = AM53C974_read_8(STATREG);
+      isreg = AM53C974_read_8(ISREG);
+      instreg = AM53C974_read_8(INSTREG);
+      cfifo = AM53C974_cfifo();
+      AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo,
+                                    dmastatus); }
+   sti();
+   }
+   
+if (!(dmastatus & DMASTATUS_SCSIINT)) {
+   sti();
+   return; }
+
+/*** SCSI related interrupts ***/
+cmdreg = AM53C974_read_8(CMDREG);
+statreg = AM53C974_read_8(STATREG);
+isreg = AM53C974_read_8(ISREG);
+instreg = AM53C974_read_8(INSTREG);
+cfifo = AM53C974_cfifo();
+
+DEB_INTR(printk("scsi%d: statreg: 0x%02x; isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+                instance->host_no, statreg, isreg, instreg, cfifo));
+
+if (statreg & STATREG_PE) {
+   /* parity error */
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   printk("scsi%d : PARITY error\n", instance->host_no);
+   if (hostdata->connected) hostdata->sync_off[hostdata->connected->target] = 0; /* setup asynchronous transfer */
+   hostdata->aborted = 1; }
+
+if (statreg & STATREG_IOE) {
+   /* illegal operation error */
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   printk("scsi%d : ILLEGAL OPERATION error\n", instance->host_no);
+   printk("cmdreg:  0x%02x; dmacmd:  0x%02x; statreg: 0x%02x; \n"
+          "isreg:   0x%02x; instreg: 0x%02x; cfifo:   0x%02x\n",
+           cmdreg, AM53C974_read_8(DMACMD), statreg, isreg, instreg, cfifo); }
+if (hostdata->in_reset && (instreg & INSTREG_SRST)) {
+   /* RESET INTERRUPT */
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   DEB(printk("Bus reset interrupt received\n"));
+   AM53C974_intr_bus_reset(instance);
+   cli();
+   if (hostdata->connected) {
+      hostdata->connected->result = DID_RESET << 16;
+      hostdata->connected->scsi_done((Scsi_Cmnd *)hostdata->connected);
+      hostdata->connected = NULL; }
+     else { 
+      if (hostdata->sel_cmd) {
+         hostdata->sel_cmd->result = DID_RESET << 16;
+         hostdata->sel_cmd->scsi_done((Scsi_Cmnd *)hostdata->sel_cmd);
+         hostdata->sel_cmd = NULL; }
+      }
+   sti();
+   if (hostdata->in_reset == 1) goto EXIT;
+     else return;
+   }
+
+if (instreg & INSTREG_ICMD) {
+   /* INVALID COMMAND INTERRUPT */
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   printk("scsi%d: Invalid command interrupt\n", instance->host_no);
+   printk("cmdreg:  0x%02x; dmacmd:  0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n"
+          "isreg:   0x%02x; instreg: 0x%02x; cfifo:   0x%02x\n",
+           cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo);
+   panic("scsi%d: cannot recover\n", instance->host_no); }
+
+if (instreg & INSTREG_DIS) {
+   /* DISCONNECT INTERRUPT */
+   DEB_INTR(printk("Disconnect interrupt received; "));
+   cli();
+   AM53C974_intr_disconnect(instance);
+   sti();
+   goto EXIT; }
+
+if (instreg & INSTREG_RESEL) {
+   /* RESELECTION INTERRUPT */
+   DEB_INTR(printk("Reselection interrupt received\n"));
+   cli();
+   AM53C974_intr_reselect(instance, statreg);
+   sti();
+   goto EXIT; }
+
+if (instreg & INSTREG_SO) {
+   DEB_INTR(printk("Successful operation interrupt received\n"));
+   if (hostdata->selecting) {
+      DEB_INTR(printk("DSR completed, starting select\n"));
+      cli();
+      AM53C974_select(instance, (Scsi_Cmnd *)hostdata->sel_cmd,
+                          (hostdata->sel_cmd->cmnd[0] == REQUEST_SENSE) ? 
+                                                  TAG_NONE : TAG_NEXT);
+      hostdata->selecting = 0;
+      AM53C974_set_sync(instance, hostdata->sel_cmd->target);
+      sti();
+      return; }
+
+   if (hostdata->sel_cmd != NULL) {
+      if ( ((isreg & ISREG_IS) != ISREG_OK_NO_STOP) &&
+           ((isreg & ISREG_IS) != ISREG_OK_STOP) ) {
+         /* UNSUCCESSFUL SELECTION */
+         DEB_INTR(printk("unsuccessful selection\n"));
+         cli();
+         hostdata->dma_busy = 0;
+        LIST(hostdata->sel_cmd, hostdata->issue_queue);
+        hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+         hostdata->issue_queue = hostdata->sel_cmd;
+         hostdata->sel_cmd = NULL;
+         hostdata->selecting = 0;
+         sti();
+         goto EXIT; }
+        else {
+         /* SUCCESSFUL SELECTION */
+         DEB(printk("successful selection; cmd=0x%02lx\n", (long)hostdata->sel_cmd));
+         cli();
+         hostdata->dma_busy = 0;
+         hostdata->disconnecting = 0;
+         hostdata->connected = hostdata->sel_cmd;
+         hostdata->sel_cmd = NULL;
+         hostdata->selecting = 0;
+#ifdef SCSI2
+         if (!hostdata->connected->device->tagged_queue)
+#endif    
+            hostdata->busy[hostdata->connected->target] |= (1 << hostdata->connected->lun);
+         /* very strange -- use_sg is sometimes nonzero for request sense commands !! */
+         if ((hostdata->connected->cmnd[0] == REQUEST_SENSE) && hostdata->connected->use_sg) {
+            DEB(printk("scsi%d: REQUEST_SENSE command with nonzero use_sg\n", instance->host_no));
+            KEYWAIT();
+            hostdata->connected->use_sg = 0; }
+         initialize_SCp((Scsi_Cmnd *)hostdata->connected);
+         hostdata->connected->SCp.phase = PHASE_CMDOUT;
+         AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+         sti();
+         return; }
+      }
+     else {
+      cli();
+      AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+      sti();
+      return; }
+   }
+    
+if (instreg & INSTREG_SR) {
+   DEB_INTR(printk("Service request interrupt received, "));
+   if (hostdata->connected) {
+      DEB_INTR(printk("calling information_transfer\n"));
+      cli();
+      AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+      sti(); }
+     else {
+      printk("scsi%d: weird: service request when no command connected\n", instance->host_no);
+      AM53C974_write_8(CMDREG, CMDREG_CFIFO); }   /* clear FIFO */
+   return;
+   }
+
+EXIT:
+  DEB_INTR(printk("intr: starting main\n"));
+  run_main();
+  DEB_INTR(printk("end of intr\n"));
+}
+
+/************************************************************************** 
+* Function : AM53C974_intr_disconnect(struct Scsi_Host *instance)
+*
+* Purpose : manage target disconnection
+*
+* Inputs : instance -- which AM53C974
+* 
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_disconnect(struct Scsi_Host *instance) 
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd                *cmd;
+AM53C974_setio(instance);
+
+if (hostdata->sel_cmd != NULL) {
+   /* normal selection timeout, typical for nonexisting targets */
+   cmd = (Scsi_Cmnd *)hostdata->sel_cmd;
+   DEB_INTR(printk("bad target\n"));
+   cmd->result = DID_BAD_TARGET << 16;
+   goto EXIT_FINISHED; }
+
+if (!hostdata->connected) {
+   /* can happen if controller was reset, a device tried to reconnect,
+      failed and disconnects now */
+   AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+   return; }
+
+if (hostdata->disconnecting) {
+   /* target sent disconnect message, so we are prepared */
+   cmd = (Scsi_Cmnd *)hostdata->connected;
+   AM53C974_set_async(instance, cmd->target);
+   DEB_INTR(printk("scsi%d : disc. from cmnd %d for ta %d, lun %d\n",
+                  instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+   if (cmd->device->disconnect) {
+      /* target wants to reselect later */
+      DEB_INTR(printk("ok, re-enabling selection\n"));
+      LIST(cmd,hostdata->disconnected_queue);
+      cmd->host_scribble = (unsigned char *)hostdata->disconnected_queue;
+      hostdata->disconnected_queue = cmd;
+      DEB_QUEUE(printk("scsi%d : command for target %d lun %d this %d was moved from connected to"
+                      "  the disconnected_queue\n", instance->host_no, cmd->target,
+                        cmd->lun, hostdata->disconnected_queue->SCp.this_residual));
+      DEB_QUEUE(AM53C974_print_queues(instance));
+      goto EXIT_UNFINISHED; }
+     else {
+      /* target does not want to reselect later, we are really finished */
+#ifdef AM53C974_DEBUG
+      if (cmd->cmnd[0] == REQUEST_SENSE) {
+        int i;
+        printk("Request sense data dump:\n");
+        for (i = 0; i < cmd->request_bufflen; i++) {
+             printk("%02x ", *((char *)(cmd->request_buffer) + i));
+             if (i && !(i % 16)) printk("\n"); }
+        printk("\n"); }
+#endif
+      goto EXIT_FINISHED; } /* !cmd->device->disconnect */
+   } /* if (hostdata->disconnecting) */
+
+/* no disconnect message received; unexpected disconnection */
+cmd = (Scsi_Cmnd *)hostdata->connected;
+if (cmd) {
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   AM53C974_set_async(instance, cmd->target);
+   printk("scsi%d: Unexpected disconnect; phase: %d; target: %d; this_residual: %d; buffers_residual: %d; message: %d\n",
+           instance->host_no, cmd->SCp.phase, cmd->target, cmd->SCp.this_residual, cmd->SCp.buffers_residual,
+           cmd->SCp.Message);
+   printk("cmdreg: 0x%02x; statreg: 0x%02x; isreg: 0x%02x; cfifo: 0x%02x\n",
+          AM53C974_read_8(CMDREG), AM53C974_read_8(STATREG), AM53C974_read_8(ISREG),
+           AM53C974_read_8(CFIREG) & CFIREG_CF);
+
+    if ((hostdata->last_message[0] == EXTENDED_MESSAGE) && 
+        (hostdata->last_message[2] == EXTENDED_SDTR)) {
+        /* sync. negotiation was aborted, setup asynchronous transfer with target */
+        hostdata->sync_off[cmd->target] = 0; }
+   if (hostdata->aborted || hostdata->msgout[0] == ABORT)
+      cmd->result = DID_ABORT << 16;
+     else
+      cmd->result = DID_ERROR << 16;
+   goto EXIT_FINISHED; }
+
+EXIT_FINISHED:
+hostdata->aborted = 0;
+hostdata->msgout[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n", 
+       (long)hostdata->issue_queue, (long)hostdata->disconnected_queue));
+cmd->scsi_done(cmd);
+
+if (!hostdata->selecting) {
+   AM53C974_set_async(instance, cmd->target); 
+   AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */
+return;
+
+EXIT_UNFINISHED:
+hostdata->msgout[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n", 
+       (long)hostdata->issue_queue, (long)hostdata->disconnected_queue));
+if (!hostdata->selecting) {
+   AM53C974_set_async(instance, cmd->target); 
+   AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */
+return;
+}
+
+/************************************************************************** 
+* Function : int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg)
+*
+* Purpose : setup message string for sync. negotiation
+*
+* Inputs : instance -- which AM53C974
+*          target -- which SCSI target to deal with
+*          msg -- input message string
+* 
+* Returns : 0 if parameters accepted or 1 if not accepted
+*
+* Side effects: hostdata is changed
+*
+* Note: we assume here that fastclk is enabled
+**************************************************************************/
+static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg) 
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+int                      period, offset, i, rate, rate_rem;
+AM53C974_setio(instance);
+
+period = (DEF_CLK * msg[3] * 8 + 1000) / 2000;
+if (period < MIN_PERIOD) {
+    period = MIN_PERIOD;
+    hostdata->msgout[3] = period / 4; }
+   else
+    if (period > MAX_PERIOD) {
+       period = MAX_PERIOD;
+       hostdata->msgout[3] = period / 4; }
+      else
+       hostdata->msgout[3] = msg[3];
+offset = msg[4]; 
+if (offset > MAX_OFFSET) offset = MAX_OFFSET;
+hostdata->msgout[4] = offset;
+hostdata->sync_per[target] = period;
+hostdata->sync_off[target] = offset;
+for (i = 0; i < 3; i++) hostdata->msgout[i] = msg[i];
+if ((hostdata->msgout[3] != msg[3]) || (msg[4] != offset)) return(1);
+
+rate = DEF_CLK / period;
+rate_rem = 10 * (DEF_CLK - period * rate) / period;
+
+if (offset)
+   printk("\ntarget %d: rate=%d.%d Mhz, synchronous, sync offset=%d bytes\n",
+          target, rate, rate_rem, offset);
+  else
+   printk("\ntarget %d: rate=%d.%d Mhz, asynchronous\n", target, rate, rate_rem);
+
+return(0);
+}
+
+/************************************************************************** 
+* Function : AM53C974_set_async(struct Scsi_Host *instance, int target)
+*
+* Purpose : put controller into async. mode
+*
+* Inputs : instance -- which AM53C974
+*          target -- which SCSI target to deal with
+* 
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+AM53C974_write_8(STPREG, hostdata->sync_per[target]);
+AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+}
+
+/************************************************************************** 
+* Function : AM53C974_set_sync(struct Scsi_Host *instance, int target)
+*
+* Purpose : put controller into sync. mode
+*
+* Inputs : instance -- which AM53C974
+*          target -- which SCSI target to deal with
+* 
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+AM53C974_write_8(STPREG, hostdata->sync_per[target]);
+AM53C974_write_8(SOFREG, (SOFREG_SO & hostdata->sync_off[target]) | 
+                (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+}
+
+/***********************************************************************
+* Function : AM53C974_information_transfer(struct Scsi_Host *instance, *
+*                          unsigned char statreg, unsigned char isreg, *
+*                         unsigned char instreg, unsigned char cfifo,  *
+*                         unsigned char dmastatus)                     *
+*                                                                      *
+* Purpose : handle phase changes                                       *
+*                                                                      *
+* Inputs : instance - which AM53C974                                   *
+*          statreg - stus register                                     *
+*          isreg - internal state register                             *
+*          instreg - interrupt status register                         *
+*          cfifo - number of bytes in FIFO                             *
+*          dmastatus - dma status register                             *
+*                                                                      *
+* Returns : nothing                                                    *
+************************************************************************/
+static void AM53C974_information_transfer(struct Scsi_Host *instance, 
+                                          unsigned char statreg, unsigned char isreg,
+                                          unsigned char instreg, unsigned char cfifo,
+                                          unsigned char dmastatus)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd                *cmd = (Scsi_Cmnd *)hostdata->connected;
+int                      ret, i, len, residual=-1;
+AM53C974_setio(instance);
+
+DEB_INFO(printk(SEPARATOR_LINE));
+switch (statreg & STATREG_PHASE) {     /* scsi phase */
+  case PHASE_DATAOUT:
+    DEB_INFO(printk("Dataout phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
+                    (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+    cmd->SCp.phase = PHASE_DATAOUT;
+    goto PHASE_DATA_IO;
+
+  case PHASE_DATAIN:
+    DEB_INFO(printk("Datain phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
+                     (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+    cmd->SCp.phase = PHASE_DATAIN;
+    PHASE_DATA_IO:
+    if (hostdata->aborted) {
+       AM53C974_write_8(DMACMD, DMACMD_IDLE);
+       AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+       AM53C974_write_8(CMDREG, CMDREG_SATN);
+       return; }
+    if ((!cmd->SCp.this_residual) && cmd->SCp.buffers_residual) {
+       cmd->SCp.buffer++;
+       cmd->SCp.buffers_residual--;
+       cmd->SCp.ptr = (unsigned char *)cmd->SCp.buffer->address;
+       cmd->SCp.this_residual = cmd->SCp.buffer->length; }
+    if (cmd->SCp.this_residual) {
+       if (!(AM53C974_read_8(DMACMD) & DMACMD_START)) {
+          hostdata->dma_busy = 0;
+          AM53C974_transfer_dma(instance, statreg & STATREG_IO,
+                                (unsigned long)cmd->SCp.this_residual,
+                                cmd->SCp.ptr); }
+         else
+          hostdata->dma_busy = 1;
+       }
+    return;
+
+  case PHASE_MSGIN:
+    DEB_INFO(printk("Message-In phase; cmd=0x%lx, sel_cmd=0x%lx\n",
+                    (long)hostdata->connected, (long)hostdata->sel_cmd));
+    AM53C974_set_async(instance, cmd->target);
+    if (cmd->SCp.phase == PHASE_DATAIN)
+       AM53C974_dma_blast(instance, dmastatus, statreg);
+    if ((cmd->SCp.phase == PHASE_DATAOUT) && (AM53C974_read_8(DMACMD) & DMACMD_START)) {
+       AM53C974_write_8(DMACMD, DMACMD_IDLE);
+       residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+                          (AM53C974_read_8(CTCHREG) << 16));
+       cmd->SCp.ptr += cmd->SCp.this_residual - residual;
+       cmd->SCp.this_residual = residual;
+       if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; }
+       }
+    if (cmd->SCp.phase == PHASE_STATIN) {
+       while ((AM53C974_read_8(CFIREG) & CFIREG_CF) < 2) ;
+       cmd->SCp.Status = AM53C974_read_8(FFREG);
+       cmd->SCp.Message = AM53C974_read_8(FFREG); 
+       DEB_INFO(printk("Message-In phase; status=0x%02x, message=0x%02x\n",
+                       cmd->SCp.Status, cmd->SCp.Message));
+       ret = AM53C974_message(instance, cmd, cmd->SCp.Message); }
+      else {
+       if (!cfifo) {
+          AM53C974_write_8(CMDREG, CMDREG_IT); 
+          AM53C974_poll_int();
+          cmd->SCp.Message = AM53C974_read_8(FFREG);
+          }
+       ret = AM53C974_message(instance, cmd, cmd->SCp.Message);
+       }
+    cmd->SCp.phase = PHASE_MSGIN;
+    AM53C974_set_sync(instance, cmd->target);
+    break;
+  case PHASE_MSGOUT:
+    DEB_INFO(printk("Message-Out phase; cfifo=%d; msgout[0]=0x%02x\n",
+                    AM53C974_read_8(CFIREG) & CFIREG_CF, hostdata->msgout[0]));
+    AM53C974_write_8(DMACMD, DMACMD_IDLE);
+    AM53C974_set_async(instance, cmd->target);
+    for (i = 0; i < sizeof(hostdata->last_message); i++) 
+        hostdata->last_message[i] = hostdata->msgout[i];
+    if ((hostdata->msgout[0] == 0) || INSIDE(hostdata->msgout[0], 0x02, 0x1F) || 
+        INSIDE(hostdata->msgout[0], 0x80, 0xFF)) 
+       len = 1;
+      else {
+       if (hostdata->msgout[0] == EXTENDED_MESSAGE) {
+#ifdef AM53C974_DEBUG_INFO
+          printk("Extended message dump:\n");
+          for (i = 0; i < hostdata->msgout[1] + 2; i++) {
+              printk("%02x ", hostdata->msgout[i]);
+              if (i && !(i % 16)) printk("\n"); }
+          printk("\n");
+#endif
+          len = hostdata->msgout[1] + 2; }
+         else
+          len = 2;
+       }
+    for (i = 0; i < len; i++) AM53C974_write_8(FFREG, hostdata->msgout[i]);
+    AM53C974_write_8(CMDREG, CMDREG_IT);
+    cmd->SCp.phase = PHASE_MSGOUT;
+    hostdata->msgout[0] = NOP;
+    AM53C974_set_sync(instance, cmd->target);
+    break;
+
+  case PHASE_CMDOUT:
+    DEB_INFO(printk("Command-Out phase\n"));
+    AM53C974_set_async(instance, cmd->target);
+    for (i = 0; i < cmd->cmd_len; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+    AM53C974_write_8(CMDREG, CMDREG_IT);
+    cmd->SCp.phase = PHASE_CMDOUT;
+    AM53C974_set_sync(instance, cmd->target);
+    break;
+
+  case PHASE_STATIN:
+    DEB_INFO(printk("Status phase\n"));
+    if (cmd->SCp.phase == PHASE_DATAIN)
+       AM53C974_dma_blast(instance, dmastatus, statreg);
+    AM53C974_set_async(instance, cmd->target);
+    if (cmd->SCp.phase == PHASE_DATAOUT) {
+       unsigned long residual;
+
+       if (AM53C974_read_8(DMACMD) & DMACMD_START) {
+          AM53C974_write_8(DMACMD, DMACMD_IDLE);
+          residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+                              (AM53C974_read_8(CTCHREG) << 16));
+          cmd->SCp.ptr += cmd->SCp.this_residual - residual;
+          cmd->SCp.this_residual = residual; }
+       if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; }
+       }
+    cmd->SCp.phase = PHASE_STATIN;
+    AM53C974_write_8(CMDREG, CMDREG_ICCS);  /* command complete */
+    break;
+
+  case PHASE_RES_0:
+  case PHASE_RES_1:
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+    DEB_INFO(printk("Reserved phase\n"));
+    break;
+  }
+KEYWAIT();
+}
+
+/******************************************************************************
+* Function : int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+*                                 unsigned char msg)                
+*
+* Purpose : handle SCSI messages
+*
+* Inputs : instance -- which AM53C974
+*          cmd -- SCSI command the message belongs to
+*          msg -- message id byte
+* 
+* Returns : 1 on success, 0 on failure.
+**************************************************************************/
+static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+                            unsigned char msg)
+{
+AM53C974_local_declare(); 
+static unsigned char     extended_msg[10];
+unsigned char            statreg;
+int                      len, ret = 0;
+unsigned char            *p;
+#ifdef AM53C974_DEBUG_MSG
+int                      j;
+#endif
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+DEB_MSG(printk(SEPARATOR_LINE));
+
+/* Linking lets us reduce the time required to get the 
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by 
+ * next_link, done() is called as with unlinked commands. */
+switch (msg) {
+#ifdef LINKED
+  case LINKED_CMD_COMPLETE:
+  case LINKED_FLG_CMD_COMPLETE:
+    /* Accept message by releasing ACK */
+    DEB_LINKED(printk("scsi%d : target %d lun %d linked command complete.\n",
+                       instance->host_no, cmd->target, cmd->lun));
+    /* Sanity check : A linked command should only terminate with
+     * one of these messages if there are more linked commands available. */
+    if (!cmd->next_link) {
+       printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
+              instance->host_no, cmd->target, cmd->lun);
+       hostdata->aborted = 1;
+       AM53C974_write_8(CMDREG, CMDREG_SATN);
+       AM53C974_write_8(CMDREG, CMDREG_MA);
+       break; }
+    if (hostdata->aborted) {
+       DEB_ABORT(printk("ATN set for cmnd %d upon reception of LINKED_CMD_COMPLETE or"
+                        "LINKED_FLG_CMD_COMPLETE message\n", cmd->cmnd[0]));
+       AM53C974_write_8(CMDREG, CMDREG_SATN); }
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+
+    initialize_SCp(cmd->next_link);
+    /* The next command is still part of this process */
+    cmd->next_link->tag = cmd->tag;
+    cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
+    DEB_LINKED(printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
+                     instance->host_no, cmd->target, cmd->lun));
+    cmd->scsi_done(cmd);
+    cmd = hostdata->connected;
+    break;
+
+#endif /* def LINKED */
+
+  case ABORT:
+  case COMMAND_COMPLETE: 
+    DEB_MSG(printk("scsi%d: command complete message received; cmd %d for target %d, lun %d\n",
+                  instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+    hostdata->disconnecting = 1;
+    cmd->device->disconnect = 0;
+
+    /* I'm not sure what the correct thing to do here is : 
+     * 
+     * If the command that just executed is NOT a request 
+     * sense, the obvious thing to do is to set the result
+     * code to the values of the stored parameters.
+     * If it was a REQUEST SENSE command, we need some way 
+     * to differentiate between the failure code of the original
+     * and the failure code of the REQUEST sense - the obvious
+     * case is success, where we fall through and leave the result
+     * code unchanged.
+     * 
+     * The non-obvious place is where the REQUEST SENSE failed  */
+    if (cmd->cmnd[0] != REQUEST_SENSE) 
+       cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
+      else if (cmd->SCp.Status != GOOD)
+       cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);                 
+    if (hostdata->aborted) {
+       AM53C974_write_8(CMDREG, CMDREG_SATN);
+       AM53C974_write_8(CMDREG, CMDREG_MA);
+       DEB_ABORT(printk("ATN set for cmnd %d upon reception of ABORT or"
+                        "COMMAND_COMPLETE message\n", cmd->cmnd[0]));
+       break; }
+    if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) {
+       DEB_MSG(printk("scsi%d : performing request sense\n", instance->host_no));
+       cmd->cmnd[0] = REQUEST_SENSE;
+       cmd->cmnd[1] &= 0xe0;
+       cmd->cmnd[2] = 0;
+       cmd->cmnd[3] = 0;
+       cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+       cmd->cmnd[5] = 0;
+       cmd->SCp.buffer = NULL;
+       cmd->SCp.buffers_residual = 0;
+       cmd->SCp.ptr = (char *)cmd->sense_buffer;
+       cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+       LIST(cmd,hostdata->issue_queue);
+       cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+       hostdata->issue_queue = (Scsi_Cmnd *)cmd;
+       DEB_MSG(printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no));
+       }
+
+    /* Accept message by clearing ACK */
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+    break;
+               
+  case MESSAGE_REJECT:
+    DEB_MSG(printk("scsi%d: reject message received; cmd %d for target %d, lun %d\n",
+                  instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+    switch (hostdata->last_message[0]) {
+      case EXTENDED_MESSAGE:
+        if (hostdata->last_message[2] == EXTENDED_SDTR) {
+           /* sync. negotiation was rejected, setup asynchronous transfer with target */
+           printk("\ntarget %d: rate=%d Mhz, asynchronous (sync. negotiation rejected)\n",
+                  cmd->target,  DEF_CLK / DEF_STP);
+           hostdata->sync_off[cmd->target] = 0;
+           hostdata->sync_per[cmd->target] = DEF_STP; }
+        break;
+      case HEAD_OF_QUEUE_TAG:
+      case ORDERED_QUEUE_TAG:
+      case SIMPLE_QUEUE_TAG:
+        cmd->device->tagged_queue = 0;
+        hostdata->busy[cmd->target] |= (1 << cmd->lun);
+        break;
+      default:
+        break;
+      }
+    if (hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+    break;
+       
+  case DISCONNECT:
+    DEB_MSG(printk("scsi%d: disconnect message received; cmd %d for target %d, lun %d\n",
+                  instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+    cmd->device->disconnect = 1;
+    hostdata->disconnecting = 1;
+    AM53C974_write_8(CMDREG, CMDREG_MA); /* Accept message by clearing ACK */
+    break;
+               
+  case SAVE_POINTERS:
+  case RESTORE_POINTERS:
+    DEB_MSG(printk("scsi%d: save/restore pointers message received; cmd %d for target %d, lun %d\n",
+                  instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+    /* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+     * operation, in violation of the SCSI spec so we can safely 
+     * ignore SAVE/RESTORE pointers calls.
+     *
+     * Unfortunately, some disks violate the SCSI spec and 
+     * don't issue the required SAVE_POINTERS message before
+     * disconnecting, and we have to break spec to remain 
+     * compatible. */
+    if (hostdata->aborted) {
+       DEB_ABORT(printk("ATN set for cmnd %d upon reception of SAVE/REST. POINTERS message\n",
+                         cmd->cmnd[0]));
+       AM53C974_write_8(CMDREG, CMDREG_SATN); }
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+    break;
+
+  case EXTENDED_MESSAGE:
+    DEB_MSG(printk("scsi%d: extended message received; cmd %d for target %d, lun %d\n",
+                  instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+   /* Extended messages are sent in the following format :
+    * Byte     
+    * 0                  EXTENDED_MESSAGE == 1
+    * 1                  length (includes one byte for code, doesn't include first two bytes)
+    * 2          code
+    * 3..length+1 arguments
+    */
+   /* BEWARE!! THIS CODE IS EXTREMELY UGLY */
+    extended_msg[0] = EXTENDED_MESSAGE;
+    AM53C974_read_8(INSTREG) ; /* clear int */
+    AM53C974_write_8(CMDREG, CMDREG_MA);  /* ack. msg byte, then wait for SO */  
+    AM53C974_poll_int();
+    /* get length */
+    AM53C974_write_8(CMDREG, CMDREG_IT); 
+    AM53C974_poll_int();
+    AM53C974_write_8(CMDREG, CMDREG_MA);  /* ack. msg byte, then wait for SO */   
+    AM53C974_poll_int();
+    extended_msg[1] = len = AM53C974_read_8(FFREG); /* get length */
+    p = extended_msg+2;
+    /* read the remaining (len) bytes */
+    while (len) {
+      AM53C974_write_8(CMDREG, CMDREG_IT); 
+      AM53C974_poll_int();
+      if (len > 1) {
+         AM53C974_write_8(CMDREG, CMDREG_MA);  /* ack. msg byte, then wait for SO */   
+         AM53C974_poll_int(); }
+      *p = AM53C974_read_8(FFREG);
+      p++; len--; }
+
+#ifdef AM53C974_DEBUG_MSG
+    printk("scsi%d: received extended message: ", instance->host_no);
+    for (j = 0; j < extended_msg[1] + 2; j++) {
+        printk("0x%02x ", extended_msg[j]);
+        if (j && !(j % 16)) printk("\n"); }
+    printk("\n");
+#endif
+
+    /* check message */
+    if (extended_msg[2] == EXTENDED_SDTR)
+       ret = AM53C974_sync_neg(instance, cmd->target, extended_msg);
+    if (ret || hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
+
+    AM53C974_write_8(CMDREG, CMDREG_MA); 
+    break;
+
+  default:
+    printk("scsi%d: unknown message 0x%02x received\n",instance->host_no, msg);
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+    /* reject message */
+    hostdata->msgout[0] = MESSAGE_REJECT; 
+    AM53C974_write_8(CMDREG, CMDREG_SATN);
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+    return(0);
+    break;
+
+  } /* switch (msg) */
+KEYWAIT();
+return(1);
+}
+
+/************************************************************************** 
+* Function : AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+*
+* Purpose : try to establish nexus for the command;
+*           start sync negotiation via start stop and transfer the command in 
+*           cmdout phase in case of an inquiry or req. sense command with no 
+*           sync. neg. performed yet
+*
+* Inputs : instance -- which AM53C974
+*          cmd -- command which requires the selection
+*          tag -- tagged queueing
+* 
+* Returns : nothing
+*        
+* Note: this function initializes the selection process, which is continued 
+*       in the interrupt handler
+**************************************************************************/
+static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned char            cfifo, tmp[3];
+unsigned int             i, len, cmd_size = COMMAND_SIZE(cmd->cmnd[0]);
+AM53C974_setio(instance);
+
+cfifo = AM53C974_cfifo();
+if (cfifo) {
+   printk("scsi%d: select error; %d residual bytes in FIFO\n", instance->host_no, cfifo);
+   AM53C974_write_8(CMDREG, CMDREG_CFIFO); /* clear FIFO */
+   }                  
+
+tmp[0] = IDENTIFY(1, cmd->lun);
+
+#ifdef SCSI2
+if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
+   tmp[1] = SIMPLE_QUEUE_TAG;
+   if (tag == TAG_NEXT) {
+      /* 0 is TAG_NONE, used to imply no tag for this command */
+      if (cmd->device->current_tag == 0) cmd->device->current_tag = 1;
+      cmd->tag = cmd->device->current_tag;
+      cmd->device->current_tag++; }
+     else  
+      cmd->tag = (unsigned char)tag;
+   tmp[2] = cmd->tag;
+   hostdata->last_message[0] = SIMPLE_QUEUE_TAG;
+   len = 3;
+   AM53C974_write_8(FFREG, tmp[0]);
+   AM53C974_write_8(FFREG, tmp[1]);
+   AM53C974_write_8(FFREG, tmp[2]);
+   }
+  else
+#endif /* def SCSI2 */
+   {
+   len = 1;
+   AM53C974_write_8(FFREG, tmp[0]);
+   cmd->tag = 0; }
+
+/* in case of an inquiry or req. sense command with no sync. neg performed yet, we start
+   sync negotiation via start stops and transfer the command in cmdout phase */
+if (((cmd->cmnd[0] == INQUIRY) || (cmd->cmnd[0] == REQUEST_SENSE)) &&
+    !(hostdata->sync_neg[cmd->target]) && hostdata->sync_en[cmd->target]) {
+   hostdata->sync_neg[cmd->target] = 1;
+   hostdata->msgout[0] = EXTENDED_MESSAGE;
+   hostdata->msgout[1] = 3;
+   hostdata->msgout[2] = EXTENDED_SDTR;
+   hostdata->msgout[3] = 250 / (int)hostdata->max_rate[cmd->target];
+   hostdata->msgout[4] = hostdata->max_offset[cmd->target];
+   len += 5; }
+
+AM53C974_write_8(SDIDREG, SDIREG_MASK & cmd->target);       /* setup dest. id  */
+AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT);                /* setup timeout reg */
+switch (len) {
+  case 1:
+   for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+   AM53C974_write_8(CMDREG, CMDREG_SAS);                    /* select with ATN, 1 msg byte */
+   hostdata->msgout[0] = NOP;
+   break;
+  case 3:
+   for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+   AM53C974_write_8(CMDREG, CMDREG_SA3S);                   /* select with ATN, 3 msg bytes */
+   hostdata->msgout[0] = NOP;
+   break;
+  default:
+   AM53C974_write_8(CMDREG, CMDREG_SASS);   /* select with ATN, stop steps; continue in message out phase */
+   break;
+  }
+}
+
+/************************************************************************** 
+* Function : AM53C974_intr_select(struct Scsi_Host *instance, unsigned char statreg)
+*
+* Purpose : handle reselection 
+*
+* Inputs : instance -- which AM53C974
+*          statreg -- status register
+* 
+* Returns : nothing
+*
+* side effects: manipulates hostdata
+**************************************************************************/
+static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned char            cfifo, msg[3], lun, t, target = 0;
+#ifdef SCSI2
+ unsigned                char tag;
+#endif
+Scsi_Cmnd                *tmp = NULL, *prev;
+AM53C974_setio(instance);
+
+cfifo = AM53C974_cfifo();
+
+if (hostdata->selecting) {
+   /* caught reselect interrupt in selection process;
+      put selecting command back into the issue queue and continue with the
+      reselecting command */
+   DEB_RESEL(printk("AM53C974_intr_reselect: in selection process\n"));
+   LIST(hostdata->sel_cmd, hostdata->issue_queue);
+   hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+   hostdata->issue_queue = hostdata->sel_cmd;
+   hostdata->sel_cmd = NULL;
+   hostdata->selecting = 0; }
+
+/* 2 bytes must be in the FIFO now */
+if (cfifo != 2) {
+   printk("scsi %d: error: %d bytes in fifo, 2 expected\n", instance->host_no, cfifo);
+   hostdata->aborted = 1;
+   goto EXIT_ABORT; }
+
+/* determine target which reselected */
+t = AM53C974_read_8(FFREG);
+if (!(t & (1 << instance->this_id))) {
+   printk("scsi %d: error: invalid host id\n", instance->host_no);
+   hostdata->aborted = 1;
+   goto EXIT_ABORT; }
+t ^= (1 << instance->this_id);
+target = 0; while (t != 1) { t >>= 1; target++; }
+DEB_RESEL(printk("scsi %d: reselect; target: %d\n", instance->host_no, target));
+      
+if (hostdata->aborted) goto EXIT_ABORT;
+
+if ((statreg & STATREG_PHASE) != PHASE_MSGIN) {
+   printk("scsi %d: error: upon reselection interrupt not in MSGIN\n", instance->host_no);
+   hostdata->aborted = 1;
+   goto EXIT_ABORT; }
+
+msg[0] = AM53C974_read_8(FFREG);
+if (!msg[0] & 0x80) {
+   printk("scsi%d: error: expecting IDENTIFY message, got ", instance->host_no);
+   print_msg(msg);
+   hostdata->aborted = 1;
+   goto EXIT_ABORT; }
+
+lun = (msg[0] & 0x07);
+
+/* We need to add code for SCSI-II to track which devices have
+ * I_T_L_Q nexuses established, and which have simple I_T_L
+ * nexuses so we can chose to do additional data transfer. */
+#ifdef SCSI2
+#error "SCSI-II tagged queueing is not supported yet"
+#endif
+
+/* Find the command corresponding to the I_T_L or I_T_L_Q  nexus we 
+ * just reestablished, and remove it from the disconnected queue. */
+for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue, prev = NULL; 
+     tmp; prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) 
+    if ((target == tmp->target) && (lun == tmp->lun) 
+#ifdef SCSI2
+         && (tag == tmp->tag) 
+#endif
+       ) {
+       if (prev) {
+         REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
+                 (Scsi_Cmnd *)(tmp->host_scribble));
+         prev->host_scribble = tmp->host_scribble; } 
+         else {
+         REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
+         hostdata->disconnected_queue = (Scsi_Cmnd *)tmp->host_scribble; }
+       tmp->host_scribble = NULL;
+       hostdata->connected = tmp;
+       break; }
+
+if (!tmp) {
+#ifdef SCSI2
+   printk("scsi%d: warning : target %d lun %d tag %d not in disconnect_queue.\n",
+         instance->host_no, target, lun, tag);
+#else
+   printk("scsi%d: warning : target %d lun %d not in disconnect_queue.\n",
+         instance->host_no, target, lun);
+#endif
+   /* Since we have an established nexus that we can't do anything with, we must abort it. */
+   hostdata->aborted = 1;
+   DEB(AM53C974_keywait());
+   goto EXIT_ABORT; }
+  else
+   goto EXIT_OK;
+
+EXIT_ABORT: 
+AM53C974_write_8(CMDREG, CMDREG_SATN);
+AM53C974_write_8(CMDREG, CMDREG_MA);
+return;
+
+EXIT_OK:
+DEB_RESEL(printk("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+         instance->host_no, target, tmp->lun, tmp->tag));
+AM53C974_set_sync(instance, target);
+AM53C974_write_8(SDIDREG, SDIREG_MASK & target);       /* setup dest. id  */
+AM53C974_write_8(CMDREG, CMDREG_MA);
+hostdata->dma_busy = 0;
+hostdata->connected->SCp.phase = PHASE_CMDOUT;
+}
+
+/************************************************************************** 
+* Function : AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+*                                  unsigned long length, char *data)
+*
+* Purpose : setup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+*          dir -- direction flag, 0: write to device, read from memory; 
+*                                 1: read from device, write to memory
+*          length -- number of bytes to transfer to from buffer
+*          data -- pointer to data buffer
+* 
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+                                             unsigned long length, char *data)
+{
+AM53C974_local_declare();
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D);  /* idle command */
+AM53C974_write_8(STCLREG, (unsigned char)(length & 0xff));
+AM53C974_write_8(STCMREG, (unsigned char)((length & 0xff00) >> 8));
+AM53C974_write_8(STCHREG, (unsigned char)((length & 0xff0000) >> 16));
+AM53C974_write_32(DMASTC, length & 0xffffff);
+AM53C974_write_32(DMASPA, (unsigned long)data);
+AM53C974_write_8(CMDREG, CMDREG_IT | CMDREG_DMA);
+AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D | DMACMD_START);
+}
+
+/************************************************************************** 
+* Function : AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+*                               unsigned char statreg)
+*
+* Purpose : cleanup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+*          dmastatus -- dma status register
+*          statreg -- status register
+* 
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+                               unsigned char statreg)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned long            ctcreg;
+int                      dir = statreg & STATREG_IO;
+int                      cfifo, pio, i = 0;
+AM53C974_setio(instance);
+
+do {
+   cfifo = AM53C974_cfifo();
+   i++;
+   } while (cfifo && (i < 50000));
+pio = (i == 50000) ? 1: 0;
+
+if (statreg & STATREG_CTZ) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; }
+
+if (dmastatus & DMASTATUS_DONE) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; }
+
+AM53C974_write_8(DMACMD, ((dir << 7) & DMACMD_DIR) | DMACMD_BLAST);
+while(!(AM53C974_read_8(DMASTATUS) & DMASTATUS_BCMPLT)) ;
+AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+if (pio) {
+   /* transfer residual bytes via PIO */
+   unsigned char *wac = (unsigned char *)AM53C974_read_32(DMAWAC);
+   printk("pio mode, residual=%d\n", AM53C974_read_8(CFIREG) & CFIREG_CF);
+   while (AM53C974_read_8(CFIREG) & CFIREG_CF) *(wac++) = AM53C974_read_8(FFREG);
+   }
+
+ctcreg = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+         (AM53C974_read_8(CTCHREG) << 16);
+
+hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - ctcreg;
+hostdata->connected->SCp.this_residual = ctcreg;
+}
+
+/************************************************************************** 
+* Function : AM53C974_intr_bus_reset(struct Scsi_Host *instance)
+*
+* Purpose : handle bus reset interrupt
+*
+* Inputs : instance -- which AM53C974
+* 
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_bus_reset(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned char cntlreg1;
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+
+cntlreg1 = AM53C974_read_8(CNTLREG1);
+AM53C974_write_8(CNTLREG1, cntlreg1 | CNTLREG1_DISR);
+}
+
+/**************************************************************************
+* Function : int AM53C974_abort(Scsi_Cmnd *cmd)
+*
+* Purpose : abort a command
+*
+* Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the 
+*      host byte of the result field to, if zero DID_ABORTED is 
+*      used.
+*
+* Returns : 0 - success, -1 on failure.
+ **************************************************************************/
+int AM53C974_abort(Scsi_Cmnd *cmd)
+{
+AM53C974_local_declare();
+struct Scsi_Host         *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd                *tmp, **prev;
+
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+cli();
+AM53C974_setio(instance);
+
+DEB_ABORT(printk(SEPARATOR_LINE));
+DEB_ABORT(printk("scsi%d : AM53C974_abort called -- trouble starts!!\n", instance->host_no));
+DEB_ABORT(AM53C974_print(instance));
+DEB_ABORT(AM53C974_keywait());
+
+/* Case 1 : If the command is the currently executing command, 
+            we'll set the aborted flag and return control so that the
+            information transfer routine can exit cleanly. */
+if ((hostdata->connected == cmd) || (hostdata->sel_cmd == cmd)) {
+   DEB_ABORT(printk("scsi%d: aborting connected command\n", instance->host_no));
+   hostdata->aborted = 1;
+   hostdata->msgout[0] = ABORT;
+   sti();
+   return(SCSI_ABORT_PENDING); }
+
+/* Case 2 : If the command hasn't been issued yet,
+            we simply remove it from the issue queue. */
+for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue), 
+     tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp; 
+     prev = (Scsi_Cmnd **)&(tmp->host_scribble),
+     tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+    if (cmd == tmp) {
+       DEB_ABORT(printk("scsi%d : abort removed command from issue queue.\n", instance->host_no));
+       REMOVE(5, *prev, tmp, tmp->host_scribble);
+       (*prev) = (Scsi_Cmnd *)tmp->host_scribble;
+       tmp->host_scribble = NULL;
+       tmp->result = DID_ABORT << 16;
+       sti();
+       tmp->done(tmp);
+       return(SCSI_ABORT_SUCCESS); }
+#ifdef AM53C974_DEBUG_ABORT
+      else {
+       if (prev == (Scsi_Cmnd **)tmp) 
+          printk("scsi%d : LOOP\n", instance->host_no); 
+       }
+#endif
+    }
+/* Case 3 : If any commands are connected, we're going to fail the abort
+ *         and let the high level SCSI driver retry at a later time or 
+ *         issue a reset.
+ *
+ *         Timeouts, and therefore aborted commands, will be highly unlikely
+ *          and handling them cleanly in this situation would make the common
+ *         case of noresets less efficient, and would pollute our code.  So,
+ *         we fail. */
+if (hostdata->connected || hostdata->sel_cmd) {
+   DEB_ABORT(printk("scsi%d : abort failed, other command connected.\n", instance->host_no));
+   sti();
+   return(SCSI_ABORT_NOT_RUNNING); }
+
+/* Case 4: If the command is currently disconnected from the bus, and 
+ *        there are no connected commands, we reconnect the I_T_L or 
+ *        I_T_L_Q nexus associated with it, go into message out, and send 
+ *         an abort message. */
+for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue; tmp; 
+     tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+     if (cmd == tmp) {
+        DEB_ABORT(printk("scsi%d: aborting disconnected command\n", instance->host_no));
+        hostdata->aborted = 1;
+        hostdata->msgout[0] = ABORT;
+        hostdata->selecting = 1;
+        hostdata->sel_cmd = tmp;
+        AM53C974_write_8(CMDREG, CMDREG_DSR);
+        sti();
+        return(SCSI_ABORT_PENDING); }
+     }
+
+/* Case 5 : If we reached this point, the command was not found in any of 
+ *         the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke. */
+DEB_ABORT(printk("scsi%d : abort failed, command not found.\n", instance->host_no));
+sti();
+return(SCSI_ABORT_NOT_RUNNING);
+}
+
+/************************************************************************** 
+* Function : int AM53C974_reset(Scsi_Cmnd *cmd)
+*
+* Purpose : reset the SCSI controller and bus
+*
+* Inputs : cmd -- which command within the command block was responsible for the reset
+* 
+* Returns : status (SCSI_ABORT_SUCCESS)
+**************************************************************************/
+int AM53C974_reset(Scsi_Cmnd *cmd)
+{
+AM53C974_local_declare();
+int                      i;
+struct Scsi_Host         *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+cli();
+DEB(printk("AM53C974_reset called; "));
+
+printk("AM53C974_reset called\n");
+AM53C974_print(instance);
+AM53C974_keywait();
+
+/* do hard reset */
+AM53C974_write_8(CMDREG, CMDREG_RDEV);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+hostdata->msgout[0] = NOP;
+for (i = 0; i < 8; i++) {
+    hostdata->busy[i] = 0;
+    hostdata->sync_per[i] = DEF_STP;
+    hostdata->sync_off[i] = 0;
+    hostdata->sync_neg[i] = 0; }
+hostdata->last_message[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->issue_queue = NULL;
+hostdata->disconnected_queue = NULL;
+hostdata->in_reset = 0;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+
+/* reset bus */
+AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */
+AM53C974_write_8(CMDREG, CMDREG_RBUS);     /* reset SCSI bus */
+udelay(40);
+AM53C974_config_after_reset(instance);
+
+sti();
+cmd->result = DID_RESET << 16;
+cmd->scsi_done(cmd);
+return SCSI_ABORT_SUCCESS;
+}
diff --git a/drivers/scsi/AM53C974.h b/drivers/scsi/AM53C974.h
new file mode 100644 (file)
index 0000000..3160890
--- /dev/null
@@ -0,0 +1,413 @@
+/* AM53/79C974 (PCscsi) driver release 0.5
+ *
+ * The architecture and much of the code of this device
+ * driver was originally developed by Drew Eckhardt for
+ * the NCR5380. The following copyrights apply:
+ *  For the architecture and all parts similar to the NCR5380:
+ *    Copyright 1993, Drew Eckhardt
+ *     Visionary Computing 
+ *     (Unix and Linux consulting and custom programming)
+ *     drew@colorado.edu
+ *     +1 (303) 666-5836
+ *
+ *  The AM53C974_nobios_detect code was origininally developed by
+ *   Robin Cutshaw (robin@xfree86.org) and is used here in a 
+ *   modified form.
+ *
+ *  For the other parts:
+ *    Copyright 1994, D. Frieauff
+ *    EMail: fri@rsx42sun0.dofn.de
+ *    Phone: x49-7545-8-2256 , x49-7541-42305
+ */
+
+/*
+ * $Log: AM53C974.h,v $
+ */
+
+#ifndef AM53C974_H
+#define AM53C974_H
+
+#include <linux/scsicam.h>
+
+/***************************************************************************************
+* Default setting of the controller's SCSI id. Edit and uncomment this only if your    *
+* BIOS does not correctly initialize the controller's SCSI id.                         *
+* If you don't get a warning during boot, it is correctly initialized.                 *
+****************************************************************************************/
+/* #define AM53C974_SCSI_ID 7 */
+
+/***************************************************************************************
+* Default settings for sync. negotiation enable, transfer rate and sync. offset.       *
+* These settings can be replaced by LILO overrides (append) with the following syntax:          *
+* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset                          *
+* Sync. negotiation is disabled by default and will be enabled for those targets which *
+* are specified in the LILO override                                                   *
+****************************************************************************************/
+#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */
+#define DEFAULT_RATE                    5 /* MHz, min: 3; max: 10 */
+#define DEFAULT_SYNC_OFFSET             0 /* bytes, min: 0; max: 15; use 0 for async. mode */
+
+
+/* --------------------- don't edit below here  --------------------- */
+
+#define AM53C974_DRIVER_REVISION_MAJOR 0
+#define AM53C974_DRIVER_REVISION_MINOR 5
+#define SEPARATOR_LINE  \
+"--------------------------------------------------------------------------\n"
+
+/* debug control */
+/* #define AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_MSG */
+/* #define AM53C974_DEBUG_KEYWAIT */
+/* #define AM53C974_DEBUG_INIT */
+/* #define AM53C974_DEBUG_QUEUE */
+/* #define AM53C974_DEBUG_INFO */
+/* #define AM53C974_DEBUG_LINKED */
+/* #define VERBOSE_AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_INTR */
+/* #define AM53C974_DEB_RESEL */
+#define AM53C974_DEBUG_ABORT
+/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */
+
+/* special options/constants */
+#define DEF_CLK                 40   /* chip clock freq. in MHz */
+#define MIN_PERIOD               4   /* for negotiation: min. number of clocks per cycle */
+#define MAX_PERIOD              13   /* for negotiation: max. number of clocks per cycle */
+#define MAX_OFFSET              15   /* for negotiation: max. offset (0=async) */
+
+#define DEF_SCSI_TIMEOUT        245  /* STIMREG value, 40 Mhz */
+#define DEF_STP                 8    /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */
+#define DEF_SOF_RAD             0    /* REQ/ACK deassertion delay */
+#define DEF_SOF_RAA             0    /* REQ/ACK assertion delay */
+#define DEF_ETM                 0    /* CNTLREG1, ext. timing mode */
+#define DEF_PERE                1    /* CNTLREG1, parity error reporting */
+#define DEF_CLKF                0    /* CLKFREG,  0=40 Mhz */
+#define DEF_ENF                 1    /* CNTLREG2, enable features */
+#define DEF_ADIDCHK             0    /* CNTLREG3, additional ID check */
+#define DEF_FASTSCSI            1    /* CNTLREG3, fast SCSI */
+#define DEF_FASTCLK             1    /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */
+#define DEF_GLITCH              1    /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */
+#define DEF_PWD                 0    /* CNTLREG4, reduced power feature */
+#define DEF_RAE                 0    /* CNTLREG4, RAE active negation on REQ, ACK only */
+#define DEF_RADE                1    /* 1CNTLREG4, active negation on REQ, ACK and data */
+
+/*** PCI block ***/
+/* standard registers are defined in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_AMD
+#define PCI_VENDOR_ID_AMD      0x1022
+#define PCI_DEVICE_ID_AMD_SCSI  0x2020
+#endif
+#define PCI_BASE_MASK           0xFFFFFFE0
+#define PCI_COMMAND_PERREN      0x40
+#define PCI_SCRATCH_REG_0      0x40    /* 16 bits */
+#define PCI_SCRATCH_REG_1      0x42    /* 16 bits */
+#define PCI_SCRATCH_REG_2      0x44    /* 16 bits */
+#define PCI_SCRATCH_REG_3      0x46    /* 16 bits */
+#define PCI_SCRATCH_REG_4      0x48    /* 16 bits */
+#define PCI_SCRATCH_REG_5      0x4A    /* 16 bits */
+#define PCI_SCRATCH_REG_6      0x4C    /* 16 bits */
+#define PCI_SCRATCH_REG_7      0x4E    /* 16 bits */
+
+/*** SCSI block ***/
+#define CTCLREG                        0x00    /* r    current transf. count, low byte    */
+#define CTCMREG                        0x04    /* r    current transf. count, middle byte */
+#define CTCHREG                        0x38    /* r    current transf. count, high byte   */
+#define STCLREG                        0x00    /* w    start transf. count, low byte      */
+#define STCMREG                        0x04    /* w    start transf. count, middle byte   */
+#define STCHREG                        0x38    /* w    start transf. count, high byte     */
+#define FFREG                  0x08    /* rw   SCSI FIFO reg.                     */
+#define STIMREG                        0x14    /* w    SCSI timeout reg.                  */
+
+#define SDIDREG                        0x10    /* w    SCSI destination ID reg.           */
+#define SDIREG_MASK            0x07    /* mask                                    */
+
+#define STPREG                 0x18    /* w    synchronous transf. period reg.    */
+#define STPREG_STP             0x1F    /* synchr. transfer period                 */
+
+#define CLKFREG                        0x24    /* w    clock factor reg.                  */
+#define CLKFREG_MASK           0x07    /* mask                                    */
+
+#define CMDREG                 0x0C    /* rw   SCSI command reg.                  */
+#define CMDREG_DMA             0x80    /* set DMA mode (set together with opcodes below) */
+#define CMDREG_IT              0x10    /* information transfer                    */
+#define CMDREG_ICCS            0x11    /* initiator command complete steps        */
+#define CMDREG_MA              0x12    /* message accepted                        */
+#define CMDREG_TPB             0x98    /* transfer pad bytes, DMA mode only       */
+#define CMDREG_SATN            0x1A    /* set ATN                                 */
+#define CMDREG_RATN            0x1B    /* reset ATN                               */
+#define CMDREG_SOAS            0x41    /* select without ATN steps                */
+#define CMDREG_SAS             0x42    /* select with ATN steps (1 msg byte)      */
+#define CMDREG_SASS            0x43    /* select with ATN and stop steps          */
+#define CMDREG_ESR             0x44    /* enable selection/reselection            */
+#define CMDREG_DSR             0x45    /* disable selection/reselection           */
+#define CMDREG_SA3S            0x46    /* select with ATN 3 steps  (3 msg bytes)  */
+#define CMDREG_NOP             0x00    /* no operation                            */
+#define CMDREG_CFIFO           0x01    /* clear FIFO                              */
+#define CMDREG_RDEV            0x02    /* reset device                            */
+#define CMDREG_RBUS            0x03    /* reset SCSI bus                          */
+
+#define STATREG                        0x10    /* r    SCSI status reg.                   */
+#define STATREG_INT            0x80    /* SCSI interrupt condition detected       */
+#define STATREG_IOE            0x40    /* SCSI illegal operation error detected   */
+#define STATREG_PE             0x20    /* SCSI parity error detected              */
+#define STATREG_CTZ            0x10    /* CTC reg decremented to zero             */
+#define STATREG_MSG            0x04    /* SCSI MSG phase (latched?)               */
+#define STATREG_CD             0x02    /* SCSI C/D phase (latched?)               */
+#define STATREG_IO             0x01    /* SCSI I/O phase (latched?)               */
+#define STATREG_PHASE           0x07    /* SCSI phase mask                        */
+
+#define INSTREG                        0x14    /* r    interrupt status reg.              */
+#define INSTREG_SRST           0x80    /* SCSI reset detected                     */
+#define INSTREG_ICMD           0x40    /* SCSI invalid command detected           */
+#define INSTREG_DIS            0x20    /* target disconnected or sel/resel timeout*/
+#define INSTREG_SR             0x10    /* device on bus has service request       */
+#define INSTREG_SO             0x08    /* successful operation                    */
+#define INSTREG_RESEL          0x04    /* device reselected as initiator          */
+
+#define ISREG                  0x18    /* r    internal state reg.                */
+#define ISREG_SOF              0x08    /* synchronous offset flag (act. low)      */
+#define ISREG_IS               0x07    /* status of intermediate op.              */
+#define ISREG_OK_NO_STOP        0x04    /* selection successful                    */
+#define ISREG_OK_STOP           0x01    /* selection successful                    */
+
+#define CFIREG                 0x1C    /* r    current FIFO/internal state reg.   */
+#define CFIREG_IS              0xE0    /* status of intermediate op.              */
+#define CFIREG_CF              0x1F    /* number of bytes in SCSI FIFO            */
+
+#define SOFREG                 0x1C    /* w    synchr. offset reg.                */
+#define SOFREG_RAD             0xC0    /* REQ/ACK deassertion delay (sync.)       */
+#define SOFREG_RAA             0x30    /* REQ/ACK assertion delay (sync.)         */
+#define SOFREG_SO              0x0F    /* synch. offset (sync.)                   */
+
+#define CNTLREG1               0x20    /* rw   control register one               */
+#define CNTLREG1_ETM           0x80    /* set extended timing mode                */
+#define CNTLREG1_DISR          0x40    /* disable interrupt on SCSI reset         */
+#define CNTLREG1_PERE          0x10    /* enable parity error reporting           */
+#define CNTLREG1_SID           0x07    /* host adapter SCSI ID                    */
+
+#define CNTLREG2               0x2C    /* rw   control register two               */
+#define CNTLREG2_ENF           0x40    /* enable features                         */
+
+#define CNTLREG3               0x30    /* rw   control register three             */ 
+#define CNTLREG3_ADIDCHK       0x80    /* additional ID check                     */
+#define CNTLREG3_FASTSCSI      0x10    /* fast SCSI                               */
+#define CNTLREG3_FASTCLK       0x08    /* fast SCSI clocking                      */
+
+#define CNTLREG4               0x34    /* rw   control register four              */ 
+#define CNTLREG4_GLITCH                0xC0    /* glitch eater                            */
+#define CNTLREG4_PWD           0x20    /* reduced power feature                   */
+#define CNTLREG4_RAE           0x08    /* write only, active negot. ctrl.         */
+#define CNTLREG4_RADE          0x04    /* active negot. ctrl.                     */
+#define CNTLREG4_RES           0x10    /* reserved bit, must be 1                 */
+
+/*** DMA block ***/
+#define DMACMD                 0x40    /* rw   command                            */
+#define DMACMD_DIR             0x80    /* transfer direction (1=read from device) */
+#define DMACMD_INTE_D          0x40    /* DMA transfer interrupt enable           */
+#define DMACMD_INTE_P          0x20    /* page transfer interrupt enable          */
+#define DMACMD_MDL             0x10    /* map to memory descriptor list           */
+#define DMACMD_DIAG            0x04    /* diagnostics, set to 0                   */
+#define DMACMD_IDLE            0x00    /* idle cmd                                */
+#define DMACMD_BLAST           0x01    /* flush FIFO to memory                    */
+#define DMACMD_ABORT           0x02    /* terminate DMA                           */
+#define DMACMD_START           0x03    /* start DMA                               */
+
+#define DMASTATUS              0x54    /* r    status register                    */
+#define DMASTATUS_BCMPLT       0x20    /* BLAST complete                          */
+#define DMASTATUS_SCSIINT      0x10    /* SCSI interrupt pending                  */
+#define DMASTATUS_DONE         0x08    /* DMA transfer terminated                 */
+#define DMASTATUS_ABORT                0x04    /* DMA transfer aborted                    */
+#define DMASTATUS_ERROR                0x02    /* DMA transfer error                      */
+#define DMASTATUS_PWDN         0x02    /* power down indicator                    */
+
+#define DMASTC                 0x44    /* rw   starting transfer count            */
+#define DMASPA                 0x48    /* rw   starting physical address          */
+#define DMAWBC                 0x4C    /* r    working byte counter               */
+#define DMAWAC                 0x50    /* r    working address counter            */
+#define DMASMDLA               0x58    /* rw   starting MDL address               */
+#define DMAWMAC                        0x5C    /* r    working MDL counter                */
+
+/*** SCSI phases ***/
+#define PHASE_MSGIN             0x07
+#define PHASE_MSGOUT            0x06
+#define PHASE_RES_1             0x05
+#define PHASE_RES_0             0x04
+#define PHASE_STATIN            0x03
+#define PHASE_CMDOUT            0x02
+#define PHASE_DATAIN            0x01
+#define PHASE_DATAOUT           0x00
+
+struct AM53C974_hostdata {
+    volatile unsigned       in_reset:1;          /* flag, says bus reset pending */
+    volatile unsigned       aborted:1;           /* flag, says aborted */
+    volatile unsigned       selecting:1;         /* selection started, but not yet finished */
+    volatile unsigned       disconnecting: 1;    /* disconnection started, but not yet finished */
+    volatile unsigned       dma_busy:1;          /* dma busy when service request for info transfer received */
+    volatile unsigned  char msgout[10];          /* message to output in MSGOUT_PHASE */
+    volatile unsigned  char last_message[10];  /* last message OUT */
+    volatile Scsi_Cmnd      *issue_queue;      /* waiting to be issued */
+    volatile Scsi_Cmnd      *disconnected_queue;       /* waiting for reconnect */
+    volatile Scsi_Cmnd      *sel_cmd;            /* command for selection */
+    volatile Scsi_Cmnd      *connected;                /* currently connected command */
+    volatile unsigned  char busy[8];           /* index = target, bit = lun */
+    unsigned  char sync_per[8];         /* synchronous transfer period (in effect) */
+    unsigned  char sync_off[8];         /* synchronous offset (in effect) */
+    unsigned  char sync_neg[8];         /* sync. negotiation performed (in effect) */
+    unsigned  char sync_en[8];          /* sync. negotiation performed (in effect) */
+    unsigned  char max_rate[8];         /* max. transfer rate (setup) */
+    unsigned  char max_offset[8];       /* max. sync. offset (setup), only valid if corresponding sync_en is nonzero */
+    };
+
+#define AM53C974 { \
+    NULL,                              /* pointer to next in list                      */  \
+    NULL,                      /* long * usage_count                           */  \
+    NULL,                       /* struct proc_dir_entry *proc_dir              */ \
+    NULL,                       /* int (*proc_info)(char *, char **, off_t, int, int, int); */ \
+    "AM53C974",                        /* name                                         */  \
+    AM53C974_detect,                   /* int (* detect)(struct SHT *)                 */  \
+    NULL,                              /* int (*release)(struct Scsi_Host *)           */  \
+    AM53C974_info,                     /* const char *(* info)(struct Scsi_Host *)     */  \
+    AM53C974_command,                  /* int (* command)(Scsi_Cmnd *)                 */  \
+    AM53C974_queue_command,    /* int (* queuecommand)(Scsi_Cmnd *,                \
+                                           void (*done)(Scsi_Cmnd *))           */  \
+    AM53C974_abort,                    /* int (* abort)(Scsi_Cmnd *)                   */  \
+    AM53C974_reset,                    /* int (* reset)(Scsi_Cmnd *)                   */  \
+    NULL,                      /* int (* slave_attach)(int, int)               */  \
+    scsicam_bios_param,                /* int (* bios_param)(Disk *, int, int[])       */  \
+    12,                        /* can_queue                                    */  \
+    -1,                         /* this_id                                      */  \
+    SG_ALL,                            /* sg_tablesize                                 */  \
+    1,                                 /* cmd_per_lun                                  */  \
+    0,                                 /* present, i.e. how many adapters of this kind */  \
+    0,                                 /* unchecked_isa_dma                            */  \
+    DISABLE_CLUSTERING                 /* use_clustering                               */  \
+    }
+
+void AM53C974_setup(char *str, int *ints);
+int AM53C974_detect(Scsi_Host_Template *tpnt);
+int AM53C974_biosparm(Disk *disk, int dev, int *info_array);
+const char *AM53C974_info(struct Scsi_Host *);
+int AM53C974_command(Scsi_Cmnd *SCpnt);
+int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+int AM53C974_abort(Scsi_Cmnd *cmd);
+int AM53C974_reset (Scsi_Cmnd *cmd);
+
+#define AM53C974_local_declare()       unsigned long io_port
+#define AM53C974_setio(instance)       io_port = instance->io_port
+#define AM53C974_read_8(addr)           inb(io_port + (addr))
+#define AM53C974_write_8(addr,x)        outb((x), io_port + (addr))
+#define AM53C974_read_16(addr)          inw(io_port + (addr))
+#define AM53C974_write_16(addr,x)       outw((x), io_port + (addr))
+#define AM53C974_read_32(addr)          inl(io_port + (addr))
+#define AM53C974_write_32(addr,x)       outl((x), io_port + (addr))
+
+#define AM53C974_poll_int()             { do { statreg = AM53C974_read_8(STATREG); } \
+                                             while (!(statreg & STATREG_INT)) ; \
+                                          AM53C974_read_8(INSTREG) ; } /* clear int */
+#define AM53C974_cfifo()               (AM53C974_read_8(CFIREG) & CFIREG_CF)
+
+/* These are "special" values for the tag parameter passed to AM53C974_select. */
+#define TAG_NEXT       -1      /* Use next free tag */
+#define TAG_NONE       -2      /* Establish I_T_L nexus instead of I_T_L_Q
+                                * even on SCSI-II devices */
+
+/************ LILO overrides *************/
+typedef struct _override_t {
+    int host_scsi_id;                  /* SCSI id of the bus controller */
+    int target_scsi_id;                 /* SCSI id of target */
+    int max_rate;                      /* max. transfer rate */
+    int max_offset;                    /* max. sync. offset, 0 = asynchronous */
+    } override_t;
+
+/************ PCI stuff *************/
+#define AM53C974_PCIREG_OPEN()                    outb(0xF1, 0xCF8); outb(0, 0xCFA)
+#define AM53C974_PCIREG_CLOSE()                   outb(0, 0xCF8)
+#define AM53C974_PCIREG_READ_BYTE(instance,a)     ( inb((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_WORD(instance,a)     ( inw((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_DWORD(instance,a)    ( inl((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a)  ( outb((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_WORD(instance,x,a)  ( outw((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) )
+
+typedef struct _pci_config_t {
+    /* start of official PCI config space header */
+    union {
+        unsigned int device_vendor;
+       struct {
+         unsigned short vendor;
+         unsigned short device;
+         } dv;
+        } dv_id;
+#define _device_vendor dv_id.device_vendor
+#define _vendor dv_id.dv.vendor
+#define _device dv_id.dv.device
+    union {
+        unsigned int status_command;
+       struct {
+         unsigned short command;
+         unsigned short status;
+         } sc;
+        } stat_cmd;
+#define _status_command stat_cmd.status_command
+#define _command stat_cmd.sc.command
+#define _status  stat_cmd.sc.status
+    union {
+        unsigned int class_revision;
+       struct {
+           unsigned char rev_id;
+           unsigned char prog_if;
+           unsigned char sub_class;
+           unsigned char base_class;
+       } cr;
+    } class_rev;
+#define _class_revision class_rev.class_revision
+#define _rev_id     class_rev.cr.rev_id
+#define _prog_if    class_rev.cr.prog_if
+#define _sub_class  class_rev.cr.sub_class
+#define _base_class class_rev.cr.base_class
+    union {
+        unsigned int bist_header_latency_cache;
+       struct {
+           unsigned char cache_line_size;
+           unsigned char latency_timer;
+           unsigned char header_type;
+           unsigned char bist;
+       } bhlc;
+    } bhlc;
+#define _bist_header_latency_cache bhlc.bist_header_latency_cache
+#define _cache_line_size bhlc.bhlc.cache_line_size
+#define _latency_timer   bhlc.bhlc.latency_timer
+#define _header_type     bhlc.bhlc.header_type
+#define _bist            bhlc.bhlc.bist
+    unsigned int _base0;
+    unsigned int _base1;
+    unsigned int _base2;
+    unsigned int _base3;
+    unsigned int _base4;
+    unsigned int _base5;
+    unsigned int rsvd1;
+    unsigned int rsvd2;
+    unsigned int _baserom;
+    unsigned int rsvd3;
+    unsigned int rsvd4;
+    union {
+        unsigned int max_min_ipin_iline;
+       struct {
+           unsigned char int_line;
+           unsigned char int_pin;
+           unsigned char min_gnt;
+           unsigned char max_lat;
+       } mmii;
+    } mmii;
+#define _max_min_ipin_iline mmii.max_min_ipin_iline
+#define _int_line mmii.mmii.int_line
+#define _int_pin  mmii.mmii.int_pin
+#define _min_gnt  mmii.mmii.min_gnt
+#define _max_lat  mmii.mmii.max_lat
+    /* end of official PCI config space header */
+    unsigned short _ioaddr; /* config type 1 - private I/O addr    */
+    unsigned int _pcibus;  /* config type 2 - private bus id      */
+    unsigned int _cardnum; /* config type 2 - private card number */
+} pci_config_t;
+
+#endif /* AM53C974_H */
index 9d2eb2e70d92e0d32cfa508bc10f1c6c7a243d53..80d4f7cb238238f0db169d2b43962291c6a12ffe 100644 (file)
@@ -37,5 +37,6 @@ dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
 dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
 dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA $CONFIG_SCSI
 dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI
+bool 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974
 #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 endmenu
index 6423151e7ca18697e4e1a3a000186f863a6d3ea0..b861e165ea47eb51f86871ef3fd409fd97fb9621 100644 (file)
@@ -76,9 +76,9 @@ endif
 ifeq ($(CONFIG_SCSI_ADVANSYS),y)
 L_OBJS += advansys.o
 else
-       ifeq ($(CONFIG_SCSI_ADVANSYS),m)
-       M_OBJS += advansys.o
-       endif
+  ifeq ($(CONFIG_SCSI_ADVANSYS),m)
+  M_OBJS += advansys.o
+  endif
 endif
 
 ifeq ($(CONFIG_SCSI_QLOGIC),y)
@@ -121,6 +121,10 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_AM53C974),y)
+L_OBJS += AM53C974.o
+endif
+
 ifeq ($(CONFIG_SCSI_BUSLOGIC),y)
 L_OBJS += BusLogic.o
 else
diff --git a/drivers/scsi/README.AM53C974 b/drivers/scsi/README.AM53C974
new file mode 100644 (file)
index 0000000..6bbead9
--- /dev/null
@@ -0,0 +1,232 @@
+SUBJECT
+-------
+AM53/79C974 PC-SCSI Driver
+
+
+DISCLAIMER
+----------
+***  THIS SHOULD BE CONSIDERED AS BETA SOFTWARE  ***
+***  USE AT YOUR OWN RISK!                       ***
+
+
+Copyright
+---------
+The architecture and much of the code of this device driver was 
+originally developed by Drew Eckhardt for the NCR5380. The 
+following copyrights apply:
+
+For the architecture and all pieces of code which can also be found 
+in the NCR5380 device driver:
+Copyright 1993, Drew Eckhardt
+  Visionary Computing 
+  (Unix and Linux consulting and custom programming)
+  drew@colorado.edu
+  +1 (303) 666-5836
+The AM53C974_nobios_detect code was origininally developed by
+Robin Cutshaw (robin@xfree86.org) and is used here in a 
+slightly modified form.
+For the remaining code:
+  Copyright 1994, D. Frieauff
+  EMail: fri@rsx42sun0.dofn.de
+  Phone: x49-7545-8-2256 , x49-7541-42305
+
+
+Version
+-------
+AM53/79C974 (PC-SCSI) Linux driver ALPHA release 0.5, 19 November 1995
+
+
+Changelog
+---------
+0.1 -> 0.2: 
+  - Extended message handling re-written to eliminate 'invalid message 17' bug
+  - Parameters of AM53C974_intr adapted
+  - Debug messages structured
+  - Spelling improved
+0.2 -> 0.3:
+  - README file updated -- please read this file up to the end!
+  - Automatic scanning of io_port and irq implemented; no need for BIOS32 
+    anymore
+  - Improved configuration (now via LILO parameter string)
+  - Cleanup of probing and initialisation code
+  - Improved sync. negotiation (can be setup individually for every device)
+  - Improved/ debugged code for reception of ext. messages
+0.3 -> 0.4:
+  - Improved PCI probing and initialisation code
+  - Compatibility changes for Linux 1.3.x
+0.4 -> 0.5:
+  - Compatibility changes for Linux 1.3.42
+
+Bugs & Todo
+-----------
+ - Add proc info function
+ - Support SCSI-2 tagged queuing
+ - Finalize abort code 
+
+Features
+--------
+This driver supports asynchronous and synchronous SCSI-I and SCSI-II devices.
+It is capable of transfer rate and synchronous negotiation (see below).
+The driver supportes scatter-gather. Transfers are DMA based, but do not 
+(yet) make use of the AM53/79C974 MDL mode.
+Max. transfer rate is 10MHz (whatever this is in real life). The transfer
+rate is negotiated with each device (see dmesg output).
+The AM53/79C974 has a 96-byte DMA FIFO to the PCI bus and a 16-byte SCSI
+FIFO. It provides active negation and glitch suppression functions.
+Burst DMA transfer rate is 132 MBytes/sec.
+
+
+Configuration
+-------------
+The following communication characteristics can be set individually  for every
+SCSI device on the bus:
+  - enable/disable sync. negotiation
+  - transfer rate
+  - asynchronous or synchronous communication
+  - in case of sync. communication, the sync. offset
+
+The sync. offset specifies the number of bytes that can be sent or 
+reveived from the SCSI bus without ACK resp. REQ signal.
+CAUTION: USING SYNCHRONOUS MODE ON LONG SCSI CABLES MAY CAUSE 
+         COMMUNICATION PROBLEMS LEADING TO LOSS OF DATA. 
+
+The default setting of the SCSI communication parameters is as follows:
+  - no negotiation
+  - 5.0 MHz transfer rate
+  - asynchronous mode
+  - zero offset
+The parameters can be modified by passing a string with the following syntax to 
+the kernel: AM53C974=host-scsi-id,target-scsi-id,max-rate,max-offset
+The parameters will be used by the driver as negotiation basis.
+The range of the rate parameter is 3 to 10 MHz.
+The range of the sync. offset parameter is 0 to 15 bytes. A value of 0 denotes 
+asynchronous comm. mode.
+If the target cannot cope with the specified transfer rate, sync. mode or sync.
+offset, the negotiation result will differ from the specified values.
+The negotiation result is printed out at the end of the negotiation process
+(to read it, use the dmesg program or the appropriate syslog).
+The parameter strings (blank separated) can be passed to the kernel at the
+LILO prompt, or as part of the LILO configuration file.
+
+For example, the string "AM53C974=7,2,8,15" would be interpreted as follows:
+ "For communication between the controller with SCSI-ID 7 and the device with
+ SCSI-ID 2 a transfer rate of 8MHz in synchronous mode with max. 15 bytes offset
+ should be negotiated".
+
+As an example, here my LILO configuration file:
+  boot = /dev/sda
+  compact
+  #prompt
+  delay = 50   # optional, for systems that boot very quickly
+  vga = normal # force sane state
+  ramdisk = 0  # paranoia setting
+  root = current  # use "current" root
+  image = /usr/src/linux/arch/i386/boot/zImage
+    label = linux
+    append = "AM53C974=7,0,10,0 AM53C974=7,1,10,0 AM53C974=7,2,10,15 AM53C974=7,4,10,0 AM53C974=7,5,10,0"
+    read-only
+  other = /dev/sda4
+    label = os2
+  other = /dev/sdb3
+    loader = /boot/any_d.b
+    table = /dev/sdb
+    label = setup
+
+The same parameters at the LILO prompt:
+  LILO boot: linux AM53C974=7,0,10,0 AM53C974=7,1,10,0 AM53C974=7,2,10,15 AM53C974=7,4,10,0 AM53C974=7,5,10,0
+
+You can override parameters specified in the LILO configuration file by 
+parameters specified on the LILO command line.
+
+
+BIOS usage
+----------
+Version 0.4 of the driver will use the BIOS, if available. Otherwise
+it will try its internal PCI scan and access routines.
+The driver assumes that the controller's SCSI-ID (usually 7) has been
+correctly loaded by the BIOS into the controller's register during
+system boot. If the driver detects that the controller's SCSI ID is not 
+'7' it will print out a warning. If this happens to you please correct
+seeting of the controller's SCSI-ID. If it is wrong, then edit the 
+AM53C974_SCSI_ID definition in file AM53C974.h accordingly.
+
+
+Test environment
+----------------
+This driver was tested on a Compaq XL566 with the following SCSI configuration:
+2 x HP C2247 fixed disk (internal, rate=10MHz, async.)
+1 x Micropolis 1624 fixed disk (external, rate=8MHz, sync., offset=15 bytes)
+1 x Wangtek W5525ES cartridge streamer (internal, rate=5MHz, async.)
+1 x Toshiba XM-3301B CD-ROM (external, rate=5MHz, async.)
+
+
+Known problems
+--------------
+ - Compaq/Matsushita CD-ROM:
+   Use of this device with AM53C974 driver version 0.2 caused the kernel to
+   hang during Linux boot. If you encounter the problem, don't enable sync.
+   negotiation with the CD-ROM, i.e. simply don't specify comm. parameters 
+   for this device on the LILO commandline or configuration file. 
+   The driver will thus use its default for the CD-ROM, which is 5MHz 
+   transfer rate asynch and no sync. negotiation.
+ - Some disks cause problems.
+
+
+What to do if there is a SCSI problem possibly related to the driver
+--------------------------------------------------------------------
+Read Klaus Liedl's WWW page (http://www-c724.uibk.ac.at/XL/).
+In case this does not help:
+Send me a complete description of the problem, including your SCSI 
+configuration plus as much debugging information as possible. 
+Don't wait until I ask you for this information. To enable the 
+generation of debugging output, remove the comments from the following 
+definitions in the AM53C974.h file:
+    AM53C974_DEBUG
+    AM53C974_DEBUG_MSG
+    AM53C974_DEBUG_KEYWAIT
+    AM53C974_DEBUG_INFO
+    AM53C974_DEBUG_INTR
+With these definitions enabled, the driver will enter single-step mode during 
+Linux boot. Use the spacebar for stepping.
+Take note of at least the last 10 printout sections (marked by dashes) before 
+the crash/hangup or whatever happens and send me all of this information via 
+email. If the system can boot, use the syslogd daemon to record the debugging 
+output. Maybe you can use the ramdisk for this purpose too (if necessary, kindly
+ask K. Liedl (Klaus.Liedl@uibk.ac.at) for support, he knows how to do it -- 
+I never tried). Stay in email contact with me. Be aware that the following 
+weeks/months could be the worst of your life.
+Note: If single-stepping takes up too much time, you can try to let the driver 
+catch the problem by pressing the 'r' key. The driver will automatically enter
+single-step mode if it has detected something weird.
+
+
+Author's Contact Adress
+-----------------------
+Email: fri@rsx42sun0.dofn.de
+Phone: x49-7545-2256 (office), x49-7541-42305 (home)
+Home address: D. Frieauff, Stockerholzstr. 27, 88048 Friedrichshafen, Germany
+
+
+!!!! Important Notice !!!!
+-----------------------------
+- Klaus Liedl maintains an excellent WWW page about Linux on Compaq XL.
+  It includes an FAQ, lots of tips & tricks as well as downloadable 
+  boot disk images. The URL is: http://www-c724.uibk.ac.at/XL/
+- Volunteer wanted for further maintenance of this driver software. I
+  don't have the time anymore to do serious support as some of you will know.
+
+Literature
+----------
+ - AMD AM53C974 PC-SCSI Technical Manual, publication #18624B
+ - Amendment to the AMD AM53C974 PC-SCSI Technical Manual
+ - AMD AM79C974 PC-NET Datasheet, publication #18681
+ - Amendment to the AMD AM79C974 PC-NET Datasheet
+
+
+THANKS to
+---------
+ - Drew Eckhardt, Robin Cutshaw, K. Liedl, Robert J. Pappas, A. Grenier, 
+   Mark Stockton, David C. Niemi, Ben Craft, and many others who have helped
+
index 2dcb6050b203a9b7d43de94d41e471b3cf5cfa96..6d0d90628bec8ed11776765e24bddb47242b518a 100644 (file)
  * General Public License for more details.
  *
  *
- * $Id: aha152x.c,v 1.13 1996/01/09 02:15:53 fischer Exp $
+ * $Id: aha152x.c,v 1.14 1996/01/17 15:11:20 fischer Exp fischer $
  *
  * $Log: aha152x.c,v $
+ * Revision 1.14  1996/01/17  15:11:20  fischer
+ * - fixed lockup in MESSAGE IN phase after reconnection
+ *
  * Revision 1.13  1996/01/09  02:15:53  fischer
  * - some cleanups
  * - moved request_irq behind controller initialization
@@ -1402,8 +1405,6 @@ void aha152x_intr(int irqno, struct pt_regs * regs)
           aha152x_panic(shpnt, "unknown lun");
        }
 
-      make_acklow(shpnt);
-      getphase(shpnt);
 
 #if defined(DEBUG_QUEUES)
       if(HOSTDATA(shpnt)->debug & debug_queues)
@@ -1430,6 +1431,8 @@ void aha152x_intr(int irqno, struct pt_regs * regs)
       CURRENT_SC->SCp.phase &= ~disconnected;
       restore_flags(flags);
 
+      make_acklow(shpnt);
+      if(getphase(shpnt)!=P_MSGI) {
       SETPORT(SIMODE0, 0);
       SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
 #if defined(DEBUG_RACE)
@@ -1438,6 +1441,7 @@ void aha152x_intr(int irqno, struct pt_regs * regs)
       SETBITS(DMACNTRL0, INTEN);
       return;
     }
+    }
   
   /* Check, if we aren't busy with a command */
   if(!CURRENT_SC)
@@ -1946,6 +1950,7 @@ void aha152x_intr(int irqno, struct pt_regs * regs)
            printk("d+, ");
 #endif
             append_SC(&DISCONNECTED_SC, CURRENT_SC);
+            CURRENT_SC->SCp.phase |= 1<<16;
             CURRENT_SC = NULL;
          restore_flags(flags);
 
index 13c241dc1d490614ea471b32200ccf5170b05414..0b2bb3bdb638fe24408a6369faa67d206b58299e 100644 (file)
@@ -2,7 +2,7 @@
 #define _AHA152X_H
 
 /*
- * $Id: aha152x.h,v 1.13 1995/12/16 12:27:23 fischer Exp $
+ * $Id: aha152x.h,v 1.14 1996/01/17 15:13:36 fischer Exp fischer $
  */
 
 #if defined(__KERNEL__)
@@ -23,7 +23,7 @@ int aha152x_proc_info(char *buffer, char **start, off_t offset, int length, int
    (unless we support more than 1 cmd_per_lun this should do) */
 #define AHA152X_MAXQUEUE       7               
 
-#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.13 $"
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.14 $"
 
 extern struct proc_dir_entry proc_scsi_aha152x;
 
index aa47bad0b589d62e84f0bff856793c3f77266d9e..2a4eed3ecb43eafba27fa79910857c8aee571ea2 100644 (file)
 #include "NCR53c406a.h"
 #endif
 
+#ifdef CONFIG_SCSI_AM53C974
+#include "AM53C974.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -226,6 +230,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #ifdef CONFIG_SCSI_EATA
     EATA,
 #endif
+#ifdef CONFIG_SCSI_AM53C974
+    AM53C974,
+#endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
 #endif
index 34d8edc3657a0bba0fb9e9e6057cfa6d13d94bc6..a32d86d89bdb629c2863a334abfb60cebf7c433f 100644 (file)
@@ -410,8 +410,8 @@ sb16_dsp_prepare_for_input (int dev, int bsize, int bcount)
   audio_devs[my_dev]->dmachan1 = dsp_16bit ? dma16 : dma8;
   dsp_count = 0;
   dsp_cleanup ();
-  sb_dsp_command (0xd0);       /* Halt DMA until trigger() is called */
   trigger_bits = 0;
+  sb_dsp_command (0xd4);
   return 0;
 }
 
@@ -421,20 +421,14 @@ sb16_dsp_prepare_for_output (int dev, int bsize, int bcount)
   audio_devs[my_dev]->dmachan1 = dsp_16bit ? dma16 : dma8;
   dsp_count = 0;
   dsp_cleanup ();
-  sb_dsp_command (0xd0);       /* Halt DMA until trigger() is called */
   trigger_bits = 0;
+  sb_dsp_command (0xd4);
   return 0;
 }
 
 static void
 sb16_dsp_trigger (int dev, int bits)
 {
-  if (bits != 0)
-    bits = 1;
-
-  if (bits == trigger_bits)    /* No change */
-    return;
-
   trigger_bits = bits;
 
   if (!bits)
index 26286e1ce4032c35dcb92fdcb0a27df56cbb19a4..b10d062cd952b0b36bbd83c180daa9aaa263ac25 100644 (file)
@@ -1378,26 +1378,23 @@ Jazz16_set_dma16 (int dma)
   initialize_ProSonic16 ();
 }
 
-long
-sb_dsp_init (long mem_start, struct address_info *hw_config)
+static void
+dsp_get_vers (struct address_info *hw_config)
 {
   int             i;
-  int             ess_major = 0, ess_minor = 0;
 
-  int             mixer_type = 0;
+  unsigned long   flags;
 
+  save_flags (flags);
+  cli ();
   sb_osp = hw_config->osp;
   sbc_major = sbc_minor = 0;
-  sb_dsp_command (0xe1);       /*
-                                * Get version
-                                */
+  sb_dsp_command (0xe1);       /* Get version */
 
-  for (i = 1000; i; i--)
+  for (i = 100000; i; i--)
     {
       if (inb (DSP_DATA_AVAIL) & 0x80)
-       {                       /*
-                                * wait for Data Ready
-                                */
+       {
          if (sbc_major == 0)
            sbc_major = inb (DSP_READ);
          else
@@ -1407,10 +1404,28 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
            }
        }
     }
+  restore_flags (flags);
+}
+
+long
+sb_dsp_init (long mem_start, struct address_info *hw_config)
+{
+  int             i;
+  int             ess_major = 0, ess_minor = 0;
+
+  int             mixer_type = 0;
+
+  dsp_get_vers (hw_config);
+
+  if (sbc_major == 0)
+    {
+      sb_reset_dsp ();
+      dsp_get_vers (hw_config);
+    }
 
   if (sbc_major == 0)
     {
-      printk ("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n",
+      printk ("\n\nFailed to get SB version (%x) - possible I/O conflict?\n\n",
              inb (DSP_DATA_AVAIL));
       sbc_major = 1;
     }
index 8f3261ac62a4408232d84c46017013deb37034bf..a1e86999215db5ec9410dcd7c18d82a5ab9c0c72 100644 (file)
@@ -998,13 +998,12 @@ static inline void recover_reusable_buffer_heads(void)
        unsigned long flags;
        
        save_flags(flags);
-       cli();
        while (reuse_list) {
+               cli();
                bh = reuse_list;
                reuse_list = bh->b_next_free;
                restore_flags(flags);
                put_unused_buffer_head(bh);
-               cli();
        }
 }
 
@@ -1062,7 +1061,7 @@ no_grow:
        return NULL;
 }
 
-static int bread_page(unsigned long address, kdev_t dev, int b[], int size)
+int brw_page(int rw, unsigned long address, kdev_t dev, int b[], int size, int bmap)
 {
        struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE];
        int block, nr;
@@ -1085,35 +1084,55 @@ static int bread_page(unsigned long address, kdev_t dev, int b[], int size)
                next->b_blocknr = block;
                next->b_count = 1;
                next->b_flushtime = 0;
-               clear_bit(BH_Dirty, &next->b_state);
-               clear_bit(BH_Req, &next->b_state);
                set_bit(BH_Uptodate, &next->b_state);
-               
-               if (!block) {
+
+               /* When we use bmap, we define block zero to represent
+                   a hole.  ll_rw_page, however, may legitimately
+                   access block zero, and we need to distinguish the
+                   two cases. 
+                  */
+               if (bmap && !block) {
                        memset(next->b_data, 0, size);
+                       next->b_count--;
                        continue;
                }
                tmp = get_hash_table(dev, block, size);
                if (tmp) {
                        if (!buffer_uptodate(tmp)) {
-                               ll_rw_block(READ, 1, &tmp);
+                               if (rw == READ)
+                                       ll_rw_block(READ, 1, &tmp);
                                wait_on_buffer(tmp);
                        }
-                       memcpy(next->b_data, tmp->b_data, size);
+                       if (rw == READ) 
+                               memcpy(next->b_data, tmp->b_data, size);
+                       else {
+                               memcpy(tmp->b_data, next->b_data, size);
+                               set_bit(BH_Dirty, &tmp->b_state);
+                       }
                        brelse(tmp);
+                       next->b_count--;
                        continue;
                }
-               clear_bit(BH_Uptodate, &next->b_state);
+               if (rw == READ)
+                       clear_bit(BH_Uptodate, &next->b_state);
+               else
+                       set_bit(BH_Dirty, &next->b_state);
                arr[nr++] = next;
        } while (prev = next, (next = next->b_this_page) != NULL);
        prev->b_this_page = bh;
        
        if (nr)
-               ll_rw_block(READ, nr, arr);
+               ll_rw_block(rw, nr, arr);
        else {
                page->locked = 0;
                page->uptodate = 1;
                wake_up(&page->wait);
+               next = bh;
+               do {
+                       next->b_next_free = reuse_list;
+                       reuse_list = next;
+                       next = next->b_this_page;
+               } while (next != bh);
        }
        ++current->maj_flt;
        return 0;
@@ -1138,6 +1157,64 @@ void mark_buffer_uptodate(struct buffer_head * bh, int on)
                clear_bit(BH_Uptodate, &bh->b_state);
 }
 
+void unlock_buffer(struct buffer_head * bh)
+{
+       struct buffer_head *tmp;
+       unsigned long flags;
+       struct page *page;
+
+       clear_bit(BH_Lock, &bh->b_state);
+       wake_up(&bh->b_wait);
+
+       if (!test_bit(BH_FreeOnIO, &bh->b_state))
+               return;
+       page = mem_map + MAP_NR(bh->b_data);
+       if (!page->locked) {
+               printk ("Whoops: unlock_buffer: "
+                       "async io complete on unlocked page\n");
+               return;
+       }
+       if (bh->b_count != 1) {
+               printk ("Whoops: unlock_buffer: b_count != 1 on async io.\n");
+               return;
+       }
+       /* Async buffer_heads are here only as labels for IO, and get
+           thrown away once the IO for this page is complete.  IO is
+           deemed complete once all buffers have been visited
+           (b_count==0) and are now unlocked. */
+       bh->b_count--;
+       for (tmp = bh; tmp=tmp->b_this_page, tmp!=bh; ) {
+               if (test_bit(BH_Lock, &tmp->b_state) || tmp->b_count)
+                       return;
+       }
+
+       /* OK, go ahead and complete the async IO on this page. */
+       save_flags(flags);
+       page->locked = 0;
+       wake_up(&page->wait);
+       cli();
+       tmp = bh;
+       do {
+               if (!test_bit(BH_FreeOnIO, &tmp->b_state)) {
+                       printk ("Whoops: unlock_buffer: "
+                               "async IO mismatch on page.\n");
+                       restore_flags(flags);
+                       return;
+               }
+               tmp->b_next_free = reuse_list;
+               reuse_list = tmp;
+               clear_bit(BH_FreeOnIO, &tmp->b_state);
+               tmp = tmp->b_this_page;
+       } while (tmp != bh);
+       restore_flags(flags);
+       if (page->free_after) {
+               extern int nr_async_pages;
+               nr_async_pages--;
+               page->free_after = 0;
+               free_page(page_address(page));
+       }
+}
+
 /*
  * Generic "readpage" function for block devices that have the normal
  * bmap functionality. This is most of the block device filesystems.
@@ -1167,38 +1244,11 @@ int generic_readpage(struct inode * inode, struct page * page)
        /* IO start */
        page->count++;
        address = page_address(page);
-       bread_page(address, inode->i_dev, nr, inode->i_sb->s_blocksize);
+       brw_page(READ, address, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
        free_page(address);
        return 0;
 }
 
-#if 0
-/*
- * bwrite_page writes a page out to the buffer cache and/or the physical device.
- * It's used for mmap writes (the same way bread_page() is used for mmap reads).
- */
-void bwrite_page(unsigned long address, kdev_t dev, int b[], int size)
-{
-       struct buffer_head * bh[MAX_BUF_PER_PAGE];
-       int i, j;
-
-       for (i=0, j=0; j<PAGE_SIZE ; i++, j+= size) {
-               bh[i] = NULL;
-               if (b[i])
-                       bh[i] = getblk(dev, b[i], size);
-       }
-       for (i=0, j=0; j<PAGE_SIZE ; i++, j += size, address += size) {
-               if (bh[i]) {
-                       memcpy(bh[i]->b_data, (void *) address, size);
-                       mark_buffer_uptodate(bh[i], 1);
-                       mark_buffer_dirty(bh[i], 0);
-                       brelse(bh[i]);
-               } else
-                       memset((void *) address, 0, size); /* ???!?!! */
-       }       
-}
-#endif
-
 /*
  * Try to increase the number of buffers available: the size argument
  * is used to determine what kind of buffers we want.
index 53639d2e00dc8f463670c61e4e54ab88b32318ef..86fe3dc4ca932f66b53d324a7325b3152b403c1c 100644 (file)
@@ -1,8 +1,16 @@
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/proc_fs.h>
 
+/*
+ * This is all required so that if we load all of scsi as a module,
+ * that the scsi code will be able to talk to the /proc/scsi handling
+ * in the procfs.
+ */
+extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
+                               off_t offset, int length, int inout);
+extern struct inode_operations proc_scsi_inode_operations;
+
 static struct symbol_table procfs_syms = {
 /* Should this be surrounded with "#ifdef CONFIG_MODULES" ? */
 #include <linux/symtab_begin.h>
@@ -12,11 +20,14 @@ static struct symbol_table procfs_syms = {
        X(generate_cluster),
        X(proc_net_inode_operations),
        X(proc_net),
-#ifdef CONFIG_SCSI /* Ugh... */
+
+       /*
+        * This is required so that if we load scsi later, that the
+        * scsi code can attach to /proc/scsi in the correct manner.
+        */
        X(proc_scsi),
        X(proc_scsi_inode_operations),
        X(dispatch_scsi_info_ptr),
-#endif
 #include <linux/symtab_end.h>
 };
 
index 0c1d7f788966bd187c58d032b31f7343a96303ef..e53ed65e9c48d72e7c965f6d43b076095ac6d4b8 100644 (file)
@@ -297,11 +297,7 @@ void proc_root_init(void)
                64, &proc_self_inode_operations,
        });
        proc_register(&proc_root, &proc_net);
-
-#ifdef CONFIG_SCSI
        proc_register(&proc_root, &proc_scsi);
-#endif
-
        proc_register(&proc_root, &proc_sys_root);
 
 #ifdef CONFIG_DEBUG_MALLOC
index 399b5901b68736541b28b0b539b380edefa4f0f9..1e333021fbb6fedcbc5bd1e34b6c5f6dc4c80821 100644 (file)
 #define NR_REQUEST     64
 
 /*
- * This is used in the elevator algorithm: Note that
- * reads always go before writes. This is natural: reads
- * are much more time-critical than writes.
+ * This is used in the elevator algorithm.  We don't prioritise reads
+ * over writes any more --- although reads are more time-critical than
+ * writes, by treating them equally we increase filesystem throughput.
+ * This turns out to give better overall performance.  -- sct
  */
 #define IN_ORDER(s1,s2) \
-((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
 ((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
-(s1)->sector < (s2)->sector)))))
+(s1)->sector < (s2)->sector)))
 
 /*
  * These will have to be changed to be aware of different buffer
index dcc4c6870e254834f008ebf650f2a908c3ee6458..46b94eb1326606b74058b76a0f2694213a6adaad 100644 (file)
@@ -598,8 +598,8 @@ extern struct buffer_head * breada(kdev_t dev,int block, int size,
 extern int generic_readpage(struct inode *, struct page *);
 extern int generic_file_read(struct inode *, struct file *, char *, int);
 extern int generic_mmap(struct inode *, struct file *, struct vm_area_struct *);
+extern int brw_page(int, unsigned long, kdev_t, int [], int, int);
 
-extern void bwrite_page(unsigned long addr,kdev_t dev,int b[],int size);
 extern void put_super(kdev_t dev);
 unsigned long generate_cluster(kdev_t dev, int b[], int size);
 extern kdev_t ROOT_DEV;
index 14fd7113c8c1baee7bd53f30dbba5b05542c7702..cff2d9769ee31465fc0dfe1297752d4a2d233e1d 100644 (file)
@@ -4,6 +4,9 @@
 #ifndef _LINUX_MM_H
 #include <linux/mm.h>
 #endif
+#ifndef _LINUX_PAGEMAP_H
+#include <linux/pagemap.h>
+#endif
 
 /*
  * Unlocked, temporary IO buffer_heads gets moved to the reuse_list
@@ -29,39 +32,8 @@ extern inline void lock_buffer(struct buffer_head * bh)
                __wait_on_buffer(bh);
 }
 
-extern inline void unlock_buffer(struct buffer_head * bh)
-{
-       struct buffer_head *tmp = bh;
-       int page_locked = 0;
-       unsigned long flags;
-       
-       clear_bit(BH_Lock, &bh->b_state);
-       wake_up(&bh->b_wait);
-       do {
-               if (test_bit(BH_Lock, &tmp->b_state)) {
-                       page_locked = 1;
-                       break;
-               }
-               tmp=tmp->b_this_page;
-       } while (tmp && tmp != bh);
-       save_flags(flags);
-       if (!page_locked) {
-               struct page *page = mem_map + MAP_NR(bh->b_data);
-               page->locked = 0;
-               wake_up(&page->wait);
-               tmp = bh;
-               cli();
-               do {
-                       if (test_bit(BH_FreeOnIO, &tmp->b_state)) {
-                               tmp->b_next_free = reuse_list;
-                               reuse_list = tmp;
-                               clear_bit(BH_FreeOnIO, &tmp->b_state);
-                       }
-                       tmp = tmp->b_this_page;
-               } while (tmp && tmp != bh);
-               restore_flags(flags);
-       }
-}
+void unlock_buffer(struct buffer_head *);
+
 
 /*
  * super-block locking. Again, interrupts may only unlock
index 7fa8a077b18ef4e062ba8ecf23791c285d0ce4af..2001acc19881a3c9d697a0a9a6988c9cc324a1f1 100644 (file)
@@ -122,7 +122,8 @@ typedef struct page {
                 error:1,
                 referenced:1,
                 locked:1,
-                unused:3,
+                free_after:1,
+                unused:2,
                 reserved:1;
        struct wait_queue *wait;
        struct page *next;
index 05524b69fbc8d56cf9740ff800cc153bbde04727..c5b142c76217194599d1457b7b2fcdfaec12cf51 100644 (file)
@@ -118,6 +118,7 @@ enum scsi_directory_inos {
        PROC_SCSI_ULTRASTOR,
        PROC_SCSI_7000FASST,
        PROC_SCSI_EATA2X,
+       PROC_SCSI_AM53C974,
        PROC_SCSI_SSC,
        PROC_SCSI_SCSI_DEBUG,   
        PROC_SCSI_NOT_PRESENT,
index aa8db50d70d4e399a949be56fa9c9f1e312feb7a..87390b3c332982a0d7a733e2192c63c3551aa3e5 100644 (file)
@@ -12,6 +12,8 @@
 #define SWP_USED       1
 #define SWP_WRITEOK    3
 
+#define SWAP_CLUSTER_MAX 32
+
 struct swap_info_struct {
        unsigned int flags;
        kdev_t swap_device;
@@ -20,6 +22,8 @@ struct swap_info_struct {
        unsigned char * swap_lockmap;
        int lowest_bit;
        int highest_bit;
+       int cluster_next;
+       int cluster_nr;
        int prio;                       /* swap priority */
        int pages;
        unsigned long max;
@@ -28,6 +32,7 @@ struct swap_info_struct {
 
 extern int nr_swap_pages;
 extern int nr_free_pages;
+extern int nr_async_pages;
 extern int min_free_pages;
 extern int free_pages_low;
 extern int free_pages_high;
@@ -40,17 +45,17 @@ struct sysinfo;
 /* linux/ipc/shm.c */
 extern int shm_swap (int, unsigned long);
 
-/* linux/mm/swap_clock.c */
-extern int try_to_free_page(int, unsigned long);
+/* linux/mm/vmscan.c */
+extern int try_to_free_page(int, unsigned long, int);
 
-/* linux/mm/swap_io.c */
-extern void rw_swap_page(int, unsigned long, char *);
+/* linux/mm/page_io.c */
+extern void rw_swap_page(int, unsigned long, char *, int);
 #define read_swap_page(nr,buf) \
-       rw_swap_page(READ,(nr),(buf))
+       rw_swap_page(READ,(nr),(buf),1)
 #define write_swap_page(nr,buf) \
-       rw_swap_page(WRITE,(nr),(buf))
+       rw_swap_page(WRITE,(nr),(buf),1)
 
-/* linux/mm/swap_mman.c */
+/* linux/mm/page_alloc.c */
 extern void swap_in(struct task_struct *, struct vm_area_struct *,
                    pte_t *, unsigned long, int);
 
index d3bcead2dc722f77bd82fe89cc18b2cf2077e887..4f4a3f18890c9543e9c15b953ccf5f7d87562c8b 100644 (file)
@@ -70,6 +70,7 @@ extern void generic_NCR5380_setup(char *str, int *intr);
 extern void aha152x_setup(char *str, int *ints);
 extern void aha1542_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 fdomain_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
@@ -239,6 +240,9 @@ struct {
 #ifdef CONFIG_SCSI_BUSLOGIC
        { "BusLogic=", BusLogic_Setup},
 #endif
+#ifdef CONFIG_SCSI_AM53C974
+        { "AM53C974=", AM53C974_setup},
+#endif
 #ifdef CONFIG_SCSI_NCR53C406A
        { "ncr53c406a=", NCR53c406a_setup},
 #endif
index 3c5b4315e72c190cfb5bd4c9ecfa75f0b812a173..e3a957704560ab39bd91e250d1d114f2a84e1c2d 100644 (file)
@@ -86,19 +86,6 @@ extern unsigned char aux_device_present, kbd_read_mask;
 #include <linux/smp.h>
 #endif
 
-#ifndef CONFIG_SCSI
-#if defined(CONFIG_PROC_FS)
-/*
- * This is all required so that if we load all of scsi as a module,
- * that the scsi code will be able to talk to the /proc/scsi handling
- * in the procfs.
- */
-extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
-                               off_t offset, int length, int inout);
-extern struct inode_operations proc_scsi_inode_operations;
-#endif
-#endif
-
 extern char *get_options(char *str, int *ints);
 extern void set_device_ro(int dev,int flag);
 extern struct file_operations * get_blkfops(unsigned int);
@@ -210,6 +197,7 @@ struct symbol_table symbol_table = {
        X(__bforget),
        X(ll_rw_block),
        X(__wait_on_buffer),
+       X(unlock_buffer),
        X(dcache_lookup),
        X(dcache_add),
        X(aout_core_dump),
@@ -435,14 +423,6 @@ struct symbol_table symbol_table = {
         */
        X(gendisk_head),
        X(resetup_one_dev),
-#if defined(CONFIG_PROC_FS)
-       /*
-        * This is required so that if we load scsi later, that the
-        * scsi code can attach to /proc/scsi in the correct manner.
-        */
-       X(proc_scsi_inode_operations),
-       X(dispatch_scsi_info_ptr),
-#endif
 #endif
        /* Added to make file system as module */
        X(set_writetime),
index 9774a630e765f87d5ef459bc9c334d3175a53fd4..d492c670a7aa67490064dff10b938dc618a294a4 100644 (file)
@@ -168,8 +168,7 @@ void update_vm_cache(struct inode * inode, unsigned long pos, const char * buf,
        if (page) {
                unsigned long addr;
 
-               if (!page->uptodate)
-                       sleep_on(&page->wait);
+               wait_on_page(page);
                addr = page_address(page);
                memcpy((void *) ((pos & ~PAGE_MASK) + addr), buf, count);
                free_page(addr);
index 4fbad0e36f9d5871d93e95de9a669994d4e96e52..e34fd7be185bbc75179b493b7eaa0a5927e1d957 100644 (file)
@@ -181,7 +181,7 @@ repeat:
                return 0;
        }
        restore_flags(flags);
-       if (priority != GFP_BUFFER && try_to_free_page(priority, limit))
+       if (priority != GFP_BUFFER && try_to_free_page(priority, limit, 1))
                goto repeat;
        return 0;
 }
index bf609ad042caeadc7b0482ee905e97b78fbc4af5..06cb491de53bcbe28a63157cbaaff7a402847456 100644 (file)
 
 static struct wait_queue * lock_queue = NULL;
 
-void rw_swap_page(int rw, unsigned long entry, char * buf)
+void rw_swap_page(int rw, unsigned long entry, char * buf, int wait)
 {
        unsigned long type, offset;
        struct swap_info_struct * p;
-
+       struct page *page;
+       
        type = SWP_TYPE(entry);
        if (type >= nr_swapfiles) {
                printk("Internal error: bad swap-device\n");
@@ -58,8 +59,17 @@ void rw_swap_page(int rw, unsigned long entry, char * buf)
                kstat.pswpin++;
        else
                kstat.pswpout++;
+       page = mem_map + MAP_NR(buf);
+       wait_on_page(page);
        if (p->swap_device) {
+               if (!wait) {
+                       page->count++;
+                       page->free_after = 1;
+                       nr_async_pages++;
+               }
                ll_rw_page(rw,p->swap_device,offset,buf);
+               if (wait)
+                       wait_on_page(page);
        } else if (p->swap_file) {
                struct inode *swapf = p->swap_file;
                unsigned int zones[PAGE_SIZE/512];
@@ -105,4 +115,3 @@ void rw_swap_page(int rw, unsigned long entry, char * buf)
                printk("rw_swap_page: lock already cleared\n");
        wake_up(&lock_queue);
 }
-
index 155d6fb05ed0a84f0c89878a2a654c7b7b9b26fa..c149ff6c609ebc212a70472d85d46635e5bbd400 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -42,6 +42,10 @@ int min_free_pages = 20;
 int free_pages_low = 30;
 int free_pages_high = 40;
 
+/* We track the number of pages currently being asynchronously swapped
+   out, so that we don't try to swap TOO many pages out at once */
+int nr_async_pages = 0;
+
 /*
  * Constants for the page aging mechanism: the maximum age (actually,
  * the maximum "youthfulness"); the quanta by which pages rejuvinate
index c2ad850e33538d5bc9782f7b279411c5afefa54d..1d359068786099035a60c0807e6067ea04f86247 100644 (file)
@@ -31,6 +31,46 @@ static struct {
 
 struct swap_info_struct swap_info[MAX_SWAPFILES];
 
+static inline int scan_swap_map(struct swap_info_struct *si)
+{
+       int offset;
+       /* 
+        * We try to cluster swap pages by allocating them
+        * sequentially in swap.  Once we've allocated
+        * SWAP_CLUSTER_MAX pages this way, however, we resort to
+        * first-free allocation, starting a new cluster.  This
+        * prevents us from scattering swap pages all over the entire
+        * swap partition, so that we reduce overall disk seek times
+        * between swap pages.  -- sct */
+       if (si->cluster_nr) {
+               while (si->cluster_next <= si->highest_bit) {
+                       offset = si->cluster_next++;
+                       if (si->swap_map[offset])
+                               continue;
+                       if (test_bit(offset, si->swap_lockmap))
+                               continue;
+                       si->cluster_nr--;
+                       goto got_page;
+               }
+       }
+       si->cluster_nr = SWAP_CLUSTER_MAX;
+       for (offset = si->lowest_bit; offset <= si->highest_bit ; offset++) {
+               if (si->swap_map[offset])
+                       continue;
+               if (test_bit(offset, si->swap_lockmap))
+                       continue;
+               si->lowest_bit = offset;
+got_page:
+               si->swap_map[offset] = 1;
+               nr_swap_pages--;
+               if (offset == si->highest_bit)
+                       si->highest_bit--;
+               si->cluster_next = offset;
+               return offset;
+       }
+       return 0;
+}
+
 unsigned long get_swap_page(void)
 {
        struct swap_info_struct * p;
@@ -44,26 +84,17 @@ unsigned long get_swap_page(void)
        while (1) {
                p = &swap_info[type];
                if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-                       for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) {
-                               if (p->swap_map[offset])
-                                 continue;
-                               if (test_bit(offset, p->swap_lockmap))
-                                 continue;
-                               p->swap_map[offset] = 1;
-                               nr_swap_pages--;
-                               if (offset == p->highest_bit)
-                                 p->highest_bit--;
-                               p->lowest_bit = offset;
-                               entry = SWP_ENTRY(type,offset);
-
-                               type = swap_info[type].next;
-                               if (type < 0 || p->prio != swap_info[type].prio) {
-                                   swap_list.next = swap_list.head;
-                               } else {
-                                   swap_list.next = type;
-                               }
-                               return entry;
+                       offset = scan_swap_map(p);
+                       if (!offset)
+                               continue;
+                       entry = SWP_ENTRY(type,offset);
+                       type = swap_info[type].next;
+                       if (type < 0 || p->prio != swap_info[type].prio) {
+                               swap_list.next = swap_list.head;
+                       } else {
+                               swap_list.next = type;
                        }
+                       return entry;
                }
                type = p->next;
                if (!wrapped) {
@@ -380,6 +411,7 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
        p->swap_lockmap = NULL;
        p->lowest_bit = 0;
        p->highest_bit = 0;
+       p->cluster_nr = 0;
        p->max = 1;
        p->next = -1;
        if (swap_flags & SWAP_FLAG_PREFER) {
@@ -399,7 +431,8 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
 
        if (S_ISBLK(swap_inode->i_mode)) {
                p->swap_device = swap_inode->i_rdev;
-
+               set_blocksize(p->swap_device, PAGE_SIZE);
+               
                filp.f_inode = swap_inode;
                filp.f_mode = 3; /* read write */
                error = blkdev_open(swap_inode, &filp);
index ae24853056c9ff0b34f2d00d2d1954e1bffc18ea..5ce79b0dc6abb0bff37614761cafe21d4548dc06 100644 (file)
@@ -5,7 +5,7 @@
  *
  *  Swap reorganised 29.12.95, Stephen Tweedie.
  *  kswapd added: 7.1.96  sct
- *  Version: $Id: vmscan.c,v 1.3.2.3 1996/01/17 02:43:11 linux Exp $
+ *  Version: $Id: vmscan.c,v 1.4.2.2 1996/01/20 18:22:47 linux Exp $
  */
 
 #include <linux/mm.h>
@@ -68,7 +68,7 @@ static void init_swap_timer(void);
  * have died while we slept).
  */
 static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
-       unsigned long address, pte_t * page_table, unsigned long limit)
+       unsigned long address, pte_t * page_table, unsigned long limit, int wait)
 {
        pte_t pte;
        unsigned long entry;
@@ -114,7 +114,7 @@ static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struc
                        set_pte(page_table, __pte(entry));
                        invalidate_page(vma, address);
                        tsk->nswap++;
-                       write_swap_page(entry, (char *) page);
+                       rw_swap_page(WRITE, entry, (char *) page, wait);
                }
                free_page(page);
                return 1;       /* we slept: the process may not exist any more */
@@ -154,7 +154,7 @@ static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struc
  */
 
 static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct * vma,
-       pmd_t *dir, unsigned long address, unsigned long end, unsigned long limit)
+       pmd_t *dir, unsigned long address, unsigned long end, unsigned long limit, int wait)
 {
        pte_t * pte;
        unsigned long pmd_end;
@@ -176,7 +176,7 @@ static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct *
        do {
                int result;
                tsk->swap_address = address + PAGE_SIZE;
-               result = try_to_swap_out(tsk, vma, address, pte, limit);
+               result = try_to_swap_out(tsk, vma, address, pte, limit, wait);
                if (result)
                        return result;
                address += PAGE_SIZE;
@@ -186,7 +186,7 @@ static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct *
 }
 
 static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct * vma,
-       pgd_t *dir, unsigned long address, unsigned long end, unsigned long limit)
+       pgd_t *dir, unsigned long address, unsigned long end, unsigned long limit, int wait)
 {
        pmd_t * pmd;
        unsigned long pgd_end;
@@ -206,7 +206,7 @@ static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct *
                end = pgd_end;
        
        do {
-               int result = swap_out_pmd(tsk, vma, pmd, address, end, limit);
+               int result = swap_out_pmd(tsk, vma, pmd, address, end, limit, wait);
                if (result)
                        return result;
                address = (address + PMD_SIZE) & PMD_MASK;
@@ -216,7 +216,7 @@ static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct *
 }
 
 static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
-       pgd_t *pgdir, unsigned long start, unsigned long limit)
+       pgd_t *pgdir, unsigned long start, unsigned long limit, int wait)
 {
        unsigned long end;
 
@@ -227,7 +227,7 @@ static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
 
        end = vma->vm_end;
        while (start < end) {
-               int result = swap_out_pgd(tsk, vma, pgdir, start, end, limit);
+               int result = swap_out_pgd(tsk, vma, pgdir, start, end, limit, wait);
                if (result)
                        return result;
                start = (start + PGDIR_SIZE) & PGDIR_MASK;
@@ -236,7 +236,7 @@ static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
        return 0;
 }
 
-static int swap_out_process(struct task_struct * p, unsigned long limit)
+static int swap_out_process(struct task_struct * p, unsigned long limit, int wait)
 {
        unsigned long address;
        struct vm_area_struct* vma;
@@ -257,7 +257,7 @@ static int swap_out_process(struct task_struct * p, unsigned long limit)
                address = vma->vm_start;
 
        for (;;) {
-               int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, limit);
+               int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, limit, wait);
                if (result)
                        return result;
                vma = vma->vm_next;
@@ -269,7 +269,7 @@ static int swap_out_process(struct task_struct * p, unsigned long limit)
        return 0;
 }
 
-static int swap_out(unsigned int priority, unsigned long limit)
+static int swap_out(unsigned int priority, unsigned long limit, int wait)
 {
        static int swap_task;
        int loop, counter;
@@ -308,7 +308,7 @@ static int swap_out(unsigned int priority, unsigned long limit)
                }
                if (!--p->swap_cnt)
                        swap_task++;
-               switch (swap_out_process(p, limit)) {
+               switch (swap_out_process(p, limit, wait)) {
                        case 0:
                                if (p->swap_cnt)
                                        swap_task++;
@@ -327,7 +327,7 @@ static int swap_out(unsigned int priority, unsigned long limit)
  * to be.  This works out OK, because we now do proper aging on page
  * contents. 
  */
-int try_to_free_page(int priority, unsigned long limit)
+int try_to_free_page(int priority, unsigned long limit, int wait)
 {
        static int state = 0;
        int i=6;
@@ -343,7 +343,7 @@ int try_to_free_page(int priority, unsigned long limit)
                                return 1;
                        state = 2;
                default:
-                       if (swap_out(i, limit))
+                       if (swap_out(i, limit, wait))
                                return 1;
                        state = 0;
                } while(i--);
@@ -359,7 +359,7 @@ int try_to_free_page(int priority, unsigned long limit)
 int kswapd(void *unused)
 {
        int i;
-       char *revision="$Revision: 1.3.2.3 $", *s, *e;
+       char *revision="$Revision: 1.4.2.2 $", *s, *e;
        
        current->session = 1;
        current->pgrp = 1;
@@ -400,7 +400,7 @@ int kswapd(void *unused)
                swapstats.wakeups++;
                /* Do the background pageout: */
                for (i=0; i < kswapd_ctl.maxpages; i++)
-                       try_to_free_page(GFP_KERNEL, ~0UL);
+                       try_to_free_page(GFP_KERNEL, ~0UL, 0);
        }
 }
 
@@ -410,8 +410,8 @@ int kswapd(void *unused)
 
 void swap_tick(void)
 {
-       if (nr_free_pages < free_pages_low ||
-           (nr_free_pages < free_pages_high && 
+       if ((nr_free_pages + nr_async_pages) < free_pages_low ||
+           ((nr_free_pages + nr_async_pages) < free_pages_high && 
             jiffies >= next_swap_jiffies)) {
                if (!kswapd_awake && kswapd_ctl.maxpages > 0) {
                        wake_up(&kswapd_wait);
index e172e30ffd8c1f483d89be03b97992468b50cd65..90cda68118aafe612384b1774ef22cda02b1ebf0 100644 (file)
@@ -263,7 +263,8 @@ function int () {
         while :; do
           readln "$1 ($2) [$def] " "$def"
           case "$ans" in
-             [1-9] | [1-9][0-9] | [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] ) 
+             -1 | [1-9] | [1-9][0-9] |\
+             [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] | [1-9][0-9][0-9][0-9][0-9]) 
                  define_int "$2" "$ans"
                  break;;
              * ) help "$2"
@@ -278,7 +279,7 @@ function int () {
 #
 function define_hex () {
        echo "$1=$2" >>$CONFIG
-       echo "#define $1 0x$2" >>$CONFIG_H
+       echo "#define $1 0x${2#*[x,X]}" >>$CONFIG_H
        eval "$1=$2"
 }
 
@@ -289,10 +290,15 @@ function define_hex () {
 #
 function hex () {
        def=$(eval echo "\${$2:-$3}")
+       def=${def#*[x,X]}
         while :; do
           readln "$1 ($2) [$def] " "$def"
+          ans=${ans#*[x,X]}
           case "$ans" in
-             [1-9] | [1-9][0-9] | [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] ) 
+             [0-9,a-f,A-F] |\
+             [0-9,a-f,A-F][0-9,a-f,A-F] |\
+             [0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F] |\
+             [0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F])\
                  define_hex "$2" "$ans"
                  break;;
              * ) help "$2"
index 016bbba24bec7c4649f25cdd622ffbd585b0970f..fd7faa4746c4df956fc8f8f19ed7898eea3bf495 100644 (file)
@@ -37,6 +37,7 @@ BEGIN{
        hasdep=0
        hasconfig=0
        needsconfig=0
+       incomment=0
        if(!(TOPDIR=ENVIRON["TOPDIR"])) {
                print "Environment variable TOPDIR is not set"
                exit 1
@@ -48,6 +49,30 @@ BEGIN{
        }
 }
 
+# eliminate comments
+{
+    # remove all comments fully contained on a single line
+       gsub("\\/\\*.*\\*\\/", "")
+       if (incomment) {
+               if ($0 ~ /\*\//) {
+                       incomment = 0;
+                       gsub(".*\\*\\/", "")
+               } else {
+                       next
+               }
+       } else {
+               # start of multi-line comment
+               if ($0 ~ /\/\*/)
+               {
+                       incomment = 1;
+                       sub("\\/\\*.*", "")
+               } else if ($0 ~ /\*\//) {
+                       incomment = 0;
+                       sub(".*\\*\\/", "")
+               }
+       }
+}
+
 /^[    ]*#[    ]*if.*[^A-Za-z_]CONFIG_/ {
        needsconfig=1
        if (!hasconfig) {
@@ -64,6 +89,7 @@ BEGIN{
                hasdep=0
                hasconfig=0
                needsconfig=0
+               incomment=0
                cmd=""
                LASTFILE=FILENAME
                depname=FILENAME