patch-1.3.5 linux/drivers/scsi/aic7xxx.c

Next file: linux/drivers/scsi/aic7xxx.h
Previous file: linux/drivers/scsi/aic7770.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.4/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
@@ -1,44 +1,67 @@
-/*
- *  @(#)aic7xxx.c 1.34 94/11/30 jda
+/*+M*************************************************************************
+ * Adaptec 274x/284x/294x device driver for Linux.
  *
- *  Adaptec 274x/284x/294x device driver for Linux.
- *  Copyright (c) 1994 The University of Calgary Department of Computer Science.
- *  
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *  
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *  
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Sources include the Adaptec 1740 driver (aha1740.c), the
- *  Ultrastor 24F driver (ultrastor.c), various Linux kernel
- *  source, the Adaptec EISA config file (!adp7771.cfg), the
- *  Adaptec AHA-2740A Series User's Guide, the Linux Kernel
- *  Hacker's Guide, Writing a SCSI Device Driver for Linux,
- *  the Adaptec 1542 driver (aha1542.c), the Adaptec EISA
- *  overlay file (adp7770.ovl), the Adaptec AHA-2740 Series
- *  Technical Reference Manual, the Adaptec AIC-7770 Data
- *  Book, the ANSI SCSI specification, the ANSI SCSI-2
- *  specification (draft 10c), ...
- *
- *  On a twin-bus adapter card, channel B is ignored.  Rationale:
- *  it would greatly complicate the sequencer and host driver code,
- *  and both busses are multiplexed on to the EISA bus anyway.  So
- *  I don't really see any technical advantage to supporting both.
- *
- *  As well, multiple adapter card using the same IRQ level are
- *  not supported.  It doesn't make sense to configure the cards
- *  this way from a performance standpoint.  Not to mention that
- *  the kernel would have to support two devices per registered IRQ.
- */
+ * Copyright (c) 1994 John Aycock
+ *   The University of Calgary Department of Computer Science.
+ *   All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the University of Calgary
+ *      Department of Computer Science and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * ----------------------------------------------------------------
+ *  Modified to include support for wide and twin bus adapters,
+ *  DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ *  and other rework of the code.
+ *
+ *  Parts of this driver are based on the FreeBSD driver by Justin
+ *  T. Gibbs.
+ *
+ *  A Boot time option was also added for not resetting the scsi bus.
+ *
+ *    Form:  aic7xxx=extended,no_reset
+ *
+ *    -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 04/03/95
+ *
+ *  $Id: aic7xxx.c,v 1.49 1995/06/28 05:41:09 deang Exp $
+ *-M*************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
 
 #include <stdarg.h>
 #include <asm/io.h>
@@ -47,1669 +70,3832 @@
 #include <linux/ioport.h>
 #include <linux/bios32.h>
 #include <linux/delay.h>
+#include <linux/sched.h>
 #include <linux/pci.h>
-
+#include <linux/proc_fs.h>
 #include "../block/blk.h"
 #include "sd.h"
 #include "scsi.h"
 #include "hosts.h"
 #include "aic7xxx.h"
 
+#define AIC7XXX_C_VERSION  "$Revision: 1.49 $"
+
+#define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
+#define MIN(a,b) ((a < b) ? a : b)
+
 /*
- *  There should be a specific return value for this in scsi.h, but
- *  it seems that most drivers ignore it.
+ * Defines for PCI bus support, testing twin bus support, DMAing of
+ * SCBs, and tagged queueing.
+ *
+ *   o PCI bus support - this has been implemented and working since
+ *     the December 1, 1994 release of this driver. If you don't have
+ *     a PCI bus and do not wish to configure your kernel with PCI
+ *     support, then make sure this define is set to the cprrect
+ *     define for PCI support (CONFIG_PCI) and configure your kernel
+ *     without PCI support (make config).
+ *
+ *   o Twin bus support - this has been tested and does work.
+ *
+ *   o DMAing of SCBs - thanks to Kai Makisara, this now works
+ *
+ *   o Tagged queueing - this driver is capable of tagged queueing
+ *     but I am unsure as to how well the higher level driver implements
+ *     tagged queueing. Therefore, the maximum commands per lun is
+ *     set to 2. If you want to implement tagged queueing, ensure
+ *     this define is not commented out.
+ *
+ *   o Sharing IRQs - allowed for sharing of IRQs. This will allow
+ *     for multiple aic7xxx host adapters sharing the same IRQ, but
+ *     not for sharing IRQs with other devices. The higher level
+ *     PCI code and interrupt handling needs to be modified to
+ *     support this.
+ *
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 03/11/95
  */
-#define DID_UNDERFLOW	DID_ERROR
 
-/* EISA/VL-bus stuff */
+/* Uncomment this for testing twin bus support. */
+#define AIC7XXX_TWIN_SUPPORT
 
-#define MINSLOT		1
-#define MAXSLOT		15
-#define SLOTBASE(x)	((x) << 12)
+/* Uncomment this for DMAing of SCBs. */
+#define AIC7XXX_USE_DMA
 
-#define MAXIRQ		15
+/* Uncomment this for tagged queueing. */
+/* #define AIC7XXX_TAGGED_QUEUEING */
 
-/* AIC-7770 offset definitions */
+/* Uncomment this for allowing sharing of IRQs. */
+#define AIC7XXX_SHARE_IRQS
 
-#define O_MINREG(x)	((x) + 0xc00)		/* i/o range to reserve */
-#define O_MAXREG(x)	((x) + 0xcbf)
+/* Set this to the delay in seconds after SCSI bus reset. */
+#define AIC7XXX_RESET_DELAY 15
 
-#define O_SCSISEQ(x)	((x) + 0xc00)		/* scsi sequence control */
-#define O_SCSISIGI(x)	((x) + 0xc03)		/* scsi control signal read */
-#define O_SCSISIGO(x)	((x) + 0xc03)		/* scsi control signal write */
-#define O_SCSIID(x)	((x) + 0xc05)		/* scsi id */
-#define O_SSTAT0(x)	((x) + 0xc0b)		/* scsi status register 0 */
-#define O_CLRSINT1(x)	((x) + 0xc0c)		/* clear scsi interrupt 1 */
-#define O_SSTAT1(x)	((x) + 0xc0c)		/* scsi status register 1 */
-#define O_SELID(x)	((x) + 0xc19)		/* [re]selection id */
-#define O_SBLKCTL(x)	((x) + 0xc1f)		/* scsi block control */
-#define O_SEQCTL(x)	((x) + 0xc60)		/* sequencer control */
-#define O_SEQRAM(x)	((x) + 0xc61)		/* sequencer ram data */
-#define O_SEQADDR(x)	((x) + 0xc62)		/* sequencer address (W) */
-#define O_BIDx(x)	((x) + 0xc80)		/* board id */
-#define O_BCTL(x)	((x) + 0xc84)		/* board control */
-#define O_HCNTRL(x)	((x) + 0xc87)		/* host control */
-#define O_SCBPTR(x)	((x) + 0xc90)		/* scb pointer */
-#define O_INTSTAT(x)	((x) + 0xc91)		/* interrupt status */
-#define O_ERROR(x)	((x) + 0xc92)		/* hard error */
-#define O_CLRINT(x)	((x) + 0xc92)		/* clear interrupt status */
-#define O_SCBCNT(x)	((x) + 0xc9a)		/* scb auto increment */
-#define O_QINFIFO(x)	((x) + 0xc9b)		/* queue in fifo */
-#define O_QINCNT(x)	((x) + 0xc9c)		/* queue in count */
-#define O_QOUTFIFO(x)	((x) + 0xc9d)		/* queue out fifo */
-#define O_QOUTCNT(x)	((x) + 0xc9e)		/* queue out count */
-#define O_SCBARRAY(x)	((x) + 0xca0)		/* scb array start */
-
-/* AIC-7870-only definitions */
-
-#define O_DSPCISTATUS(x) ((x) + 0xc86)		/* ??? */
-
-/* host adapter offset definitions */
-
-#define HA_REJBYTE(x)	((x) + 0xc31)		/* 1st message in byte */
-#define HA_MSG_FLAGS(x)	((x) + 0xc35)		/* outgoing message flag */
-#define HA_MSG_LEN(x)	((x) + 0xc36)		/* outgoing message length */
-#define HA_MSG_START(x)	((x) + 0xc37)		/* outgoing message body */
-#define HA_ARG_1(x)	((x) + 0xc4c)		/* sdtr <-> rate parameters */
-#define HA_ARG_2(x)	((x) + 0xc4d)
-#define HA_RETURN_1(x)	((x) + 0xc4c)
-#define HA_RETURN_2(x)	((x) + 0xc4d)
-#define HA_SIGSTATE(x)	((x) + 0xc4e)		/* value in SCSISIGO */
-#define HA_NEEDSDTR(x)	((x) + 0xc4f)		/* synchronous negotiation? */
-#define HA_SCBCOUNT(x)	((x) + 0xc56)		/* number of hardware SCBs */
-
-#define HA_SCSICONF(x)	((x) + 0xc5a)		/* SCSI config register */
-#define HA_INTDEF(x)	((x) + 0xc5c)		/* interrupt def'n register */
-#define HA_HOSTCONF(x)	((x) + 0xc5d)		/* host config def'n register */
+/*
+ * Uncomment this to always use scatter/gather lists.
+ * *NOTE: The sequencer must be changed also!
+ */
+#define AIC7XXX_USE_SG
 
-/* debugging code */
+/*
+ * Controller type and options
+ */
+typedef enum {
+  AIC_NONE,
+  AIC_274x,    /* EISA aic7770 */
+  AIC_284x,    /* VLB  aic7770 */
+  AIC_7870,    /* PCI  aic7870 */
+  AIC_7850,    /* PCI  aic7850 */
+  AIC_7872     /* PCI  aic7870 on 394x */
+} aha_type;
+
+typedef enum {
+  AIC_SINGLE,  /* Single Channel */
+  AIC_TWIN,    /* Twin Channel */
+  AIC_WIDE     /* Wide Channel */
+} aha_bus_type;
+
+typedef enum {
+  AIC_UNKNOWN,
+  AIC_ENABLED,
+  AIC_DISABLED
+} aha_status_type;
 
 /*
-#define AIC7XXX_DEBUG
-*/
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW   DID_ERROR
 
 /*
- *  If a parity error occurs during a data transfer phase, run the
- *  command to completion - it's easier that way - making a note
- *  of the error condition in this location.  This then will modify
- *  a DID_OK status into a DID_PARITY one for the higher-level SCSI
- *  code.
+ *  What we want to do is have the higher level scsi driver requeue
+ *  the command to us. There is no specific driver status for this
+ *  condition, but the higher level scsi driver will requeue the
+ *  command on a DID_BUS_BUSY error.
  */
-#define aic7xxx_parity(cmd)	((cmd)->SCp.Status)
+#define DID_RETRY_COMMAND DID_BUS_BUSY
 
 /*
- *  Since the sequencer code DMAs the scatter-gather structures
- *  directly from memory, we use this macro to assert that the
- *  kernel structure hasn't changed.
+ * EISA/VL-bus stuff
  */
-#define SG_STRUCT_CHECK(sg) \
-	((char *)&(sg).address - (char *)&(sg) != 0 ||	\
-	 (char *)&(sg).length  - (char *)&(sg) != 8 ||	\
-	 sizeof((sg).address) != 4 ||			\
-	 sizeof((sg).length)  != 4 ||			\
-	 sizeof(sg)	      != 12)
+#define MINSLOT		1
+#define MAXSLOT		15
+#define SLOTBASE(x)	((x) << 12)
+#define MAXIRQ		15
 
 /*
- *  "Static" structures.  Note that these are NOT initialized
- *  to zero inside the kernel - we have to initialize them all
- *  explicitly.
- *
- *  We support a maximum of one adapter card per IRQ level (see the
- *  rationale for this above).  On an interrupt, use the IRQ as an
- *  index into aic7xxx_boards[] to locate the card information.
+ * Standard EISA Host ID regs  (Offset from slot base)
  */
-static struct Scsi_Host *aic7xxx_boards[MAXIRQ + 1];
+#define HID0(x)         ((x) + 0xC80)   /* 0,1: msb of ID2, 2-7: ID1      */
+#define HID1(x)         ((x) + 0xC81)   /* 0-4: ID3, 5-7: LSB ID2         */
+#define HID2(x)         ((x) + 0xC82)   /* product                        */
+#define HID3(x)         ((x) + 0xC83)   /* firmware revision              */
 
 /*
- *  The maximum number of SCBs we could have for ANY type
- *  of card.  DON'T FORGET TO CHANGE THE SCB MASK IN THE
- *  SEQUENCER CODE IF THIS IS MODIFIED!
+ * AIC-7770 I/O range to reserve for a card
  */
-#define AIC7XXX_MAXSCB	16
+#define MINREG(x)	((x) + 0xC00ul)
+#define MAXREG(x)	((x) + 0xCBFul)
 
-struct aic7xxx_host {
-	int base;					/* card base address */
-	int maxscb;					/* hardware SCBs */
-	int startup;					/* intr type check */
-	int extended;					/* extended xlate? */
-	volatile int unpause;				/* value for HCNTRL */
-	volatile Scsi_Cmnd *SCB_array[AIC7XXX_MAXSCB];	/* active commands */
-};
+/* -------------------- AIC-7770 offset definitions ----------------------- */
 
-struct aic7xxx_host_config {
-	int irq;					/* IRQ number */
-	int base;					/* I/O base */
-	int maxscb;					/* hardware SCBs */
-	int unpause;					/* value for HCNTRL */
-	int scsi_id;					/* host SCSI id */
-	int extended;					/* extended xlate? */
-};
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+#define SCSISEQ(x)		((x) + 0xC00ul)
+#define		TEMODEO		0x80
+#define		ENSELO		0x40
+#define		ENSELI		0x20
+#define		ENRSELI		0x10
+#define		ENAUTOATNO	0x08
+#define		ENAUTOATNI	0x04
+#define		ENAUTOATNP	0x02
+#define		SCSIRSTO	0x01
 
-struct aic7xxx_scb {
-	unsigned char control;
-	unsigned char target_channel_lun;		/* 4/1/3 bits */
-	unsigned char SG_segment_count;
-	unsigned char SG_list_pointer[4];
-	unsigned char SCSI_cmd_pointer[4];
-	unsigned char SCSI_cmd_length;
-	unsigned char RESERVED[2];			/* must be zero */
-	unsigned char target_status;
-	unsigned char residual_data_count[3];
-	unsigned char residual_SG_segment_count;
-	unsigned char data_pointer[4];
-	unsigned char data_count[3];
-#if 0
-	/*
-	 *  No real point in transferring this to the
-	 *  SCB registers.
-	 */
-	unsigned char RESERVED[6];
-#endif
-};
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+#define SXFRCTL1(x)		((x) + 0xC02ul)
+#define		BITBUCKET	0x80
+#define		SWRAPEN		0x40
+#define		ENSPCHK		0x20
+#define		STIMESEL	0x18
+#define		ENSTIMER	0x04
+#define		ACTNEGEN	0x02
+#define		STPWEN		0x01		/* Powered Termination */
 
 /*
- *  NB.  This table MUST be ordered shortest period first.
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
  */
-static struct {
-	short period;
-	short rate;
-	char *english;
-} aic7xxx_synctab[] = {
-	100,	0,	"10.0",
-	125,	1,	"8.0",
-	150,	2,	"6.67",
-	175,	3,	"5.7",
-	200,	4,	"5.0",
-	225,	5,	"4.4",
-	250,	6,	"4.0",
-	275,	7,	"3.6"
-};
+#define SCSISIGI(x)		((x) + 0xC03ul)
+#define		CDI		0x80
+#define		IOI		0x40
+#define		MSGI		0x20
+#define		ATNI		0x10
+#define		SELI		0x08
+#define		BSYI		0x04
+#define		REQI		0x02
+#define		ACKI		0x01
 
-static int aic7xxx_synctab_max =
-	sizeof(aic7xxx_synctab) / sizeof(aic7xxx_synctab[0]);
+/*
+ * SCSI Contol Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus. Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+#define SCSISIGO(x)		((x) + 0xC03ul)
+#define		CDO		0x80
+#define		IOO		0x40
+#define		MSGO		0x20
+#define		ATNO		0x10
+#define		SELO		0x08
+#define		BSYO		0x04
+#define		REQO		0x02
+#define		ACKO		0x01
 
-enum aha_type {
-	T_NONE,
-	T_274X,
-	T_284X,
-	T_294X,
-	T_MAX
-};
+/*
+ * SCSI Rate
+ */
+#define SCSIRATE(x)		((x) + 0xC04ul)
 
-#ifdef AIC7XXX_DEBUG
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel
+ */
+#define SCSIID(x)		((x) + 0xC05ul)
+#define		TID		0xF0		/* Target ID mask */
+#define		OID		0x0F		/* Our ID mask */
 
-	extern int vsprintf(char *, const char *, va_list);
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+#define SSTAT0(x)		((x) + 0xC0Bul)
+#define		TARGET		0x80		/* Board is a target */
+#define		SELDO		0x40		/* Selection Done */
+#define		SELDI		0x20		/* Board has been selected */
+#define		SELINGO		0x10		/* Selection In Progress */
+#define		SWRAP		0x08		/* 24bit counter wrap */
+#define		SDONE		0x04		/* STCNT = 0x000000 */
+#define		SPIORDY		0x02		/* SCSI PIO Ready */
+#define		DMADONE		0x01		/* DMA transfer completed */
 
-	static
-	void debug(const char *fmt, ...)
-	{
-		va_list ap;
-		char buf[256];
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+#define CLRSINT1(x)		((x) + 0xC0Cul)
+#define		CLRSELTIMEO	0x80
+#define		CLRATNO		0x40
+#define		CLRSCSIRSTI	0x20
+/*  UNUSED			0x10 */
+#define		CLRBUSFREE	0x08
+#define		CLRSCSIPERR	0x04
+#define		CLRPHASECHG	0x02
+#define		CLRREQINIT	0x01
 
-		va_start(ap, fmt);
-		  vsprintf(buf, fmt, ap);
-		  printk(buf);
-		va_end(ap);
-	}
-
-	static
-	void debug_config(enum aha_type type, struct aic7xxx_host_config *p)
-	{
-		int ioport2, ioport3;
-
-		static char *BRT[T_MAX][16] = {
-			{ },					/* T_NONE */
-			{
-				"2",   "???", "???", "12",	/* T_274X */
-				"???", "???", "???", "28",
-				"???", "???", "???", "44",
-				"???", "???", "???", "60"
-			},
-			{
-				"2",  "4",  "8",  "12",		/* T_284X */
-				"16", "20", "24", "28",
-				"32", "36", "40", "44",
-				"48", "52", "56", "60"
-			},
-			{
-				"???", "???", "???", "???",	/* T_294X */
-				"???", "???", "???", "???",
-				"???", "???", "???", "???",
-				"???", "???", "???", "???"
-			}
-		};
-		static int DFT[4] = {
-			0, 50, 75, 100
-		};
-		static int SST[4] = {
-			256, 128, 64, 32
-		};
-
-		ioport2 = inb(HA_HOSTCONF(p->base));
-		ioport3 = inb(HA_SCSICONF(p->base));
-
-		switch (type) {
-		    case T_274X:
-			printk("AHA274X AT EISA SLOT %d:\n", p->base >> 12);
-			break;
-		    case T_284X:
-			printk("AHA284X AT SLOT %d:\n", p->base >> 12);
-			break;
-		    case T_294X:
-			printk("AHA294X (PCI-bus):\n");
-			break;
-		    default:
-			panic("aic7xxx debug_config: internal error\n");
-		}
+/*
+ * SCSI Status 1 (p. 3-24)
+ * These interrupt bits are of interest to the kernel driver
+ */
+#define SSTAT1(x)		((x) + 0xC0Cul)
+#define		SELTO		0x80
+#define		ATNTARG 	0x40
+#define		SCSIRSTI	0x20
+#define		PHASEMIS	0x10
+#define		BUSFREE		0x08
+#define		SCSIPERR	0x04
+#define		PHASECHG	0x02
+#define		REQINIT		0x01
 
-		printk("    irq %d\n"
-		       "    bus release time %s bclks\n"
-		       "    data fifo threshold %d%%\n",
-		       p->irq,
-		       BRT[type][(ioport2 >> 2) & 0xf],
-		       DFT[(ioport2 >> 6) & 0x3]);
-
-		printk("    SCSI CHANNEL A:\n"
-		       "        scsi id %d\n"
-		       "        scsi bus parity check %sabled\n"
-		       "        scsi selection timeout %d ms\n"
-		       "        scsi bus reset at power-on %sabled\n",
-		       ioport3 & 0x7,
-		       (ioport3 & 0x20) ? "en" : "dis",
-		       SST[(ioport3 >> 3) & 0x3],
-		       (ioport3 & 0x40) ? "en" : "dis");
-
-		if (type == T_274X) {
-			printk("        scsi bus termination %sabled\n",
-			       (ioport3 & 0x80) ? "en" : "dis");
-		}
-	}
+/*
+ * SCSI Interrrupt Mode 1 (pp. 3-28,29).
+ * Set bits in this register enable the corresponding
+ * interrupt source.
+ */
+#define	SIMODE1(x)		((x) + 0xC11ul)
+#define		ENSELTIMO	0x80
+#define		ENATNTARG	0x40
+#define		ENSCSIRST	0x20
+#define		ENPHASEMIS	0x10
+#define		ENBUSFREE	0x08
+#define		ENSCSIPERR	0x04
+#define		ENPHASECHG	0x02
+#define		ENREQINIT	0x01
 
-	static
-	void debug_rate(int base, int rate)
-	{
-		int target = inb(O_SCSIID(base)) >> 4;
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id. The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+#define SELID(x)		((x) + 0xC19ul)
+#define		SELID_MASK	0xF0
+#define		ONEBIT		0x08
+/*  UNUSED			0x07 */
 
-		if (rate) {
-			printk("aic7xxx: target %d now synchronous at %sMb/s\n",
-			       target,
-			       aic7xxx_synctab[(rate >> 4) & 0x7].english);
-		} else {
-			printk("aic7xxx: target %d using asynchronous mode\n",
-			       target);
-		}
-	}
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device.  In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device.  When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM.  When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.  See aic7xxx_read_eprom for detailed information on
+ * the protocol necessary to read the serial EEPROM.
+ */
+#define SEECTL(x)		((x) + 0xC1Eul)
+#define		EXTARBACK	0x80
+#define		EXTARBREQ	0x40
+#define		SEEMS		0x20
+#define		SEERDY		0x10
+#define		SEECS		0x08
+#define		SEECK		0x04
+#define		SEEDO		0x02
+#define		SEEDI		0x01
 
-#else
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection. In a twin channel configuration
+ * addresses 0x00-0x1E are gated to the appropriate channel based on this
+ * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+#define SBLKCTL(x)		((x) + 0xC1Ful)
+/*  UNUSED			0xC0 */
+#define		AUTOFLUSHDIS	0x20		/* used for Rev C check */
+/*  UNUSED			0x10 */
+#define		SELBUSB		0x08
+/*  UNUSED			0x04 */
+#define		SELWIDE		0x02
+/*  UNUSED			0x01 */
+#define		SELSINGLE	0x00
 
-#	define debug(fmt, args...)
-#	define debug_config(x)
-#	define debug_rate(x,y)
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+#define SEQCTL(x)		((x) + 0xC60ul)
+#define		PERRORDIS	0x80
+#define		PAUSEDIS	0x40
+#define		FAILDIS		0x20
+#define 	FASTMODE	0x10
+#define		BRKADRINTEN	0x08
+#define		STEP		0x04
+#define		SEQRESET	0x02
+#define		LOADRAM		0x01
 
-#endif AIC7XXX_DEBUG
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
+ * four bytes in sucessesion. The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+#define SEQRAM(x)		((x) + 0xC61ul)
 
 /*
- *  XXX - these options apply unilaterally to _all_ 274x/284x/294x
- *	  cards in the system.  This should be fixed, but then,
- *	  does anyone really have more than one in a machine?
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
  */
-static int aic7xxx_extended = 0;		/* extended translation on? */
+#define SEQADDR0(x)		((x) + 0xC62ul)
+#define SEQADDR1(x)		((x) + 0xC63ul)
 
-void aic7xxx_setup(char *s, int *dummy)
-{
-	int i;
-	char *p;
+#define ACCUM(x)		((x) + 0xC64ul)		/* accumulator */
 
-	static struct {
-		char *name;
-		int *flag;
-	} options[] = {
-		"extended",	&aic7xxx_extended,
-		NULL
-	};
+/*
+ * Board Control (p. 3-43)
+ */
+#define BCTL(x)		((x) + 0xC84ul)
+/*   RSVD			0xF0 */
+#define		ACE		0x08	/* Support for external processors */
+/*   RSVD			0x06 */
+#define		ENABLE		0x01
 
-	for (p = strtok(s, ","); p; p = strtok(NULL, ",")) {
-		for (i = 0; options[i].name; i++)
-			if (!strcmp(options[i].name, p))
-				*(options[i].flag) = !0;
-	}
-}
+#define BUSSPD(x)		((x) + 0xC86ul)	/* FIFO threshold bits ? */
 
-static
-void aic7xxx_getscb(int base, struct aic7xxx_scb *scb)
-{
-	/*
-	 *  This is almost identical to aic7xxx_putscb().
-	 */
-	outb(0x80, O_SCBCNT(base));	/* SCBAUTO */
+/*
+ * Host Control (p. 3-47) R/W
+ * Overal host control of the device.
+ */
+#define HCNTRL(x)		((x) + 0xC87ul)
+/*    UNUSED			0x80 */
+#define		POWRDN		0x40
+/*    UNUSED			0x20 */
+#define		SWINT		0x10
+#define		IRQMS		0x08
+#define		PAUSE		0x04
+#define		INTEN		0x02
+#define		CHIPRST		0x01
+#define		REQ_PAUSE	IRQMS | PAUSE | INTEN
+#define		UNPAUSE_274X	IRQMS | INTEN
+#define		UNPAUSE_284X	INTEN
+#define		UNPAUSE_294X	IRQMS | INTEN
 
-	asm volatile("cld\n\t"
-		     "rep\n\t"
-		     "insb"
-		     : /* no output */
-		     :"D" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
-		     :"di", "cx", "dx");
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the four SCBs into the SCBARRAY window.
+ */
+#define SCBPTR(x)		((x) + 0xC90ul)
 
-	outb(0, O_SCBCNT(base));
-}
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+#define INTSTAT(x)		((x) + 0xC91ul)
+#define		SEQINT_MASK	0xF0		/* SEQINT Status Codes */
+#define			BAD_PHASE	0x00
+#define			SEND_REJECT	0x10
+#define			NO_IDENT	0x20
+#define			NO_MATCH	0x30
+#define			MSG_SDTR	0x40
+#define			MSG_WDTR	0x50
+#define			MSG_REJECT	0x60
+#define			BAD_STATUS	0x70
+#define			RESIDUAL	0x80
+#define			ABORT_TAG	0x90
+#define			AWAITING_MSG	0xa0
+#define 	BRKADRINT 0x08
+#define		SCSIINT	  0x04
+#define		CMDCMPLT  0x02
+#define		SEQINT    0x01
+#define		INT_PEND  (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
 
 /*
- *  How much data should be transferred for this SCSI command?  Stop
- *  at segment sg_last if it's a scatter-gather command so we can
- *  compute underflow easily.
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors. You usually cannot recover from
+ * these without a full board reset.
  */
-static
-unsigned aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
-{
-	int i, segments;
-	unsigned length;
-	struct scatterlist *sg;
+#define ERROR(x)		((x) + 0xC92ul)
+/*    UNUSED			0xF0 */
+#define		PARERR		0x08
+#define		ILLOPCODE	0x04
+#define		ILLSADDR	0x02
+#define		ILLHADDR	0x01
 
-	segments = cmd->use_sg - sg_last;
-	sg = (struct scatterlist *)cmd->buffer;
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+#define CLRINT(x)		((x) + 0xC92ul)
+#define		CLRBRKADRINT	0x08
+#define		CLRSCSIINT	0x04
+#define		CLRCMDINT 	0x02
+#define		CLRSEQINT 	0x01
 
-	if (cmd->use_sg) {
-		for (i = length = 0;
-		     i < cmd->use_sg && i < segments;
-		     i++)
-		{
-			length += sg[i].length;
-		}
-	} else
-		length = cmd->request_bufflen;
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+#define SCBCNT(x)		((x) + 0xC9Aul)
+#define		SCBAUTO		0x80
+#define		SCBCNT_MASK	0x1F
 
-	return(length);
-}
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+#define QINFIFO(x)		((x) + 0xC9Bul)
 
-static
-void aic7xxx_sg_check(Scsi_Cmnd *cmd)
-{
-	int i;
-	struct scatterlist *sg = (struct scatterlist *)cmd->buffer;
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+#define QINCNT(x)		((x) + 0xC9Cul)
 
-	if (cmd->use_sg) {
-		for (i = 0; i < cmd->use_sg; i++)
-			if ((unsigned)sg[i].length > 0xffff)
-				panic("aic7xxx_sg_check: s/g segment > 64k\n");
-	}
-}
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+#define QOUTFIFO(x)		((x) + 0xC9Dul)
 
-static
-void aic7xxx_to_scsirate(unsigned char *rate,
-			 unsigned char transfer,
-			 unsigned char offset)
-{
-	int i;
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+#define QOUTCNT(x)		((x) + 0xC9Eul)
 
-	transfer *= 4;
+#define SCBARRAY(x)		((x) + 0xCA0ul)
 
-	for (i = 0; i < aic7xxx_synctab_max-1; i++) {
+/* ---------------- END AIC-7770 Register Definitions ----------------- */
 
-		if (transfer == aic7xxx_synctab[i].period) {
-			*rate = (aic7xxx_synctab[i].rate << 4) | (offset & 0xf);
-			return;
-		}
+/* --------------------- AIC-7870-only definitions -------------------- */
 
-		if (transfer > aic7xxx_synctab[i].period &&
-		    transfer < aic7xxx_synctab[i+1].period)
-		{
-			*rate = (aic7xxx_synctab[i+1].rate << 4) |
-				(offset & 0xf);
-			return;
-		}
-	}
-	*rate = 0;
-}
+#define DSPCISTATUS(x)	 	((x) + 0xC86ul)
+#define 	DFTHRESH        0xC0
+
+/* Scratch RAM offset definitions */
+
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the Linux sequencer code. If I can figure out
+ * how to read the EISA configuration info at probe time, the cards could
+ * be run without BIOS support installed
+ */
 
 /*
- *  Pause the sequencer and wait for it to actually stop - this
- *  is important since the sequencer can disable pausing for critical
- *  sections.
+ * 1 byte per target starting at this address for configuration values
  */
-#define PAUSE_SEQUENCER(p)	\
-	do {								\
-		outb(0xe, O_HCNTRL(p->base));	/* IRQMS|PAUSE|INTEN */	\
-									\
-		while ((inb(O_HCNTRL(p->base)) & 0x4) == 0)		\
-			;						\
-	} while (0)
+#define HA_TARG_SCRATCH(x)	((x) + 0xC20ul)
 
 /*
- *  Unpause the sequencer.  Unremarkable, yet done often enough to
- *  warrant an easy way to do it.
+ * The sequencer will stick the first byte of any rejected message here so
+ * we can see what is getting thrown away.
  */
-#define UNPAUSE_SEQUENCER(p)	\
-	outb(p->unpause, O_HCNTRL(p->base))	/* IRQMS|INTEN */
+#define HA_REJBYTE(x)		((x) + 0xC31ul)
 
 /*
- *  See comments in aic7xxx_loadram() wrt this.
+ * Bit vector of targets that have disconnection disabled.
  */
-#define RESTART_SEQUENCER(p)	\
-	do {							\
-		do {						\
-			outb(0x2, O_SEQCTL(p->base));		\
-								\
-		} while (inb(O_SEQADDR(p->base)) != 0 &&	\
-			 inb(O_SEQADDR(p->base) + 1) != 0);	\
-								\
-		UNPAUSE_SEQUENCER(p);				\
-	} while (0)
+#define	HA_DISC_DSB		((x) + 0xc32ul)
 
 /*
- *  Since we declared this using SA_INTERRUPT, interrupts should
- *  be disabled all through this function unless we say otherwise.
+ * Length of pending message
  */
-static
-void aic7xxx_isr(int irq)
-{
-	int base, intstat;
-	struct aic7xxx_host *p;
-	
-	p = (struct aic7xxx_host *)aic7xxx_boards[irq]->hostdata;
-	base = p->base;
+#define HA_MSG_LEN(x)		((x) + 0xC34ul)
 
-	/*
-	 *  Check the startup flag - if no commands have been queued,
-	 *  we probably have the interrupt type set wrong.  Reverse
-	 *  the stored value and the active one in the host control
-	 *  register.
-	 */
-	if (p->startup) {
-		p->unpause ^= 0x8;
-		outb(inb(O_HCNTRL(p->base)) ^ 0x8, O_HCNTRL(p->base));
-		return;
-	}
+/*
+ * Outgoing Message Body
+ */
+#define HA_MSG_START(x)		((x) + 0xC35ul)
 
-	/*
-	 *  Handle all the interrupt sources - especially for SCSI
-	 *  interrupts, we won't get a second chance at them.
-	 */
-	intstat = inb(O_INTSTAT(base));
+/*
+ * These are offsets into the card's scratch ram. Some of the values are
+ * specified in the AHA2742 technical reference manual and are initialized
+ * by the BIOS at boot time.
+ */
+#define HA_ARG_1(x)		((x) + 0xC4Aul)	/* sdtr <-> rate parameters */
+#define HA_RETURN_1(x)		((x) + 0xC4Aul)
+#define		SEND_SENSE	0x80
+#define		SEND_SDTR 	0x80
+#define		SEND_WDTR 	0x80
+#define		SEND_REJ	0x40
+
+#define HA_SIGSTATE(x)		((x) + 0xC4Bul)	/* value in SCSISIGO */
+#define HA_SCBCOUNT(x)		((x) + 0xC52ul)	/* number of hardware SCBs */
+
+#define HA_FLAGS(x)		((x) + 0xC53ul)	/* TWIN and WIDE bus flags */
+#define		SINGLE_BUS	0x00
+#define		TWIN_BUS	0x01
+#define		WIDE_BUS	0x02
+#define		ACTIVE_MSG	0x20
+#define		IDENTIFY_SEEN	0x40
+#define		RESELECTING	0x80
+
+#define HA_ACTIVE0(x)		((x) + 0xC54ul)	/* Active bits; targets 0-7 */
+#define HA_ACTIVE1(x)		((x) + 0xC55ul)	/* Active bits; targets 8-15 */
+#define	SAVED_TCL(x)		((x) + 0xC56ul)	/* Saved target, channel, LUN */
+#define WAITING_SCBH(x)		((x) + 0xC57ul) /* Head of disconnected targets list. */
+#define WAITING_SCBT(x)		((x) + 0xC58ul) /* Tail of disconnected targets list. */
+
+#define HA_SCSICONF(x)		((x) + 0xC5Aul)	/* SCSI config register */
+#define HA_INTDEF(x)		((x) + 0xC5Cul)	/* interrupt def'n register */
+#define HA_HOSTCONF(x)		((x) + 0xC5Dul)	/* host config def'n register */
+
+#define MSG_ABORT		0x06
+#define	MSG_BUS_DEVICE_RESET	0x0c
+#define BUS_8_BIT		0x00
+#define BUS_16_BIT		0x01
+#define BUS_32_BIT		0x02
 
-	if (intstat & 0x8) {				/* BRKADRINT */
 
-		panic("aic7xxx_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
-		      inb(O_ERROR(base)), inw(O_SEQADDR(base)));
-	}
+/*
+ *
+ * Define the format of the SEEPROM registers (16 bits).
+ *
+ */
+struct seeprom_config {
 
-	if (intstat & 0x4) {				/* SCSIINT */
-
-		int scbptr = inb(O_SCBPTR(base));
-		int status = inb(O_SSTAT1(base));
-		Scsi_Cmnd *cmd;
-
-		cmd = (Scsi_Cmnd *)p->SCB_array[scbptr];
-		if (!cmd) {
-			printk("aic7xxx_isr: no command for scb (scsiint)\n");
-			/*
-			 *  Turn off the interrupt and set status
-			 *  to zero, so that it falls through the
-			 *  reset of the SCSIINT code.
-			 */
-			outb(status, O_CLRSINT1(base));
-			UNPAUSE_SEQUENCER(p);
-			outb(0x4, O_CLRINT(base));	/* undocumented */
-			status = 0;
-		}
-		p->SCB_array[scbptr] = NULL;
+/*
+ * SCSI ID Configuration Flags
+ */
+#define CFXFER		0x0007		/* synchronous transfer rate */
+#define CFSYNCH		0x0008		/* enable synchronous transfer */
+#define CFDISC		0x0010		/* enable disconnection */
+#define CFWIDEB		0x0020		/* wide bus device */
+/* UNUSED		0x00C0 */
+#define CFSTART		0x0100		/* send start unit SCSI command */
+#define CFINCBIOS	0x0200		/* include in BIOS scan */
+#define CFRNFOUND	0x0400		/* report even if not found */
+/* UNUSED		0xF800 */
+  unsigned short device_flags[16];	/* words 0-15 */
 
-		/*
-		 *  Only the SCSI Status 1 register has information
-		 *  about exceptional conditions that we'd have a
-		 *  SCSIINT about; anything in SSTAT0 will be handled
-		 *  by the sequencer.  Note that there can be multiple
-		 *  bits set.
-		 */
-		if (status & 0x80) {			/* SELTO */
-			/*
-			 *  Hardware selection timer has expired.  Turn
-			 *  off SCSI selection sequence.
-			 */
-			outb(0, O_SCSISEQ(base));
-			cmd->result = DID_TIME_OUT << 16;
-
-			/*
-			 *  If there's an active message, it belongs to the
-			 *  command that is getting punted - remove it.
-			 */
-			outb(0, HA_MSG_FLAGS(base));
-
-			/*
-			 *  Shut off the offending interrupt sources, reset
-			 *  the sequencer address to zero and unpause it,
-			 *  then call the high-level SCSI completion routine.
-			 *
-			 *  WARNING!  This is a magic sequence!  After many
-			 *  hours of guesswork, turning off the SCSI interrupts
-			 *  in CLRSINT? does NOT clear the SCSIINT bit in
-			 *  INTSTAT.  By writing to the (undocumented, unused
-			 *  according to the AIC-7770 manual) third bit of
-			 *  CLRINT, you can clear INTSTAT.  But, if you do it
-			 *  while the sequencer is paused, you get a BRKADRINT
-			 *  with an Illegal Host Address status, so the
-			 *  sequencer has to be restarted first.
-			 */
-			outb(0x80, O_CLRSINT1(base));	/* CLRSELTIMO */
-			RESTART_SEQUENCER(p);
+/*
+ * BIOS Control Bits
+ */
+#define CFSUPREM	0x0001		/* support all removeable drives */
+#define CFSUPREMB	0x0002		/* support removeable drives for boot only */
+#define CFBIOSEN	0x0004		/* BIOS enabled */
+/* UNUSED		0x0008 */
+#define CFSM2DRV	0x0010		/* support more than two drives */
+/* UNUSED		0x0060 */
+#define CFEXTEND	0x0080		/* extended translation enabled */
+/* UNUSED		0xFF00 */
+  unsigned short bios_control;		/* word 16 */
 
-			outb(0x4, O_CLRINT(base));	/* undocumented */
-			cmd->scsi_done(cmd);
-		}
+/*
+ * Host Adapter Control Bits
+ */
+/* UNUSED		0x0003 */
+#define CFWSTERM	0x0008		/* SCSI high byte termination (wide card) */
+#define CFSTERM		0x0004		/* SCSI low byte termination (non-wide cards) */
+#define CFSPARITY	0x0010		/* SCSI parity */
+/* UNUSED		0x0020 */
+#define CFRESETB	0x0040		/* reset SCSI bus at IC initialization */
+/* UNUSED		0xFF80 */
+  unsigned short adapter_control;	/* word 17 */
 
-		if (status & 0x4) {			/* SCSIPERR */
-			/*
-			 *  A parity error has occurred during a data
-			 *  transfer phase.  Flag it and continue.
-			 */
-			printk("aic7xxx: parity error on target %d, lun %d\n",
-			       cmd->target,
-			       cmd->lun);
-			aic7xxx_parity(cmd) = DID_PARITY;
-
-			/*
-			 *  Clear interrupt and resume as above.
-			 */
-			outb(0x4, O_CLRSINT1(base));	/* CLRSCSIPERR */
-			UNPAUSE_SEQUENCER(p);
+/*
+ * Bus Release, Host Adapter ID
+ */
+#define CFSCSIID	0x000F		/* host adapter SCSI ID */
+/* UNUSED		0x00F0 */
+#define CFBRTIME	0xFF00		/* bus release time */
+  unsigned short brtime_id;		/* word 18 */
 
-			outb(0x4, O_CLRINT(base));	/* undocumented */
-		}
+/*
+ * Maximum targets
+ */
+#define CFMAXTARG	0x00FF	/* maximum targets */
+/* UNUSED		0xFF00 */
+  unsigned short max_targets;		/* word 19 */
 
-		if ((status & (0x8|0x4)) == 0 && status) {
-			/*
-			 *  We don't know what's going on.  Turn off the
-			 *  interrupt source and try to continue.
-			 */
-			printk("aic7xxx_isr: sstat1 = 0x%x\n", status);
-			outb(status, O_CLRSINT1(base));
-			UNPAUSE_SEQUENCER(p);
-			outb(0x4, O_CLRINT(base));	/* undocumented */
-		}
-	}
+  unsigned short res_1[11];		/* words 20-30 */
+  unsigned short checksum;		/* word 31 */
 
-	if (intstat & 0x2) {				/* CMDCMPLT */
+};
 
-		int complete, old_scbptr;
-		struct aic7xxx_scb scb;
-		unsigned actual;
-		Scsi_Cmnd *cmd;
 
-		/*
-		 *  The sequencer will continue running when it
-		 *  issues this interrupt.  There may be >1 commands
-		 *  finished, so loop until we've processed them all.
-		 */
-		do {
-			complete = inb(O_QOUTFIFO(base));
+#define AIC7XXX_DEBUG
 
-			cmd = (Scsi_Cmnd *)p->SCB_array[complete];
-			if (!cmd) {
-				printk("aic7xxx warning: "
-				       "no command for scb (cmdcmplt)\n");
-				continue;
-			}
-			p->SCB_array[complete] = NULL;
-			
-			PAUSE_SEQUENCER(p);
-
-			/*
-			 *  After pausing the sequencer (and waiting
-			 *  for it to stop), save its SCB pointer, then
-			 *  write in our completed one and read the SCB
-			 *  registers.  Afterwards, restore the saved
-			 *  pointer, unpause the sequencer and call the
-			 *  higher-level completion function - unpause
-			 *  first since we have no idea how long done()
-			 *  will take.
-			 */
-			old_scbptr = inb(O_SCBPTR(base));
-			outb(complete, O_SCBPTR(base));
-
-			aic7xxx_getscb(base, &scb);
-			outb(old_scbptr, O_SCBPTR(base));
-
-			UNPAUSE_SEQUENCER(p);
-
-			cmd->result = scb.target_status |
-				     (aic7xxx_parity(cmd) << 16);
-
-			/*
-			 *  Did we underflow?  At this time, there's only
-			 *  one other driver that bothers to check for this,
-			 *  and cmd->underflow seems to be set rather half-
-			 *  heartedly in the higher-level SCSI code.
-			 */
-			actual = aic7xxx_length(cmd,
-						scb.residual_SG_segment_count);
-
-			actual -= ((scb.residual_data_count[2] << 16) |
-				   (scb.residual_data_count[1] <<  8) |
-				   (scb.residual_data_count[0]));
-
-			if (actual < cmd->underflow) {
-				printk("aic7xxx: target %d underflow - "
-				       "wanted (at least) %u, got %u\n",
-				       cmd->target, cmd->underflow, actual);
-
-				cmd->result = scb.target_status |
-					     (DID_UNDERFLOW << 16);
-			}
-
-			cmd->scsi_done(cmd);
-
-			/*
-			 *  Clear interrupt status before checking
-			 *  the output queue again.  This eliminates
-			 *  a race condition whereby a command could
-			 *  complete between the queue poll and the
-			 *  interrupt clearing, so notification of the
-			 *  command being complete never made it back
-			 *  up to the kernel.
-			 */
-			outb(0x2, O_CLRINT(base));	/* CLRCMDINT */
+/*
+ * Pause the sequencer and wait for it to actually stop - this
+ * is important since the sequencer can disable pausing for critical
+ * sections.
+ */
+#define PAUSE_SEQUENCER(p) \
+  outb(p->pause, HCNTRL(p->base));			\
+  while ((inb(HCNTRL(p->base)) & PAUSE) == 0)		\
+    ;							\
 
-		} while (inb(O_QOUTCNT(base)));
-	}
+/*
+ * Unpause the sequencer. Unremarkable, yet done often enough to
+ * warrant an easy way to do it.
+ */
+#define UNPAUSE_SEQUENCER(p) \
+  outb(p->unpause, HCNTRL(p->base))
 
-	if (intstat & 0x1) {				/* SEQINT */
+/*
+ * Restart the sequencer program from address zero
+ */
+#define RESTART_SEQUENCER(p) \
+  do {							\
+    outb(SEQRESET | FASTMODE, SEQCTL(p->base));	\
+  } while (inb(SEQADDR0(p->base)) != 0 &&		\
+	   inb(SEQADDR1(p->base)) != 0);		\
+  UNPAUSE_SEQUENCER(p);
 
-		unsigned char transfer, offset, rate;
+/*
+ * If an error occurs during a data transfer phase, run the comand
+ * to completion - it's easier that way - making a note of the error
+ * condition in this location. This then will modify a DID_OK status
+ * into an appropriate error for the higher-level SCSI code.
+ */
+#define aic7xxx_error(cmd)	((cmd)->SCp.Status)
 
-		/*
-		 *  Although the sequencer is paused immediately on
-		 *  a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
-		 *  condition will have unpaused the sequencer before
-		 *  this point.
-		 */
-		PAUSE_SEQUENCER(p);
+/*
+ * Keep track of the targets returned status.
+ */
+#define aic7xxx_status(cmd)	((cmd)->SCp.sent_command)
 
-		switch (intstat & 0xf0) {
-		    case 0x00:
-			panic("aic7xxx_isr: unknown scsi bus phase\n");
-		    case 0x10:
-			debug("aic7xxx_isr warning: "
-			      "issuing message reject, 1st byte 0x%x\n",
-			      inb(HA_REJBYTE(base)));
-			break;
-		    case 0x20:
-			panic("aic7xxx_isr: reconnecting target %d "
-			      "didn't issue IDENTIFY message\n",
-			      (inb(O_SELID(base)) >> 4) & 0xf);
-		    case 0x30:
-			debug("aic7xxx_isr: sequencer couldn't find match "
-			      "for reconnecting target %d - issuing ABORT\n",
-			      (inb(O_SELID(base)) >> 4) & 0xf);
-			break;
-		    case 0x40:
-			transfer = inb(HA_ARG_1(base));
-			offset = inb(HA_ARG_2(base));
-			aic7xxx_to_scsirate(&rate, transfer, offset);
-			outb(rate, HA_RETURN_1(base));
-			debug_rate(base, rate);
-			break;
-		    default:
-			debug("aic7xxx_isr: seqint, "
-			      "intstat = 0x%x, scsisigi = 0x%x\n",
-			      intstat, inb(O_SCSISIGI(base)));
-			break;
-		}
+/*
+ * The position of the SCSI commands scb within the scb array.
+ */
+#define aic7xxx_position(cmd)	((cmd)->SCp.have_data_in)
 
-		outb(0x1, O_CLRINT(base));		/* CLRSEQINT */
-		UNPAUSE_SEQUENCER(p);
-	}
-}
+/*
+ * Since the sequencer code DMAs the scatter-gather structures
+ * directly from memory, we use this macro to assert that the
+ * kernel structure hasn't changed.
+ */
+#define SG_STRUCT_CHECK(sg) \
+  ((char *)&(sg).address - (char *)&(sg) != 0 ||  \
+   (char *)&(sg).length  - (char *)&(sg) != 8 ||  \
+   sizeof((sg).address) != 4 ||                   \
+   sizeof((sg).length)  != 4 ||                   \
+   sizeof(sg)           != 12)
 
 /*
- *  Probing for EISA boards: it looks like the first two bytes
- *  are a manufacturer code - three characters, five bits each:
- *
- *		 BYTE 0   BYTE 1   BYTE 2   BYTE 3
- *		?1111122 22233333 PPPPPPPP RRRRRRRR
+ * "Static" structures. Note that these are NOT initialized
+ * to zero inside the kernel - we have to initialize them all
+ * explicitly.
  *
- *  The characters are baselined off ASCII '@', so add that value
- *  to each to get the real ASCII code for it.  The next two bytes
- *  appear to be a product and revision number, probably vendor-
- *  specific.  This is what is being searched for at each port,
- *  and what should probably correspond to the ID= field in the
- *  ECU's .cfg file for the card - if your card is not detected,
- *  make sure your signature is listed in the array.
- *
- *  The fourth byte's lowest bit seems to be an enabled/disabled
- *  flag (rest of the bits are reserved?).
+ * We support a maximum of one adapter card per IRQ level (see the
+ * rationale for this above). On an interrupt, use the IRQ as an
+ * index into aic7xxx_boards[] to locate the card information.
  */
-
-static
-enum aha_type aic7xxx_probe(int slot, int s_base)
-{
-	int i;
-	unsigned char buf[4];
-
-	static struct {
-		int n;
-		unsigned char signature[sizeof(buf)];
-		enum aha_type type;
-	} S[] = {
-		4, { 0x04, 0x90, 0x77, 0x71 }, T_274X,	/* host adapter 274x */
-		4, { 0x04, 0x90, 0x77, 0x70 }, T_274X,	/* motherboard 274x  */
-		4, { 0x04, 0x90, 0x77, 0x56 }, T_284X,	/* 284x, BIOS enabled */
-	};
-
-	for (i = 0; i < sizeof(buf); i++) {
-		/*
-		 *  The VL-bus cards need to be primed by
-		 *  writing before a signature check.
-		 */
-		outb(0x80 + i, s_base);
-		buf[i] = inb(s_base + i);
-	}
-
-	for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) {
-		if (!memcmp(buf, S[i].signature, S[i].n)) {
-			/*
-			 *  Signature match on enabled card?
-			 */
-			if (inb(s_base + 4) & 1)
-				return(S[i].type);
-			printk("aic7xxx disabled at slot %d, ignored\n", slot);
-		}
-	}
-	return(T_NONE);
-}
+static struct Scsi_Host *aic7xxx_boards[MAXIRQ + 1];
 
 /*
- *  Return ' ' for plain 274x, 'T' for twin-channel, 'W' for
- *  wide channel, '?' for anything else.
+ * The driver keeps up to four scb structures per card in memory. Only the
+ * first 26 bytes of the structure are valid for the hardware, the rest used
+ * for driver level bookeeping. The driver is further optimized
+ * so that we only have to download the first 19 bytes since as long
+ * as we always use S/G, the last fields should be zero anyway.
  */
+#ifdef AIC7XXX_USE_SG
+#define SCB_DOWNLOAD_SIZE	19	/* amount to actually download */
+#else
+#define SCB_DOWNLOAD_SIZE	26
+#endif
 
-static
-char aic7xxx_type(int base)
-{
-	/*
-	 *  AIC-7770/7870s can be wired so that, on chip reset,
-	 *  the SCSI Block Control register indicates how many
-	 *  busses the chip is configured for.  The two high bits
-	 *  set indicate a 294x.
-	 */
-	switch (inb(O_SBLKCTL(base))) {
-	    case 0:
-	    case 0xc0:
-		return(' ');
-	    case 2:
-	    case 0xc2:
-		return('W');
-	    case 8:
-		return('T');
-	    default:
-		printk("aic7xxx has unknown bus configuration\n");
-		return('?');
-	}
-}
-
-static
-void aic7xxx_loadram(int base)
-{
-	static unsigned char seqprog[] = {
-		/*
-		 *  Each sequencer instruction is 29 bits
-		 *  long (fill in the excess with zeroes)
-		 *  and has to be loaded from least -> most
-		 *  significant byte, so this table has the
-		 *  byte ordering reversed.
-		 */
-#		include "aic7xxx_seq.h"
-	};
+#define SCB_UPLOAD_SIZE		19	/* amount to actually upload */
 
+struct aic7xxx_scb {
+/* ------------    Begin hardware supported fields    ---------------- */
+/*1 */  unsigned char control;
+#define SCB_NEEDWDTR 0x80                       /* Initiate Wide Negotiation */
+#define SCB_NEEDSDTR 0x40                       /* Initiate Sync Negotiation */
+#define SCB_NEEDDMA  0x08                       /* SCB needs to be DMA'd from
+						 * from host memory
+						 */
+#define SCB_REJ_MDP      0x80                   /* Reject MDP message */
+#define SCB_DISEN        0x40                   /* SCB Disconnect enable */
+#define SCB_TE           0x20                   /* Tag enable */
+/*      RESERVED         0x10 */
+#define SCB_WAITING      0x08                   /* Waiting */
+#define SCB_DIS          0x04                   /* Disconnected */
+#define SCB_TAG_TYPE     0x03
+#define         SIMPLE_QUEUE 0x00               /* Simple Queue */
+#define         HEAD_QUEUE   0x01               /* Head of Queue */
+#define         ORD_QUEUE    0x02               /* Ordered Queue */
+/*              ILLEGAL      0x03 */
+/*2 */  unsigned char target_channel_lun;       /* 4/1/3 bits */
+/*3 */  unsigned char SG_segment_count;
+/*7 */  unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/*11*/  unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/*12*/  unsigned char SCSI_cmd_length;
+/*14*/  unsigned char RESERVED[2];              /* must be zero */
+/*15*/  unsigned char target_status;
+/*18*/  unsigned char residual_data_count[3];
+/*19*/  unsigned char residual_SG_segment_count;
+/*23*/  unsigned char data_pointer[4] __attribute__ ((packed));
+/*26*/  unsigned char data_count[3];
+/*30*/  unsigned char host_scb[4] __attribute__ ((packed));
+/*31*/  u_char next_waiting;            /* Used to thread SCBs awaiting selection. */
+#define SCB_LIST_NULL 0x10              /* SCB list equivelent to NULL */
+#if 0
 	/*
-	 *  When the AIC-7770 is paused (as on chip reset), the
-	 *  sequencer address can be altered and a sequencer
-	 *  program can be loaded by writing it, byte by byte, to
-	 *  the sequencer RAM port - the Adaptec documentation
-	 *  recommends using REP OUTSB to do this, hence the inline
-	 *  assembly.  Since the address autoincrements as we load
-	 *  the program, reset it back to zero afterward.  Disable
-	 *  sequencer RAM parity error detection while loading, and
-	 *  make sure the LOADRAM bit is enabled for loading.
+	 *  No real point in transferring this to the
+	 *  SCB registers.
 	 */
-	outb(0x83, O_SEQCTL(base));	/* PERRORDIS|SEQRESET|LOADRAM */
+	unsigned char RESERVED[1];
+#endif
 
-	asm volatile("cld\n\t"
-		     "rep\n\t"
-		     "outsb"
-		     : /* no output */
-		     :"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base))
-		     :"si", "cx", "dx");
+	/*-----------------end of hardware supported fields----------------*/
+	struct aic7xxx_scb *next;	/* next ptr when in free list */
+	Scsi_Cmnd          *cmd;	/* Scsi_Cmnd for this scb */
+	int                 state;	/* current state of scb */
+#define SCB_FREE               0x00
+#define SCB_ACTIVE             0x01
+#define SCB_ABORTED            0x02
+#define SCB_DEVICE_RESET       0x04
+#define SCB_IMMED              0x08
+#define SCB_SENSE              0x10
+	unsigned int        position;       /* Position in scb array */
+#ifdef AIC7XXX_USE_SG
+	struct scatterlist  sg;
+	struct scatterlist  sense_sg;
+#endif
+	unsigned char       sense_cmd[6];   /* Allocate 6 characters for sense command */
+};
 
-	/*
-	 *  WARNING!  This is a magic sequence!  After extensive
-	 *  experimentation, it seems that you MUST turn off the
-	 *  LOADRAM bit before you play with SEQADDR again, else
-	 *  you will end up with parity errors being flagged on
-	 *  your sequencer program.  (You would also think that
-	 *  turning off LOADRAM and setting SEQRESET to reset the
-	 *  address to zero would work, but you need to do it twice
-	 *  for it to take effect on the address.  Timing problem?)
-	 */
-	outb(0, O_SEQCTL(base));
-	do {
-		/*
-		 *  Actually, reset it until
-		 *  the address shows up as
-		 *  zero just to be safe..
-		 */
-		outb(0x2, O_SEQCTL(base));	/* SEQRESET */
+static struct {
+  unsigned char errno;
+  char *errmesg;
+} hard_error[] = {
+  { ILLHADDR,  "Illegal Host Access" },
+  { ILLSADDR,  "Illegal Sequencer Address referrenced" },
+  { ILLOPCODE, "Illegal Opcode in sequencer program" },
+  { PARERR,    "Sequencer Ram Parity Error" }
+};
 
-	} while (inb(O_SEQADDR(base)) != 0 && inb(O_SEQADDR(base) + 1) != 0);
-}
+static unsigned char
+generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
 
-static
-void aha274x_config(struct aic7xxx_host_config *p, va_list ap)
-{
-	int base = va_arg(ap, int);
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB	16
 
-	/*
-	 *  Give the AIC-7770 a reset - reading the 274x's registers
-	 *  returns zeroes unless you do.  This forces a pause of the
-	 *  Sequencer.
-	 */
-	outb(1, O_HCNTRL(base));	/* CHIPRST */
+/*
+ * Define a structure used for each host adapter, only one per IRQ.
+ */
+struct aic7xxx_host {
+  int                      base;             /* card base address */
+  int                      maxscb;           /* hardware SCBs */
+  int                      numscb;           /* current number of scbs */
+  int                      extended;         /* extended xlate? */
+  aha_type                 type;             /* card type */
+  aha_bus_type             bus_type;         /* normal/twin/wide bus */
+  unsigned char            a_scanned;        /* 0 not scanned, 1 scanned */
+  unsigned char            b_scanned;        /* 0 not scanned, 1 scanned */
+  unsigned int             isr_count;        /* Interrupt count */
+  volatile unsigned char   unpause;          /* unpause value for HCNTRL */
+  volatile unsigned char   pause;            /* pause value for HCNTRL */
+  volatile unsigned short  needsdtr_copy;    /* default config */
+  volatile unsigned short  needsdtr;
+  volatile unsigned short  sdtr_pending;
+  volatile unsigned short  needwdtr_copy;    /* default config */
+  volatile unsigned short  needwdtr;
+  volatile unsigned short  wdtr_pending;
+  struct seeprom_config    seeprom;
+  int                      have_seeprom;
+  struct Scsi_Host        *next;             /* allow for multiple IRQs */
+  struct aic7xxx_scb       scb_array[AIC7XXX_MAXSCB];  /* active commands */
+  struct aic7xxx_scb      *free_scb;         /* list of free SCBs */
+};
 
-	p->base = base;
-	p->irq = inb(HA_INTDEF(base)) & 0xf;
-	p->scsi_id = inb(HA_SCSICONF(base)) & 0x7;
+struct aic7xxx_host_config {
+  int              irq;        /* IRQ number */
+  int              base;       /* I/O base */
+  int              maxscb;     /* hardware SCBs */
+  int              unpause;    /* unpause value for HCNTRL */
+  int              pause;      /* pause value for HCNTRL */
+  int              scsi_id;    /* host SCSI ID */
+  int              scsi_id_b;  /* host SCSI ID B channel for twin cards */
+  int              extended;   /* extended xlate? */
+  int              busrtime;   /* bus release time */
+  aha_type         type;       /* card type */
+  aha_bus_type     bus_type;   /* normal/twin/wide bus */
+  aha_status_type  parity;     /* bus parity enabled/disabled */
+  aha_status_type  low_term;   /* bus termination low byte */
+  aha_status_type  high_term;  /* bus termination high byte (wide cards only) */
+};
 
-	/*
-	 *  This value for HCNTRL may be changed in the ISR if we
-	 *  catch a spurious interrupt right away.
-	 */
-	p->unpause = 0xa;
+/*
+ * Valid SCSIRATE values. (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsiscfr reg to use that transfer rate.
+ */
+static struct {
+  short period;
+  short rate;
+  char *english;
+} aic7xxx_syncrates[] = {
+  { 100,   0,  "10.0" },
+  { 125,   1,  "8.0"  },
+  { 150,   2,  "6.67" },
+  { 175,   3,  "5.7"  },
+  { 200,   4,  "5.0"  },
+  { 225,   5,  "4.4"  },
+  { 250,   6,  "4.0"  },
+  { 275,   7,  "3.6"  }
+};
 
-	/*
-	 *  XXX - these are values that I don't know how to query
-	 *	  the hardware for.  Apparently some revision E
-	 *	  '7770s can have more SCBs, and I don't know how
-	 *	  to get the "extended translation" flag from the
-	 *	  EISA data area.
-	 */
-	p->maxscb = 4;
-	p->extended = aic7xxx_extended;
+static int num_aic7xxx_syncrates =
+    sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]);
 
-	/*
-	 *  A reminder until this can be detected automatically.
-	 */
-	printk("aha274x: extended translation %sabled\n",
-		p->extended ? "en" : "dis");
-}
+#ifdef AIC7XXX_DEBUG
+extern int vsprintf(char *, const char *, va_list);
 
-static
-void aha284x_config(struct aic7xxx_host_config *p, va_list ap)
+static void
+debug(const char *fmt, ...)
 {
-	int base = va_arg(ap, int);
+  va_list ap;
+  char buf[256];
 
-	/*
-	 *  Give the AIC-7770 a reset - this forces a pause of the
-	 *  Sequencer and returns everything to default values.
-	 */
-	outb(1, O_HCNTRL(base));	/* CHIPRST */
-
-	p->base = base;
-	p->unpause = 0x2;
-	p->irq = inb(HA_INTDEF(base)) & 0xf;
-	p->scsi_id = inb(HA_SCSICONF(base)) & 0x7;
-
-	/*
-	 *  XXX - these are values that I don't know how to query
-	 *	  the hardware for.  Apparently some revision E
-	 *	  '7770s can have more SCBs, and I don't know how
-	 *	  to get the "extended translation" flag from the
-	 *	  onboard memory.
-	 */
-	p->maxscb = 4;
-	p->extended = aic7xxx_extended;
-
-	/*
-	 *  A reminder until this can be detected automatically.
-	 */
-	printk("aha284x: extended translation %sabled\n",
-		p->extended ? "en" : "dis");
+  va_start(ap, fmt);
+  vsprintf(buf, fmt, ap);
+  printk(buf);
+  va_end(ap);
+}
+
+static void
+debug_config(struct aic7xxx_host_config *p)
+{
+  int host_conf, scsi_conf;
+  unsigned char brelease;
+  unsigned char dfthresh;
+
+  static int DFT[] = { 0, 50, 75, 100 };
+  static int SST[] = { 256, 128, 64, 32 };
+  static char *BUSW[] = { "", "-TWIN", "-WIDE" };
+
+  host_conf = inb(HA_HOSTCONF(p->base));
+  scsi_conf = inb(HA_SCSICONF(p->base));
+
+  /*
+   * The 7870 gets the bus release time and data FIFO threshold
+   * from the serial EEPROM (stored in the config structure) and
+   * scsi_conf register respectively.  The 7770 gets the bus
+   * release time and data FIFO threshold from the scsi_conf and
+   * host_conf registers respectively.
+   */
+  if ((p->type == AIC_274x) || (p->type == AIC_284x))
+  {
+    brelease = scsi_conf & 0x3F;
+    dfthresh = host_conf >> 6;
+  }
+  else
+  {
+    brelease = p->busrtime;
+    dfthresh = scsi_conf >> 6;
+  }
+  if (brelease == 0)
+  {
+    brelease = 2;
+  }
+
+  switch (p->type)
+  {
+    case AIC_274x:
+      printk("AIC7770%s AT EISA SLOT %d:\n", BUSW[p->bus_type], p->base >> 12);
+      break;
+
+    case AIC_284x:
+      printk("AIC7770%s AT VLB SLOT %d:\n", BUSW[p->bus_type], p->base >> 12);
+      break;
+
+    case AIC_7870:
+      printk("AIC7870%s (PCI-bus):\n", BUSW[p->bus_type]);
+      break;
+
+    case AIC_7850:
+      printk("AIC7850%s (PCI-bus):\n", BUSW[p->bus_type]);
+      break;
+
+    case AIC_7872:
+      printk("AIC7872%s (PCI-bus):\n", BUSW[p->bus_type]);
+      break;
+
+    default:
+      panic("aic7xxx debug_config: internal error\n");
+  }
+
+  printk("    irq %d\n"
+	 "    bus release time %d bclks\n"
+	 "    data fifo threshold %d%%\n",
+	 p->irq,
+	 brelease,
+	 DFT[dfthresh]);
+
+  printk("    SCSI CHANNEL A:\n"
+	 "        scsi id %d\n"
+	 "        scsi selection timeout %d ms\n"
+	 "        scsi bus reset at power-on %sabled\n",
+	 scsi_conf & 0x07,
+	 SST[(scsi_conf >> 3) & 0x03],
+	 (scsi_conf & 0x40) ? "en" : "dis");
+
+  if (((p->type == AIC_274x) || (p->type == AIC_284x)) && p->parity == AIC_UNKNOWN)
+  { /* Set the parity for 7770 based cards. */
+    p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
+  }
+  if (p->parity != AIC_UNKNOWN)
+  {
+    printk("        scsi bus parity %sabled\n",
+	   (p->parity == AIC_ENABLED) ? "en" : "dis");
+  }
+
+  if (p->type == AIC_274x)
+  {
+    p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED;
+  }
+  if (p->low_term != AIC_UNKNOWN)
+  {
+    printk("        scsi bus termination (low byte) %sabled\n",
+	  (p->low_term == AIC_ENABLED) ? "en" : "dis");
+  }
+  if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN))
+  {
+    printk("        scsi bus termination (high byte) %sabled\n",
+	  (p->high_term == AIC_ENABLED) ? "en" : "dis");
+  }
 }
+#else
+#  define debug(fmt, args...)
+#  define debug_config(x)
+#endif AIC7XXX_DEBUG
 
-static
-void aha294x_config(struct aic7xxx_host_config *p, va_list ap)
-{
-	int error;
-	unsigned long io_port;
-	unsigned char bus, device_fn, irq;
-
-	bus = va_arg(ap, unsigned char);
-	device_fn = va_arg(ap, unsigned char);
-
-	/*
-	 *  Read esundry information from PCI BIOS.
-	 */
-	error = pcibios_read_config_dword(bus,
-					  device_fn,
-					  PCI_BASE_ADDRESS_0,
-					  &io_port);
-	if (error) {
-		panic("aha294x_config: error %s reading i/o port\n",
-		      pcibios_strerror(error));
-	}
-
-	error = pcibios_read_config_byte(bus,
-					 device_fn,
-					 PCI_INTERRUPT_LINE,
-					 &irq);
-	if (error) {
-		panic("aha294x_config: error %s reading irq\n",
-		      pcibios_strerror(error));
-	}
-
-	/*
-	 *  Make the base I/O register look like EISA and VL-bus.
-	 */
-	p->base = io_port - 0xc01;
-
-	/*
-	 *  Give the AIC-7870 a reset - this forces a pause of the
-	 *  Sequencer and returns everything to default values.
-	 */
-	outb(1, O_HCNTRL(p->base));	/* CHIPRST */
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ *       cards in the system. This should be fixed, but then,
+ *       does anyone really have more than one in a machine?
+ */
+static int aic7xxx_extended = 0;	/* extended translation on? */
+static int aic7xxx_no_reset = 0;	/* no resetting of SCSI bus */
 
-	p->irq = irq;
-	p->maxscb = 16;
-	p->unpause = 0xa;
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_setup
+ *
+ * Description:
+ *   Handle Linux boot parameters.
+ *-F*************************************************************************/
+void
+aic7xxx_setup(char *s, int *dummy)
+{
+  int   i;
+  char *p;
+
+  static struct {
+    char *name;
+    int *flag;
+  } options[] = {
+    { "extended",    &aic7xxx_extended },
+    { "no_reset",    &aic7xxx_no_reset },
+    { NULL,          NULL}
+  };
+
+  for (p = strtok(s, ","); p; p = strtok(NULL, ","))
+  {
+    for (i = 0; options[i].name; i++)
+    {
+      if (!strcmp(options[i].name, p))
+      {
+	*(options[i].flag) = !0;
+      }
+    }
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_loadseq
+ *
+ * Description:
+ *   Load the sequencer code into the controller memory.
+ *-F*************************************************************************/
+static void
+aic7xxx_loadseq(int base)
+{
+  static unsigned char seqprog[] = {
+    /*
+     * Each sequencer instruction is 29 bits
+     * long (fill in the excess with zeroes)
+     * and has to be loaded from least -> most
+     * significant byte, so this table has the
+     * byte ordering reversed.
+     */
+#   include "aic7xxx_seq.h"
+  };
+
+  /*
+   * When the AIC-7770 is paused (as on chip reset), the
+   * sequencer address can be altered and a sequencer
+   * program can be loaded by writing it, byte by byte, to
+   * the sequencer RAM port - the Adaptec documentation
+   * recommends using REP OUTSB to do this, hence the inline
+   * assembly. Since the address autoincrements as we load
+   * the program, reset it back to zero afterward. Disable
+   * sequencer RAM parity error detection while loading, and
+   * make sure the LOADRAM bit is enabled for loading.
+   */
+  outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL(base));
+
+  asm volatile("cld\n\t"
+	       "rep\n\t"
+	       "outsb"
+	       : /* no output */
+	       :"S" (seqprog), "c" (sizeof(seqprog)), "d" (SEQRAM(base))
+	       :"si", "cx", "dx");
+
+  /*
+   * WARNING!  This is a magic sequence!  After extensive
+   * experimentation, it seems that you MUST turn off the
+   * LOADRAM bit before you play with SEQADDR again, else
+   * you will end up with parity errors being flagged on
+   * your sequencer program. (You would also think that
+   * turning off LOADRAM and setting SEQRESET to reset the
+   * address to zero would work, but you need to do it twice
+   * for it to take effect on the address. Timing problem?)
+   */
+  do {
+    /*
+     * Actually, reset it until
+     * the address shows up as
+     * zero just to be safe..
+     */
+    outb(SEQRESET | FASTMODE, SEQCTL(base));
+  } while ((inb(SEQADDR0(base)) != 0) && (inb(SEQADDR1(base)) != 0));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_delay
+ *
+ * Description:
+ *   Delay for specified amount of time.
+ *-F*************************************************************************/
+static void
+aic7xxx_delay(int seconds)
+{
+  unsigned long i;
 
-	/*
-	 *  XXX - these are values that I don't know how to query
-	 *	  the hardware for, so for now the SCSI host ID is
-	 *	  hardwired to 7, and the "extended translation"
-	 *	  flag is taken from boot-time flags.
-	 */
-	p->scsi_id = 7;
-	p->extended = aic7xxx_extended;
+  i = jiffies + (seconds * 100);  /* compute time to stop */
 
-	/*
-	 *  XXX - force data fifo threshold to 100%.  Why does this
-	 *	  need to be done?
-	 */
-#	define	DFTHRESH	3
+  while (jiffies < i)
+  {
+    ;  /* Do nothing! */
+  }
+}
 
-	outb(DFTHRESH << 6, O_DSPCISTATUS(p->base));
-	outb(p->scsi_id | (DFTHRESH << 6), HA_SCSICONF(p->base));
+/*+F*************************************************************************
+ * Function:
+ *   rcs_version
+ *
+ * Description:
+ *   Return a string containing just the RCS version number from either
+ *   an Id or Revison RCS clause.
+ *-F*************************************************************************/
+const char *
+rcs_version(const char *version_info)
+{
+  static char buf[10];
+  char *bp, *ep;
+
+  bp = NULL;
+  strcpy(buf, "????");
+  if (!strncmp(version_info, "$Id: ", 5))
+  {
+    if ((bp = strchr(version_info, ' ')) != NULL)
+    {
+      bp++;
+      if ((bp = strchr(bp, ' ')) != NULL)
+      {
+	bp++;
+      }
+    }
+  }
+  else
+  {
+    if (!strncmp(version_info, "$Revision: ", 11))
+    {
+      if ((bp = strchr(version_info, ' ')) != NULL)
+      {
+	bp++;
+      }
+    }
+  }
+
+  if (bp != NULL)
+  {
+    if ((ep = strchr(bp, ' ')) != NULL)
+    {
+      register int len = ep - bp;
+
+      strncpy(buf, bp, len);
+      buf[len] = '\0';
+    }
+  }
+
+  return buf;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_info
+ *
+ * Description:
+ *   Return a string describing the driver.
+ *-F*************************************************************************/
+const char *
+aic7xxx_info(struct Scsi_Host *notused)
+{
+  static char buffer[128];
+
+  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
+  strcat(buffer, "/");
+  strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
+  strcat(buffer, "/");
+  strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+
+  return buffer;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_putscb
+ *
+ * Description:
+ *   Transfer a SCB to the controller.
+ *-F*************************************************************************/
+static void
+aic7xxx_putscb(int base, struct aic7xxx_scb *scb)
+{
+#ifdef AIC7XXX_USE_DMA
+  /*
+   * All we need to do, is to output the position
+   * of the SCB in the SCBARRAY to the QINFIFO
+   * of the host adapter.
+   */
+  outb(scb->position, QINFIFO(base));
+#else
+  /*
+   * By turning on the SCB auto increment, any reference
+   * to the SCB I/O space postincrements the SCB address
+   * we're looking at. So turn this on and dump the relevant
+   * portion of the SCB to the card.
+   */
+  outb(SCBAUTO, SCBCNT(base));
+
+  asm volatile("cld\n\t"
+	       "rep\n\t"
+	       "outsb"
+	       : /* no output */
+	       :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base))
+	       :"si", "cx", "dx");
 
-	/*
-	 *  A reminder until this can be detected automatically.
-	 */
-	printk("aha294x: extended translation %sabled\n",
-		p->extended ? "en" : "dis");
+  outb(0, SCBCNT(base));
+#endif
 }
 
-static
-int aic7xxx_register(Scsi_Host_Template *template, enum aha_type type, ...)
-{
-	va_list ap;
-	int i, base;
-	struct Scsi_Host *host;
-	struct aic7xxx_host *p;
-	struct aic7xxx_host_config config;
-
-	va_start(ap, type);
-	
-	switch (type) {
-	    case T_274X:
-		aha274x_config(&config, ap);
-		break;
-	    case T_284X:
-		aha284x_config(&config, ap);
-		break;
-	    case T_294X:
-		aha294x_config(&config, ap);
-		break;
-	    default:
-		panic("aic7xxx_register: internal error\n");
-	}
-	va_end(ap);
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_putdmascb
+ *
+ * Description:
+ *   DMA a SCB to the controller.
+ *-F*************************************************************************/
+static void
+aic7xxx_putdmascb(int base, struct aic7xxx_scb *scb)
+{
+  /*
+   * By turning on the SCB auto increment, any reference
+   * to the SCB I/O space postincrements the SCB address
+   * we're looking at. So turn this on and dump the relevant
+   * portion of the SCB to the card.
+   */
+  outb(SCBAUTO, SCBCNT(base));
+
+  asm volatile("cld\n\t"
+	       "rep\n\t"
+	       "outsb"
+	       : /* no output */
+	       :"S" (scb), "c" (31), "d" (SCBARRAY(base))
+	       :"si", "cx", "dx");
+
+  outb(0, SCBCNT(base));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_getscb
+ *
+ * Description:
+ *   Get a SCB from the controller.
+ *-F*************************************************************************/
+static void
+aic7xxx_getscb(int base, struct aic7xxx_scb *scb)
+{
+  /*
+   * This is almost identical to aic7xxx_putscb().
+   */
+  outb(SCBAUTO, SCBCNT(base));
+
+  asm volatile("cld\n\t"
+	       "rep\n\t"
+	       "insb"
+	       : /* no output */
+	       :"D" (scb), "c" (SCB_UPLOAD_SIZE), "d" (SCBARRAY(base))
+	       :"di", "cx", "dx");
+
+  outb(0, SCBCNT(base));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_length
+ *
+ * Description:
+ *   How much data should be transferred for this SCSI command? Stop
+ *   at segment sg_last if it's a scatter-gather command so we can
+ *   compute underflow easily.
+ *-F*************************************************************************/
+static unsigned
+aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
+{
+  int i, segments;
+  unsigned length;
+  struct scatterlist *sg;
+
+  segments = cmd->use_sg - sg_last;
+  sg = (struct scatterlist *) cmd->buffer;
+
+  if (cmd->use_sg)
+  {
+    for (i = length = 0; i < cmd->use_sg && i < segments; i++)
+    {
+      length += sg[i].length;
+    }
+  }
+  else
+  {
+    length = cmd->request_bufflen;
+  }
+
+  return(length);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scsirate
+ *
+ * Description:
+ *   Look up the valid period to SCSIRATE conversion in our table
+ *-F*************************************************************************/
+static void
+aic7xxx_scsirate(unsigned char *scsirate, unsigned char period,
+		 unsigned char offset, int target)
+{
+  int i;
+
+  for (i = 0; i < num_aic7xxx_syncrates; i++)
+  {
+    if ((aic7xxx_syncrates[i].period - period) >= 0)
+    {
+      *scsirate = (aic7xxx_syncrates[i].rate << 4) | (offset & 0x0F);
+      printk("aic7xxx: target %d now synchronous at %sMb/s, offset = 0x%x\n",
+	     target, aic7xxx_syncrates[i].english, offset);
+      return;
+    }
+  }
+
+  /*
+   * Default to asyncronous transfer
+   */
+  *scsirate = 0;
+  printk("aic7xxx: target %d using asynchronous transfers\n", target);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_isr
+ *
+ * Description:
+ *   SCSI controller interrupt handler.
+ *
+ *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ *         be disabled all through this function unless we say otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, struct pt_regs * regs)
+{
+  int base, intstat;
+  struct aic7xxx_host *p;
+  struct aic7xxx_scb *scb;
+  unsigned char active, ha_flags, transfer;
+  unsigned char scsi_id, bus_width;
+  unsigned char offset, rate, scratch;
+  unsigned char max_offset;
+  unsigned char head, tail;
+  unsigned short target_mask;
+  long flags;
+  void *addr;
+  int actual;
+  int target, tcl;
+  int scbptr;
+  Scsi_Cmnd *cmd;
+#if 0
+static int_count = 0;
+#endif
 
-	base = config.base;
+  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+#ifdef AIC7XXX_SHARE_IRQS
+  /*
+   * Search for the host with a pending interrupt.
+   */
+  while ((p != NULL) && !(inb(INTSTAT(p->base)) & INT_PEND))
+  {
+    p = (struct aic7xxx_host *) p->next->hostdata;
+  }
+  if (p == NULL)
+  {
+    printk("aic7xxx_isr: Encountered spurious interrupt.\n");
+    return;
+  }
+#endif
+  base = p->base;
+  if (p->isr_count == 0xffffffff)
+  {
+    p->isr_count = 0;
+  }
+  else
+  {
+    p->isr_count = p->isr_count + 1;
+  }
+  if ((p->a_scanned == 0) && (p->isr_count == 1))
+  {
+    /* Allow for one interrupt when the card is enabled. */
+    return;
+  }
+
+  /*
+   * Handle all the interrupt sources - especially for SCSI
+   * interrupts, we won't get a second chance at them.
+   */
+  intstat = inb(INTSTAT(base));
+
+  if (intstat & BRKADRINT)
+  {
+    int i;
+    unsigned char errno = inb(ERROR(base));
+
+    printk("aic7xxx_isr: brkadrint (0x%x):\n", errno);
+    for (i = 0; i < NUMBER(hard_error); i++)
+    {
+      if (errno & hard_error[i].errno)
+      {
+	printk("  %s\n", hard_error[i].errmesg);
+      }
+    }
+
+    panic("aic7xxx_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
+	  inb(ERROR(base)),
+	  inb(SEQADDR1(base)) << 8 | inb(SEQADDR0(base)));
+  }
+
+  if (intstat & SEQINT)
+  {
+    /*
+     * Although the sequencer is paused immediately on
+     * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
+     * condition will have unpaused the sequencer before
+     * this point.
+     */
+    PAUSE_SEQUENCER(p);
+
+    switch (intstat & SEQINT_MASK)
+    {
+      case BAD_PHASE:
+	panic("aic7xxx_isr: unknown scsi bus phase\n");
+
+      case SEND_REJECT:
+	debug("aic7xxx_isr warning: issuing message reject, 1st byte 0x%x\n",
+	      inb(HA_REJBYTE(base)));
+	break;
+
+      case NO_IDENT:
+	panic("aic7xxx_isr: reconnecting target %d at seqaddr 0x%x "
+	      "didn't issue IDENTIFY message\n",
+	      (inb(SELID(base)) >> 4) & 0x0F,
+	      (inb(SEQADDR1(base)) << 8) | inb(SEQADDR0(base)));
+	break;
+
+      case NO_MATCH:
+	tcl = inb(SCBARRAY(base) + 1);
+	target = (tcl >> 4) & 0x0F;
+	/* Purposefully mask off the top bit of targets 8-15. */
+	target_mask = 0x01 << (target & 0x07);
+
+	debug("aic7xxx_isr: sequencer couldn't find match "
+	      "for reconnecting target %d, channel %d, lun %d - "
+	      "issuing ABORT\n", target, (tcl & 0x08) >> 3, tcl & 0x07);
+	if (tcl & 0x88)
+	{
+	  /* Second channel stores its info in byte
+	   * two of HA_ACTIVE
+	   */
+	  active = inb(HA_ACTIVE1(base));
+	  active = active & ~(target_mask);
+	  outb(active, HA_ACTIVE1(base));
+	}
+	else
+	{
+	  active = inb(HA_ACTIVE0(base));
+	  active = active & ~(target_mask);
+	  outb(active, HA_ACTIVE0(base));
+	}
+#ifdef AIC7XXX_USE_DMA
+	outb(SCB_NEEDDMA, SCBARRAY(base));
+#endif
 
 	/*
-	 *  The IRQ level in i/o port 4 maps directly onto the real
-	 *  IRQ number.  If it's ok, register it with the kernel.
-	 *
-	 *  NB. the Adaptec documentation says the IRQ number is only
-	 *	in the lower four bits; the ECU information shows the
-	 *	high bit being used as well.  Which is correct?
+	 * Check out why this use to be outb(0x80, CLRINT(base))
+	 * clear the timeout
 	 */
-	if (config.irq < 9 || config.irq > 15) {
-		printk("aic7xxx uses unsupported IRQ level, ignoring\n");
-		return(0);
+	outb(CLRSELTIMEO, CLRSINT1(base));
+	RESTART_SEQUENCER(p);
+	break;
+
+      case MSG_SDTR:
+	/*
+	 * Help the sequencer to translate the negotiated
+	 * transfer rate. Transfer is 1/4 the period
+	 * in ns as is returned by the sync negotiation
+	 * message. So, we must multiply by four.
+	 */
+	transfer = (inb(HA_ARG_1(base)) << 2);
+	offset = inb(ACCUM(base));
+	scsi_id = inb(SCSIID(base)) >> 0x04;
+	if (inb(SBLKCTL(base)) & 0x08)
+	{
+	  scsi_id = scsi_id + 8;  /* B channel */
 	}
-	
+	target_mask = (0x01 << scsi_id);
+	scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
 	/*
-	 *  Lock out other contenders for our i/o space.
+	 * The maximum offset for a wide device is 0x08; for a
+	 * 8-bit bus device the maximum offset is 0x0f.
 	 */
-	snarf_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base));
-
-	/*
-	 *  Any card-type-specific adjustments before we register
-	 *  the scsi host(s).
-	 */
-	switch (aic7xxx_type(base)) {
-	    case 'T':
-		printk("aic7xxx warning: ignoring channel B of 274x-twin\n");
-		break;
-	    case ' ':
-		break;
-	    default:
-		printk("aic7xxx is an unsupported type, ignoring\n");
-		return(0);
+	if (scratch & 0x80)
+	{
+	  max_offset = 0x08;
 	}
-
+	else
+	{
+	  max_offset = 0x0f;
+	}
+	aic7xxx_scsirate(&rate, transfer, MIN(offset, max_offset), scsi_id);
 	/*
-	 *  Before registry, make sure that the offsets of the
-	 *  struct scatterlist are what the sequencer will expect,
-	 *  otherwise disable scatter-gather altogether until someone
-	 *  can fix it.  This is important since the sequencer will
-	 *  DMA elements of the SG array in while executing commands.
+	 * Preserve the wide transfer flag.
 	 */
-	if (template->sg_tablesize != SG_NONE) {
-		struct scatterlist sg;
+	rate = rate | (scratch & 0x80);
+	outb(rate, HA_TARG_SCRATCH(base) + scsi_id);
+	outb(rate, SCSIRATE(base));
+	if ((rate & 0xf) == 0)
+	{ /*
+	   * The requested rate was so low that asynchronous transfers
+	   * are faster (not to mention the controller won't support
+	   * them), so we issue a reject to ensure we go to asynchronous
+	   * transfers.
+	   */
+	   outb(SEND_REJ, HA_RETURN_1(base));
+	}
+	else
+	{
+	  /*
+	   * See if we initiated Sync Negotiation
+	   */
+	  if (p->sdtr_pending & target_mask)
+	  {
+	    /*
+	     * Don't send an SDTR back to the target.
+	     */
+	    outb(0, HA_RETURN_1(base));
+	  }
+	  else
+	  {
+	    /*
+	     * Send our own SDTR in reply.
+	     */
+	    printk("Sending SDTR!!\n");
+	    outb(SEND_SDTR, HA_RETURN_1(base));
+	  }
+	}
+	/*
+	 * Clear the flags.
+	 */
+	p->needsdtr = p->needsdtr & ~target_mask;
+	p->sdtr_pending = p->sdtr_pending & ~target_mask;
+	break;
+
+      case MSG_WDTR:
+      {
+	bus_width = inb(ACCUM(base));
+	scsi_id = inb(SCSIID(base)) >> 0x04;
+	if (inb(SBLKCTL(base)) & 0x08)
+	{
+	  scsi_id = scsi_id + 8;  /* B channel */
+	}
+	printk("Received MSG_WDTR, scsi_id = %d, "
+	       "needwdtr = 0x%x\n", scsi_id, p->needwdtr);
+	scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
 
-		if (SG_STRUCT_CHECK(sg)) {
-			printk("aic7xxx warning: kernel scatter-gather "
-			       "structures changed, disabling it\n");
-			template->sg_tablesize = SG_NONE;
-		}
+	target_mask = (0x01 << scsi_id);
+	if (p->wdtr_pending & target_mask)
+	{
+	  /*
+	   * Don't send an WDTR back to the target, since we asked first.
+	   */
+	  outb(0, HA_RETURN_1(base));
+	  switch (bus_width)
+	  {
+	    case BUS_8_BIT:
+	      scratch = scratch & 0x7F;
+	      break;
+
+	    case BUS_16_BIT:
+	      printk("aic7xxx_isr: target %d using 16 bit transfers\n",
+		     scsi_id);
+	      scratch = scratch | 0x80;
+	      break;
+	  }
 	}
-	
-	/*
-	 *  Register each "host" and fill in the returned Scsi_Host
-	 *  structure as best we can.  Some of the parameters aren't
-	 *  really relevant for bus types beyond ISA, and none of the
-	 *  high-level SCSI code looks at it anyway.. why are the fields
-	 *  there?  Also save the pointer so that we can find the
-	 *  information when an IRQ is triggered.
+	else
+	{
+	  /*
+	   * Send our own WDTR in reply.
+	   */
+	  printk("Will send WDTR!!\n");
+	  switch (bus_width)
+	  {
+	    case BUS_8_BIT:
+	      scratch = scratch & 0x7F;
+	      break;
+
+	    case BUS_32_BIT:
+	      /* Negotiate 16 bits. */
+	      bus_width = BUS_16_BIT;
+	      /* Yes, we mean to fall thru here */
+
+	    case BUS_16_BIT:
+	      printk("aic7xxx_isr: target %d using 16 bit transfers\n",
+		     scsi_id);
+	      scratch = scratch | 0x80;
+	      break;
+	  }
+	  outb(bus_width | SEND_WDTR, HA_RETURN_1(base));
+	}
+	p->needwdtr = p->needwdtr & ~target_mask;
+	p->wdtr_pending = p->wdtr_pending & ~target_mask;
+	outb(scratch, HA_TARG_SCRATCH(base) + scsi_id);
+	outb(scratch, SCSIRATE(base));
+	break;
+      }
+
+      case MSG_REJECT:
+      {
+	/*
+	 * What we care about here is if we had an
+	 * outstanding SDTR or WDTR message for this
+	 * target. If we did, this is a signal that
+	 * the target is refusing negotiation.
 	 */
-	host = scsi_register(template, sizeof(struct aic7xxx_host));
-	host->can_queue = config.maxscb;
-	host->this_id = config.scsi_id;
-	host->irq = config.irq;
-
-	aic7xxx_boards[config.irq] = host;
-	
-	p = (struct aic7xxx_host *)host->hostdata;
-	for (i = 0; i < AIC7XXX_MAXSCB; i++)
-		p->SCB_array[i] = NULL;
-
-	p->base = config.base;
-	p->maxscb = config.maxscb;
-	p->extended = config.extended;
 
-	/*
-	 *  The interrupt trigger is different depending
-	 *  on whether the card is EISA or VL-bus - sometimes.
-	 *  The startup variable will be cleared once the first
-	 *  command is queued, and is checked in the isr to
-	 *  try and detect when the interrupt type is set
-	 *  incorrectly, triggering an interrupt immediately.
-	 *  This is now just set on a per-card-type basis.
-	 */
-	p->unpause = config.unpause;
-	p->startup = !0;
+	unsigned char targ_scratch, scsi_id;
+	unsigned short mask;
 
-	/*
-	 *  Register IRQ with the kernel _after_ the host information
-	 *  is set up, in case we take an interrupt right away, due to
-	 *  the interrupt type being set wrong.
-	 */
-	if (request_irq(config.irq, aic7xxx_isr, SA_INTERRUPT, "aic7xxx")) {
-		printk("aic7xxx couldn't register irq %d, ignoring\n",
-		       config.irq);
-		return(0);
+	scsi_id = inb(SCSIID(base)) >> 0x04;
+	if (inb(SBLKCTL(base)) & 0x08)
+	{
+	  scsi_id = scsi_id + 8;
 	}
 
-	/*
-	 *  Print out debugging information before re-enabling
-	 *  the card - a lot of registers on it can't be read
-	 *  when the sequencer is active.
-	 */
-#ifdef AIC7XXX_DEBUG
-	debug_config(type, &config);
-#endif
+	mask = (0x01 << scsi_id);
 
-	/*
-	 *  Load the sequencer program, then re-enable the board -
-	 *  resetting the AIC-7770 disables it, leaving the lights
-	 *  on with nobody home.  On the PCI bus you *may* be home,
-	 *  but then your mailing address is dynamically assigned
-	 *  so no one can find you anyway :-)
-	 */
-	aic7xxx_loadram(base);
-	if (type != T_294X)
-		outb(1, O_BCTL(base));	/* ENABLE */
+	targ_scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
 
-	/*
-	 *  Set the host adapter registers to indicate that synchronous
-	 *  negotiation should be attempted the first time the targets
-	 *  are communicated with.  Also initialize the active message
-	 *  flag to indicate that there is no message.
-	 */
-	outb(0xff, HA_NEEDSDTR(base));
-	outb(0, HA_MSG_FLAGS(base));
+	if (p->wdtr_pending & mask)
+	{
+	  /*
+	   * note 8bit xfers and clear flag
+	   */
+	  targ_scratch = targ_scratch & 0x7F;
+	  p->needwdtr = p->needwdtr & ~mask;
+	  p->wdtr_pending = p->wdtr_pending & ~mask;
+	  outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
+	  printk("aic7xxx: target %d refusing WIDE negotiation. Using "
+		 "8 bit transfers\n", scsi_id);
+	}
+	else
+	{
+	  if (p->sdtr_pending & mask)
+	  {
+	    /*
+	     * note asynch xfers and clear flag
+	     */
+	    targ_scratch = targ_scratch & 0xF0;
+	    p->needsdtr = p->needsdtr & ~mask;
+	    p->sdtr_pending = p->sdtr_pending & ~mask;
+	    outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
+	    printk("aic7xxx: target %d refusing syncronous negotiation. Using "
+		   "asyncronous transfers\n", scsi_id);
+	  }
+	  /*
+	   * Otherwise, we ignore it.
+	   */
+	}
+	outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
+	outb(targ_scratch, SCSIRATE(base));
+	break;
+      }
+
+      case BAD_STATUS:
+	scsi_id = inb(SCSIID(base)) >> 0x04;
+	scbptr = inb(SCBPTR(base));
+	scb = &(p->scb_array[scbptr]);
+	outb(0, HA_RETURN_1(base));   /* CHECK_CONDITION may change this */
+	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+	{
+	  printk("aic7xxx_isr: referenced scb not valid "
+		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
+		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+	}
+	else
+	{
+	  cmd = scb->cmd;
+	  aic7xxx_getscb(base, scb);
+	  aic7xxx_status(cmd) = scb->target_status;
+
+	  cmd->result = cmd->result | scb->target_status;
+
+	  /*
+	   * This test is just here for debugging purposes.
+	   * It will go away when the timeout problem is resolved.
+	   */
+	  switch (status_byte(scb->target_status))
+	  {
+	    case GOOD:
+	      break;
+
+	    case CHECK_CONDITION:
+	      if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
+	      {
+		void         *req_buf;
+#ifndef AIC7XXX_USE_SG
+		unsigned int  req_buflen;
+#endif
 
-	/*
-	 *  For reconnecting targets, the sequencer code needs to
-	 *  know how many SCBs it has to search through.
-	 */
-	outb(config.maxscb, HA_SCBCOUNT(base));
+		/* Update the timeout for the SCSI command. */
+/*                update_timeout(cmd, SENSE_TIMEOUT); */
 
-	/*
-	 *  Unpause the sequencer before returning and enable
-	 *  interrupts - we shouldn't get any until the first
-	 *  command is sent to us by the high-level SCSI code.
-	 */
-	UNPAUSE_SEQUENCER(p);
-	return(1);
-}
+		/* Send a sense command to the requesting target. */
+		cmd->flags = cmd->flags | WAS_SENSE;
+		memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+		       sizeof(generic_sense));
+
+		scb->sense_cmd[1] = cmd->lun << 5;
+		scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+#ifdef AIC7XXX_USE_SG
+		scb->sense_sg.address = (char *) &cmd->sense_buffer;
+		scb->sense_sg.length = sizeof(cmd->sense_buffer);
+		req_buf = &scb->sense_sg;
+#else
+		req_buf = &cmd->sense_buffer;
+		req_buflen = sizeof(cmd->sense_buffer);
+#endif
+		cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+		memset(scb, 0, SCB_DOWNLOAD_SIZE);
+		scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+		    ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
+		addr = scb->sense_cmd;
+		scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+		memcpy(scb->SCSI_cmd_pointer, &addr,
+		       sizeof(scb->SCSI_cmd_pointer));
+#ifdef AIC7XXX_USE_SG
+		scb->SG_segment_count = 1;
+		memcpy (scb->SG_list_pointer, &req_buf,
+			sizeof(scb->SG_list_pointer));
+#else
+		scb->SG_segment_count = 0;
+		memcpy (scb->data_pointer, &req_buf,
+			sizeof(scb->data_pointer));
+		memcpy (scb->data_count, &req_buflen, 3);
+#endif
 
-int aic7xxx_detect(Scsi_Host_Template *template)
-{
-	enum aha_type type;
-	int found = 0, slot, base;
+		outb(SCBAUTO, SCBCNT(base));
+		asm volatile("cld\n\t"
+			     "rep\n\t"
+			     "outsb"
+			     : /* no output */
+			     :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base))
+			     :"si", "cx", "dx");
+		outb(0, SCBCNT(base));
+		outb(SCB_LIST_NULL, (SCBARRAY(base) + 30));
 
-	/*
-	 *  EISA/VL-bus card signature probe.
-	 */
-	for (slot = MINSLOT; slot <= MAXSLOT; slot++) {
-
-		base = SLOTBASE(slot);
-		
-		if (check_region(O_MINREG(base),
-				 O_MAXREG(base)-O_MINREG(base)))
+		/*
+		 * Add this SCB to the "waiting for selection" list.
+		 */
+		head = inb(WAITING_SCBH(base));
+		tail = inb(WAITING_SCBT(base));
+		if (head & SCB_LIST_NULL)
+		{ /* list is empty */
+		  head = scb->position;
+		  tail = SCB_LIST_NULL;
+		}
+		else
 		{
-			/*
-			 *  Some other driver has staked a
-			 *  claim to this i/o region already.
-			 */
-			continue;
+		  if (tail & SCB_LIST_NULL)
+		  { /* list has one element */
+		    tail = scb->position;
+		    outb(head, SCBPTR(base));
+		    outb(tail, (SCBARRAY(base) + 30));
+		  }
+		  else
+		  { /* list has more than one element */
+		    outb(tail, SCBPTR(base));
+		    tail = scb->position;
+		    outb(tail, (SCBARRAY(base) + 30));
+		  }
 		}
+		outb(head, WAITING_SCBH(base));
+		outb(tail, WAITING_SCBT(base));
+		outb(SEND_SENSE, HA_RETURN_1(base));
+	      }  /* first time sense, no errors */
+	      else
+	      {
+		/*
+		 * Indicate that we asked for sense, have the sequencer do
+		 * a normal command complete, and have the scsi driver handle
+		 * this condition.
+		 */
+		cmd->flags = cmd->flags | ASKED_FOR_SENSE;
+	      }
+	      break;
+
+	    case BUSY:
+	      printk("aic7xxx_isr: Target busy\n");
+	      if (!aic7xxx_error(cmd))
+	      {
+		aic7xxx_error(cmd) = DID_BUS_BUSY;
+	      }
+	      break;
+
+	    case QUEUE_FULL:
+	      printk("aic7xxx_isr: Queue full\n");
+	      if (!aic7xxx_error(cmd))
+	      {
+		aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+	      }
+	      break;
 
-		type = aic7xxx_probe(slot, O_BIDx(base));
-
-		if (type != T_NONE) {
-			/*
-			 *  We "find" a 274x if we locate the card
-			 *  signature and we can set it up and register
-			 *  it with the kernel without incident.
-			 */
-			found += aic7xxx_register(template, type, base);
-		}
+	    default:
+	      printk("aic7xxx_isr: Unexpected target status 0x%x\n",
+		     scb->target_status);
+	      if (!aic7xxx_error(cmd))
+	      {
+		aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+	      }
+	      break;
+	  }  /* end switch */
+	}  /* end else of */
+	break;
+
+      case RESIDUAL:
+	scbptr = inb(SCBPTR(base));
+	scb = &(p->scb_array[scbptr]);
+	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+	{
+	  printk("aic7xxx_isr: referenced scb not valid "
+		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
+		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
 	}
-
-	/*
-	 *  PCI-bus probe.
-	 */
-	if (pcibios_present()) {
-		int index = 0;
-		unsigned char bus, device_fn;
-
-		while (!pcibios_find_device(PCI_VENDOR_ID_ADAPTEC,
-					    PCI_DEVICE_ID_ADAPTEC_2940,
-					    index,
-					    &bus,
-					    &device_fn))
-		{
-			found += aic7xxx_register(template, T_294X,
-						  bus, device_fn);
-			index += 1;
-		}
+	else
+	{
+	  cmd = scb->cmd;
+	  /*
+	   *  Don't destroy valid residual information with
+	   *  residual coming from a check sense operation.
+	   */
+	  if (!(cmd->flags & WAS_SENSE))
+	  {
+	    /*
+	     *  We had an underflow. At this time, there's only
+	     *  one other driver that bothers to check for this,
+	     *  and cmd->underflow seems to be set rather half-
+	     *  heartedly in the higher-level SCSI code.
+	     */
+	    actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
+
+	    actual -= ((inb(SCBARRAY(base + 17)) << 16) |
+		       (inb(SCBARRAY(base + 16)) <<  8) |
+		       inb(SCBARRAY(base + 15)));
+
+	    if (actual < cmd->underflow)
+	    {
+	      printk("aic7xxx: target %d underflow - "
+		     "wanted (at least) %u, got %u\n",
+		     cmd->target, cmd->underflow, actual);
+
+	      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+	      aic7xxx_status(cmd) = scb->target_status;
+	    }
+	  }
+	}
+	break;
+
+      case ABORT_TAG:
+	scbptr = inb(SCBPTR(base));
+	scb = &(p->scb_array[scbptr]);
+	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+	{
+	  printk("aic7xxx_isr: referenced scb not valid "
+		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
+		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
 	}
-
-	template->name = (char *)aic7xxx_info(NULL);
-	return(found);
-}
-
-const char *aic7xxx_info(struct Scsi_Host *notused)
-{
-	return("Adaptec AHA274x/284x/294x (EISA/VL-bus/PCI -> Fast SCSI) "
-	       AIC7XXX_SEQ_VERSION "/"
-	       AIC7XXX_H_VERSION "/"
-	       "1.34");
-}
-
-static
-void aic7xxx_buildscb(struct aic7xxx_host *p,
-		      Scsi_Cmnd *cmd,
-		      struct aic7xxx_scb *scb)
-{
-	void *addr;
-	unsigned length;
-
-	memset(scb, 0, sizeof(*scb));
-
-	/*
-	 *  NB. channel selection (bit 3) is always zero.
-	 */
-	scb->target_channel_lun = ((cmd->target << 4) & 0xf0) |
-				   (cmd->lun & 0x7);
-
-	/*
-	 *  The interpretation of request_buffer and request_bufflen
-	 *  changes depending on whether or not use_sg is zero; a
-	 *  non-zero use_sg indicates the number of elements in the
-	 *  scatter-gather array.
-	 *
-	 *  The AIC-7770 can't support transfers of any sort larger
-	 *  than 2^24 (three-byte count) without backflips.  For what
-	 *  the kernel is doing, this shouldn't occur.  I hope.
-	 */
-	length = aic7xxx_length(cmd, 0);
-
-	/*
-	 *  The sequencer code cannot yet handle scatter-gather segments
-	 *  larger than 64k (two-byte length).  The 1.1.x kernels, however,
-	 *  have a four-byte length field in the struct scatterlist, so
-	 *  make sure we don't exceed 64k on these kernels for now.
-	 */
-	aic7xxx_sg_check(cmd);
-
-	if (length > 0xffffff) {
-		panic("aic7xxx_buildscb: can't transfer > 2^24 - 1 bytes\n");
+	else
+	{
+	  cmd = scb->cmd;
+	  /*
+	   * We didn't recieve a valid tag back from the target
+	   * on a reconnect.
+	   */
+	  printk("aic7xxx_isr: invalid tag recieved on channel %c "
+		 "target %d, lun %d -- sending ABORT_TAG\n",
+		  (cmd->channel & 0x01) ? 'B':'A',
+		  cmd->target, cmd->lun & 0x07);
+	  /*
+	   *  This is a critical section, since we don't want the
+	   *  queue routine mucking with the host data.
+	   */
+	  save_flags(flags);
+	  cli();
+
+	  /*
+	   *  Process the command after marking the scb as free
+	   *  and adding it to the free list.
+	   */
+	  scb->state = SCB_FREE;
+	  scb->cmd = NULL;
+	  scb->next = p->free_scb;      /* preserve next pointer */
+	  p->free_scb = scb;            /* add at head of list */
+
+	  restore_flags (flags);
+	  cmd->result = (DID_RETRY_COMMAND << 16);
+	  cmd->scsi_done(cmd);
+	}
+	break;
+
+      case AWAITING_MSG:
+	scbptr = inb(SCBPTR(base));
+	scb = &(p->scb_array[scbptr]);
+	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+	{
+	  printk("aic7xxx_isr: referenced scb not valid "
+		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
+		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
 	}
-
+	else
+	{
+	  /*
+	   * This SCB had a zero length command, informing the sequencer
+	   * that we wanted to send a special message to this target.
+	   * We only do this for BUS_DEVICE_RESET messages currently.
+	   */
+	   if (scb->state & SCB_DEVICE_RESET)
+	   {
+	     outb(MSG_BUS_DEVICE_RESET, HA_MSG_START(base));
+	     outb(1, HA_MSG_LEN(base));
+	   }
+	   else
+	   {
+	     panic ("aic7xxx_isr: AWAITING_SCB for an SCB that does "
+		    "not have a waiting message");
+	   }
+	}
+	break;
+
+      default:               /* unknown */
+	debug("aic7xxx_isr: seqint, intstat = 0x%x, scsisigi = 0x%x\n",
+	      intstat, inb(SCSISIGI(base)));
+	break;
+    }
+    outb(CLRSEQINT, CLRINT(base));
+    UNPAUSE_SEQUENCER(p);
+  }
+
+  if (intstat & SCSIINT)
+  {
+    int status = inb(SSTAT1(base));
+
+    scbptr = inb(SCBPTR(base));
+    scb = &p->scb_array[scbptr];
+    if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+    {
+      printk("aic7xxx_isr: no command for scb (scsiint)\n");
+      /*
+       * Turn off the interrupt and set status
+       * to zero, so that it falls through the
+       * reset of the SCSIINT code.
+       */
+      outb(status, CLRSINT1(base));
+      UNPAUSE_SEQUENCER(p);
+      outb(CLRSCSIINT, CLRINT(base));
+      status = 0;
+      scb = NULL;
+    }
+    else
+    {
+      cmd = scb->cmd;
+
+      /*
+       * Only the SCSI Status 1 register has information
+       * about exceptional conditions that we'd have a
+       * SCSIINT about; anything in SSTAT0 will be handled
+       * by the sequencer. Note that there can be multiple
+       * bits set.
+       */
+      if (status & SELTO)
+      {
+	unsigned char target_mask = (1 << (cmd->target & 0x07));
+	unsigned char waiting;
+
+	/*
+	 * Hardware selection timer has expired. Turn
+	 * off SCSI selection sequence.
+	 */
+	outb(ENRSELI, SCSISEQ(base));
+	cmd->result = (DID_TIME_OUT << 16);
 	/*
-	 *  XXX - this relies on the host data being stored in a
-	 *	  little-endian format.
+	 * Clear an pending messages for the timed out
+	 * target and mark the target as free.
 	 */
-	addr = cmd->cmnd;
-	scb->SCSI_cmd_length = cmd->cmd_len;
-	memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+	ha_flags = inb(HA_FLAGS(base));
+	outb(ha_flags & ~ACTIVE_MSG, HA_FLAGS(base));
 
-	if (cmd->use_sg) {
-#if 0
-		debug("aic7xxx_buildscb: SG used, %d segments, length %u\n",
-		      cmd->use_sg,
-		      length);
-#endif
-		scb->SG_segment_count = cmd->use_sg;
-		memcpy(scb->SG_list_pointer,
-		       &cmd->request_buffer,
-		       sizeof(scb->SG_list_pointer));
-	} else {
-		scb->SG_segment_count = 0;
-		memcpy(scb->data_pointer,
-		       &cmd->request_buffer,
-		       sizeof(scb->data_pointer));
-		memcpy(scb->data_count,
-		       &cmd->request_bufflen,
-		       sizeof(scb->data_count));
+	if (scb->target_channel_lun & 0x88)
+	{
+	  active = inb(HA_ACTIVE1(base));
+	  active = active & ~(target_mask);
+	  outb(active, HA_ACTIVE1(base));
+	}
+	else
+	{
+	  active = inb(HA_ACTIVE0(base));
+	  active = active & ~(target_mask);
+	  outb(active, HA_ACTIVE0(base));
 	}
-}
-
-static
-void aic7xxx_putscb(int base, struct aic7xxx_scb *scb)
-{
-	/*
-	 *  By turning on the SCB auto increment, any reference
-	 *  to the SCB I/O space postincrements the SCB address
-	 *  we're looking at.  So turn this on and dump the relevant
-	 *  portion of the SCB to the card.
-	 */
-	outb(0x80, O_SCBCNT(base));	/* SCBAUTO */
-
-	asm volatile("cld\n\t"
-		     "rep\n\t"
-		     "outsb"
-		     : /* no output */
-		     :"S" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
-		     :"si", "cx", "dx");
-
-	outb(0, O_SCBCNT(base));
-}
-
-int aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
-{
-	long flags;
-	int empty, old_scbptr;
-	struct aic7xxx_host *p;
-	struct aic7xxx_scb scb;
 
-#if 0
-	debug("aic7xxx_queue: cmd 0x%x (size %u), target %d, lun %d\n",
-	      cmd->cmnd[0],
-	      cmd->cmd_len,
-	      cmd->target,
-	      cmd->lun);
+#ifdef AIC7XXX_USE_DMA
+	outb(SCB_NEEDDMA, SCBARRAY(base));
 #endif
 
-	p = (struct aic7xxx_host *)cmd->host->hostdata;
-
-	/*
-	 *  Construct the SCB beforehand, so the sequencer is
-	 *  paused a minimal amount of time.
-	 */
-	aic7xxx_buildscb(p, cmd, &scb);
-
 	/*
-	 *  Clear the startup flag - we can now legitimately
-	 *  expect interrupts.
-	 */
-	p->startup = 0;
+	 * Shut off the offending interrupt sources, reset
+	 * the sequencer address to zero and unpause it,
+	 * then call the high-level SCSI completion routine.
+	 *
+	 * WARNING!  This is a magic sequence!  After many
+	 * hours of guesswork, turning off the SCSI interrupts
+	 * in CLRSINT? does NOT clear the SCSIINT bit in
+	 * INTSTAT. By writing to the (undocumented, unused
+	 * according to the AIC-7770 manual) third bit of
+	 * CLRINT, you can clear INTSTAT. But, if you do it
+	 * while the sequencer is paused, you get a BRKADRINT
+	 * with an Illegal Host Address status, so the
+	 * sequencer has to be restarted first.
+	 */
+	outb(CLRSELTIMEO, CLRSINT1(base));
+
+	outb(CLRSCSIINT, CLRINT(base));
+
+	/* Shift the waiting for selection queue forward */
+	waiting = inb(WAITING_SCBH(base));
+	outb(waiting, SCBPTR(base));
+	waiting = inb(SCBARRAY(base) + 30);
+	outb(waiting, WAITING_SCBH(base));
 
+	RESTART_SEQUENCER(p);
 	/*
-	 *  This is a critical section, since we don't want the
-	 *  interrupt routine mucking with the host data or the
-	 *  card.  Since the kernel documentation is vague on
-	 *  whether or not we are in a cli/sti pair already, save
-	 *  the flags to be on the safe side.
+	 * This is a critical section, since we don't want the
+	 * queue routine mucking with the host data.
 	 */
 	save_flags(flags);
 	cli();
 
 	/*
-	 *  Find a free slot in the SCB array to load this command
-	 *  into.  Since can_queue is set to the maximum number of
-	 *  SCBs for the card, we should always find one.
-	 */
-	for (empty = 0; empty < p->maxscb; empty++)
-		if (!p->SCB_array[empty])
-			break;
-	if (empty == p->maxscb)
-		panic("aic7xxx_queue: couldn't find a free scb\n");
-
-	/*
-	 *  Pause the sequencer so we can play with its registers -
-	 *  wait for it to acknowledge the pause.
-	 *
-	 *  XXX - should the interrupts be left on while doing this?
+	 * Process the command after marking the scb as free
+	 * and adding it to the free list.
 	 */
-	PAUSE_SEQUENCER(p);
+	scb->state = SCB_FREE;
+	scb->cmd = NULL;
+	scb->next = p->free_scb;        /* preserve next pointer */
+	p->free_scb = scb;              /* add at head of list */
 
-	/*
-	 *  Save the SCB pointer and put our own pointer in - this
-	 *  selects one of the four banks of SCB registers.  Load
-	 *  the SCB, then write its pointer into the queue in FIFO
-	 *  and restore the saved SCB pointer.
-	 */
-	old_scbptr = inb(O_SCBPTR(p->base));
-	outb(empty, O_SCBPTR(p->base));
-	
-	aic7xxx_putscb(p->base, &scb);
+	restore_flags(flags);
 
-	outb(empty, O_QINFIFO(p->base));
-	outb(old_scbptr, O_SCBPTR(p->base));
+	cmd->scsi_done(cmd);
+#if 0
+  printk("aic7xxx_isr: SELTO scb(%d) state(%x), cmd(%x)\n",
+	 scb->position, scb->state, (unsigned int) scb->cmd);
+#endif
+      }
+      else
+      {
+	if (status & SCSIPERR)
+	{
+	  /*
+	   * A parity error has occurred during a data
+	   * transfer phase. Flag it and continue.
+	   */
+	  printk("aic7xxx: parity error on target %d, "
+		 "channel %d, lun %d\n",
+		 cmd->target,
+		 cmd->channel & 0x01,
+		 cmd->lun & 0x07);
+	  aic7xxx_error(cmd) = DID_PARITY;
+
+	  /*
+	   * Clear interrupt and resume as above.
+	   */
+	  outb(CLRSCSIPERR, CLRSINT1(base));
+	  UNPAUSE_SEQUENCER(p);
 
-	/*
-	 *  Make sure the Scsi_Cmnd pointer is saved, the struct it
-	 *  points to is set up properly, and the parity error flag
-	 *  is reset, then unpause the sequencer and watch the fun
-	 *  begin.
-	 */
-	cmd->scsi_done = fn;
-	p->SCB_array[empty] = cmd;
-	aic7xxx_parity(cmd) = DID_OK;
+	  outb(CLRSCSIINT, CLRINT(base));
+	  scb = NULL;
+	}
+	else
+	{
+	  if (! (status & BUSFREE))
+	  {
+	     /*
+	      * We don't know what's going on. Turn off the
+	      * interrupt source and try to continue.
+	      */
+	     printk("aic7xxx_isr: sstat1 = 0x%x\n", status);
+	     outb(status, CLRSINT1(base));
+	     UNPAUSE_SEQUENCER(p);
+	     outb(CLRSCSIINT, CLRINT(base));
+	     scb = NULL;
+	  }
+	}
+      }
+    }  /* else */
+  }
+
+  if (intstat & CMDCMPLT)
+  {
+    int complete;
+
+    /*
+     * The sequencer will continue running when it
+     * issues this interrupt. There may be >1 commands
+     * finished, so loop until we've processed them all.
+     */
+    do {
+      complete = inb(QOUTFIFO(base));
+
+      scb = &(p->scb_array[complete]);
+      if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+      {
+	printk("aic7xxx warning: "
+	       "no command for scb %d (cmdcmplt)\n"
+	       "QOUTCNT = %d, SCB state = 0x%x, CMD = 0x%x\n",
+	       complete, inb(QOUTFIFO(base)),
+	       scb->state, (unsigned int) scb->cmd);
+	outb(CLRCMDINT, CLRINT(base));
+	continue;
+      }
+      cmd = scb->cmd;
+
+      cmd->result = (aic7xxx_error(cmd) << 16) | aic7xxx_status(cmd);
+      if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
+      { /* Got sense information. */
+	cmd->flags = cmd->flags & ASKED_FOR_SENSE;
+      }
+#if 0
+      printk("aic7xxx_intr: (complete) state = %d, cmd = 0x%x, free = 0x%x\n",
+	     scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb);
+#endif
+      /*
+       * This is a critical section, since we don't want the
+       * queue routine mucking with the host data.
+       */
+      save_flags(flags);
+      cli();
+
+      scb->state = SCB_FREE;
+      scb->next = p->free_scb;
+      scb->cmd = NULL;
+      p->free_scb = &(p->scb_array[scb->position]);
 
-	UNPAUSE_SEQUENCER(p);
+      restore_flags(flags);
+#if 0
+  if (scb != &p->scb_array[scb->position])
+  {
+    printk("aic7xxx_isr: (complete) address mismatch, pos %d\n", scb->position);
+  }
+  printk("aic7xxx_isr: (complete) state = %d, cmd = 0x%x, free = 0x%x\n",
+	 scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb);
+#endif
 
-	restore_flags(flags);
-	return(0);
-}
+      cmd->scsi_done(cmd);
 
-/* return values from aic7xxx_kill */
+      /*
+       * Clear interrupt status before checking
+       * the output queue again. This eliminates
+       * a race condition whereby a command could
+       * complete between the queue poll and the
+       * interrupt clearing, so notification of the
+       * command being complete never made it back
+       * up to the kernel.
+       */
+      outb(CLRCMDINT, CLRINT(base));
+    } while (inb(QOUTCNT(base)));
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_probe
+ *
+ * Description:
+ *   Probing for EISA boards: it looks like the first two bytes
+ *   are a manufacturer code - three characters, five bits each:
+ *
+ *               BYTE 0   BYTE 1   BYTE 2   BYTE 3
+ *              ?1111122 22233333 PPPPPPPP RRRRRRRR
+ *
+ *   The characters are baselined off ASCII '@', so add that value
+ *   to each to get the real ASCII code for it. The next two bytes
+ *   appear to be a product and revision number, probably vendor-
+ *   specific. This is what is being searched for at each port,
+ *   and what should probably correspond to the ID= field in the
+ *   ECU's .cfg file for the card - if your card is not detected,
+ *   make sure your signature is listed in the array.
+ *
+ *   The fourth byte's lowest bit seems to be an enabled/disabled
+ *   flag (rest of the bits are reserved?).
+ *-F*************************************************************************/
+static aha_type
+aic7xxx_probe(int slot, int base)
+{
+  int i;
+  unsigned char buf[4];
+
+  static struct {
+    int n;
+    unsigned char signature[sizeof(buf)];
+    aha_type type;
+  } AIC7xxx[] = {
+    { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_274x },  /* host adapter 274x */
+    { 4, { 0x04, 0x90, 0x77, 0x70 }, AIC_274x },  /* motherboard 274x  */
+    { 4, { 0x04, 0x90, 0x77, 0x56 }, AIC_284x },  /* 284x, BIOS enabled */
+    { 4, { 0x04, 0x90, 0x77, 0x57 }, AIC_284x }   /* 284x, BIOS disabled */
+  };
+
+  /*
+   * The VL-bus cards need to be primed by
+   * writing before a signature check.
+   */
+  for (i = 0; i < sizeof(buf); i++)
+  {
+    outb(0x80 + i, base);
+    buf[i] = inb(base + i);
+  }
+
+  for (i = 0; i < NUMBER(AIC7xxx); i++)
+  {
+    /*
+     * Signature match on enabled card?
+     */
+    if (!memcmp(buf, AIC7xxx[i].signature, AIC7xxx[i].n))
+    {
+      if (inb(base + 4) & 1)
+      {
+	return(AIC7xxx[i].type);
+      }
+
+      printk("aic7xxx disabled at slot %d, ignored\n", slot);
+    }
+  }
+
+  return(AIC_NONE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   read_seeprom
+ *
+ * Description:
+ *   Reads the serial EEPROM and returns 1 if successful and 0 if
+ *   not successful.
+ *
+ *   The instruction set of the 93C46 chip is as follows:
+ *
+ *               Start  OP
+ *     Function   Bit  Code  Address    Data     Description
+ *     -------------------------------------------------------------------
+ *     READ        1    10   A5 - A0             Reads data stored in memory,
+ *                                               starting at specified address
+ *     EWEN        1    00   11XXXX              Write enable must preceed
+ *                                               all programming modes
+ *     ERASE       1    11   A5 - A0             Erase register A5A4A3A2A1A0
+ *     WRITE       1    01   A5 - A0   D15 - D0  Writes register
+ *     ERAL        1    00   10XXXX              Erase all registers
+ *     WRAL        1    00   01XXXX    D15 - D0  Writes to all registers
+ *     EWDS        1    00   00XXXX              Disables all programming
+ *                                               instructions
+ *     *Note: A value of X for address is a don't care condition.
+ *
+ *   The 93C46 has a four wire interface: clock, chip select, data in, and
+ *   data out.  In order to perform one of the above functions, you need
+ *   to enable the chip select for a clock period (typically a minimum of
+ *   1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ *   respectively.  While the chip select remains high, you can clock in
+ *   the instructions (above) starting with the start bit, followed by the
+ *   OP code, Address, and Data (if needed).  For the READ instruction, the
+ *   requested 16-bit register contents is read from the data out line but
+ *   is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ *   first).  The clock cycling from low to high initiates the next data
+ *   bit to be sent from the chip.
+ *
+ *   The 7870 interface to the 93C46 serial EEPROM is through the SEECTL
+ *   register.  After successful arbitration for the memory port, the
+ *   SEECS bit of the SEECTL register is connected to the chip select.
+ *   The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
+ *   and data in lines respectively.  The SEERDY bit of SEECTL is useful
+ *   in that it gives us an 800 nsec timer.  After a write to the SEECTL
+ *   register, the SEERDY goes high 800 nsec later.  The one exception
+ *   to this is when we first request access to the memory port.  The
+ *   SEERDY goes high to signify that access has been granted and, for
+ *   this case, has no implied timing.
+ *
+ *-F*************************************************************************/
+static int
+read_seeprom(int base, struct seeprom_config *sc)
+{
+  int i = 0, k = 0;
+  unsigned long timeout;
+  unsigned char temp;
+  unsigned short checksum = 0;
+  unsigned short *seeprom = (unsigned short *) sc;
+  struct seeprom_cmd {
+    unsigned char len;
+    unsigned char bits[3];
+  };
+  struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+#define CLOCK_PULSE(p) \
+  while ((inb(SEECTL(base)) & SEERDY) == 0)	\
+  {						\
+    ;  /* Do nothing */				\
+  }
+
+  /*
+   * Request access of the memory port.  When access is
+   * granted, SEERDY will go high.  We use a 1 second
+   * timeout which should be near 1 second more than
+   * is needed.  Reason: after the 7870 chip reset, there
+   * should be no contention.
+   */
+  outb(SEEMS, SEECTL(base));
+  timeout = jiffies + 100;  /* 1 second timeout */
+  while ((jiffies < timeout) && ((inb(SEECTL(base)) & SEERDY) == 0))
+  {
+    ;  /* Do nothing!  Wait for access to be granted. */
+  }
+  if ((inb(SEECTL(base)) & SEERDY) == 0)
+  {
+    outb (0, SEECTL(base));
+    return (0);
+  }
+
+  /*
+   * Read the first 32 registers of the seeprom.  For the 7870,
+   * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
+   * but only the first 32 are used by Adaptec BIOS.  The loop
+   * will range from 0 to 31.
+   */
+  for (k = 0; k < (sizeof(*sc) / 2); k = k + 1)
+  {
+    /* Send chip select for one clock cycle. */
+    outb(SEEMS | SEECK | SEECS, SEECTL(base));
+    CLOCK_PULSE(base);
+
+    /*
+     * Now we're ready to send the read command followed by the
+     * address of the 16-bit register we want to read.
+     */
+    for (i = 0; i < seeprom_read.len; i = i + 1)
+    {
+      temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
+      outb(temp, SEECTL(base));
+      CLOCK_PULSE(base);
+      temp = temp ^ SEECK;
+      outb(temp, SEECTL(base));
+      CLOCK_PULSE(base);
+    }
+    /* Send the 6 bit address (MSB first, LSB last). */
+    for (i = 5; i >= 0; i = i - 1)
+    {
+      temp = k;
+      temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
+      temp = SEEMS | SEECS | (temp << 1);
+      outb(temp, SEECTL(base));
+      CLOCK_PULSE(base);
+      temp = temp ^ SEECK;
+      outb(temp, SEECTL(base));
+      CLOCK_PULSE(base);
+    }
+
+    /*
+     * Now read the 16 bit register.  An initial 0 precedes the
+     * register contents which begins with bit 15 (MSB) and ends
+     * with bit 0 (LSB).  The initial 0 will be shifted off the
+     * top of our word as we let the loop run from 0 to 16.
+     */
+    for (i = 0; i <= 16; i = i + 1)
+    {
+      temp = SEEMS | SEECS;
+      outb(temp, SEECTL(base));
+      CLOCK_PULSE(base);
+      temp = temp ^ SEECK;
+      seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL(base)) & SEEDI);
+      outb(temp, SEECTL(base));
+      CLOCK_PULSE(base);
+    }
+
+    /*
+     * The serial EEPROM has a checksum in the last word.  Keep a
+     * running checksum for all words read except for the last
+     * word.  We'll verify the checksum after all words have been
+     * read.
+     */
+    if (k < (sizeof(*sc) / 2) - 1)
+    {
+      checksum = checksum + seeprom[k];
+    }
+
+    /* Reset the chip select for the next command cycle. */
+    outb(SEEMS, SEECTL(base));
+    CLOCK_PULSE(base);
+    outb(SEEMS | SEECK, SEECTL(base));
+    CLOCK_PULSE(base);
+    outb(SEEMS, SEECTL(base));
+    CLOCK_PULSE(base);
+  }
+
+  if (checksum != sc->checksum)
+  {
+    printk ("aic7xxx : SEEPROM checksum error, ignoring SEEPROM settings.\n");
+    return (0);
+  }
 
-enum k_state {
-	k_ok,				/* scb found and message sent */
-	k_busy,				/* message already present */
-	k_absent,			/* couldn't locate scb */
-	k_disconnect,			/* scb found, but disconnected */
-};
+#if 0
+  printk ("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+  printk ("Serial EEPROM:");
+  for (k = 0; k < (sizeof(*sc) / 2); k = k + 1)
+  {
+    if (((k % 8) == 0) && (k != 0))
+    {
+      printk ("\n              ");
+    }
+    printk (" 0x%x", seeprom[k]);
+  }
+  printk ("\n");
+#endif
 
-/*
- *  This must be called with interrupts disabled - it's going to
- *  be messing around with the host data, and an interrupt being
- *  fielded in the middle could get ugly.
- *
- *  Since so much of the abort and reset code is shared, this
- *  function performs more magic than it really should.  If the
- *  command completes ok, then it will call scsi_done with the
- *  result code passed in.  The unpause parameter controls whether
- *  or not the sequencer gets unpaused - the reset function, for
- *  instance, may want to do something more aggressive.
- *
- *  Note that the command is checked for in our SCB_array first
- *  before the sequencer is paused, so if k_absent is returned,
- *  then the sequencer is NOT paused.
- */
-
-static
-enum k_state aic7xxx_kill(Scsi_Cmnd *cmd, unsigned char message,
-			  unsigned int result, int unpause)
-{
-	struct aic7xxx_host *p;
-	int i, scb, found, queued;
-	unsigned char scbsave[AIC7XXX_MAXSCB];
+  /* Release access to the memory port and the serial EEPROM. */
+  outb(0, SEECTL(base));
+  return (1);
+}
 
-	p = (struct aic7xxx_host *)cmd->host->hostdata;
+/*+F*************************************************************************
+ * Function:
+ *   detect_maxscb
+ *
+ * Description:
+ *   Return the maximum number of SCB's allowed for a given controller.
+ *-F*************************************************************************/
+static int
+detect_maxscb(aha_type type, int base)
+{
+  unsigned char sblkctl_reg;
+  int maxscb = 0;
+
+  switch (type)
+  {
+    case AIC_274x:
+    case AIC_284x:
+      /*
+       * Check for Rev C or E boards. Rev E boards can supposedly have
+       * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
+       * Until we know how to access more than 4 SCBs for the Rev E chips,
+       * we limit them, along with the Rev C chips, to 4 SCBs.
+       *
+       * The Rev E boards have a read/write autoflush bit in the
+       * SBLKCTL registor, while in the Rev C boards it is read only.
+       */
+      sblkctl_reg = inb(SBLKCTL(base)) ^ AUTOFLUSHDIS;
+      outb(sblkctl_reg, SBLKCTL(base));
+      if (inb(SBLKCTL(base)) == sblkctl_reg)
+      {  /* We detected a Rev E board. */
+	printk("aic7770: Rev E and subsequent; using 4 SCB's\n");
+	outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL(base));
+	maxscb = 4;
+      }
+      else
+      {
+	printk("aic7770: Rev C and previous; using 4 SCB's\n");
+	maxscb = 4;
+      }
+      break;
+
+    case AIC_7850:
+      maxscb = 3;
+      break;
+
+    case AIC_7870:
+      maxscb = 16;
+      break;
+
+    case AIC_7872:
+      /*
+       * Really has 255, but we'll wait to verify that we access
+       * them the same way and do not have to set the card to
+       * use the memory port to access external SCB RAM.
+       */
+      maxscb = 16;
+      break;
+
+    case AIC_NONE:
+      /*
+       * This should never happen... But just in case.
+       */
+      break;
+  }
+
+  return(maxscb);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_register
+ *
+ * Description:
+ *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, aha_type type,
+		 int base, unsigned char irq)
+{
+  static char * board_name[] = {"", "274x", "284x", "7870", "7850", "7872"};
+  int i;
+  unsigned char sblkctl;
+  int max_targets;
+  int found = 1;
+  unsigned char target_settings;
+  unsigned char scsi_conf;
+  int have_seeprom = 0;
+  struct Scsi_Host *host;
+  struct aic7xxx_host *p;
+  struct aic7xxx_host_config config;
+  struct seeprom_config sc;
+
+  config.type = type;
+  config.base = base;
+  config.irq = irq;
+  config.parity = AIC_UNKNOWN;
+  config.low_term = AIC_UNKNOWN;
+  config.high_term = AIC_UNKNOWN;
+  config.busrtime = 0;
+
+  /*
+   * Lock out other contenders for our i/o space.
+   */
+  request_region(MINREG(base), MAXREG(base) - MINREG(base), "aic7xxx");
+
+  switch (type)
+  {
+    case AIC_274x:
+#if 1
+      printk("aha274x: aic7770 hcntrl=0x%x\n", inb(HCNTRL(config.base)));
+#endif
+      /*
+       * For some 274x boards, we must clear the CHIPRST bit
+       * and pause the sequencer. For some reason, this makes
+       * the driver work. For 284x boards, we give it a
+       * CHIPRST just like the 294x boards.
+       *
+       * Use the BIOS settings to determine the interrupt
+       * trigger type (level or edge) and use this value
+       * for pausing and unpausing the sequencer.
+       */
+      config.unpause = (inb(HCNTRL(config.base)) & IRQMS) | INTEN;
+      config.pause = config.unpause | PAUSE;
+      config.extended = aic7xxx_extended;
+
+      /*
+       * I don't think we need to kick the reset again, the initial probe
+       * does a reset, it seems that this is kicking a dead horse here.
+       * So... I will try to just verify that the chip has come out of the
+       * reset state and continue the same as the 284x.
+       * In the Calgary version of the driver:
+       *   1) Chip Reset
+       *   2) Set unpause to IRQMS | INTEN
+       *   3) If an interrupt occured without any commands queued, the
+       *      unpause was set to just INTEN
+       * I changed the initial reset code to just mask in the CHIPRST bit
+       * and try to leave the other settings alone.
+       *
+       * I don't think we need the warning about chip reset not being clear.
+       * On both my test machines (2842 & 2940), they work just fine with a
+       * HCNTRL() of 0x5 (PAUSE | CHIPRST). Notice though, the 274x also
+       * adds the INTEN flag, where neither the 284x or 294x do.
+       */
+      outb(config.pause | CHIPRST, HCNTRL(config.base));
+      aic7xxx_delay(1);
+      if (inb(HCNTRL(config.base)) & CHIPRST)
+      {
+	printk("aic7xxx_register: Chip reset not cleared; clearing manually.\n");
+      }
+      outb(config.pause, HCNTRL(config.base));
+
+      /*
+       * Just to be on the safe side with the 274x, we will re-read the irq
+       * since there was some issue about reseting the board.
+       */
+      config.irq = inb(HA_INTDEF(config.base)) & 0x0F;
+      config.busrtime = inb(HA_SCSICONF(config.base)) & 0x3C;
+
+      /*
+       * A reminder until this can be detected automatically.
+       */
+      printk("aha274x: extended translation %sabled\n",
+	     config.extended ? "en" : "dis");
+      break;
+
+    case AIC_284x:
+#if 1
+      printk("aha284x: aic7770 hcntrl=0x%x\n", inb(HCNTRL(config.base)));
+#endif
+      outb(CHIPRST, HCNTRL(config.base));
+      config.unpause = UNPAUSE_284X;
+      config.pause = REQ_PAUSE; /* DWG would like to be like the rest */
+      config.extended = aic7xxx_extended;
+      config.irq = inb(HA_INTDEF(config.base)) & 0x0F;
+
+      /*
+       * A reminder until this can be detected automatically.
+       */
+      printk("aha284x: extended translation %sabled\n",
+	     config.extended ? "en" : "dis");
+      break;
+
+    case AIC_7850:
+    case AIC_7870:
+    case AIC_7872:
+#if 1
+      printk("aic%s hcntrl=0x%x\n", board_name[type], inb(HCNTRL(config.base)));
+#endif
 
-	/*
-	 *  If we can't find the command, assume it just completed
-	 *  and shrug it away.
-	 */
-	for (scb = 0; scb < p->maxscb; scb++)
-		if (p->SCB_array[scb] == cmd)
-			break;
+      outb(CHIPRST, HCNTRL(config.base));
+      config.unpause = UNPAUSE_294X;
+      config.pause = config.unpause | PAUSE;
+      config.extended = aic7xxx_extended;
+      config.scsi_id = 7;
+
+      printk ("aic78xx: Reading SEEPROM... ");
+      have_seeprom = read_seeprom(base, &sc);
+      if (! have_seeprom)
+      {
+	printk ("Unable to read SEEPROM\n");
+      }
+      else
+      {
+	printk ("done\n");
+	config.extended = (sc.bios_control & CFEXTEND) >> 7;
+	config.scsi_id = (sc.brtime_id & CFSCSIID);
+	config.parity = (sc.adapter_control & CFSPARITY) ?
+			 AIC_ENABLED : AIC_DISABLED;
+	config.low_term = (sc.adapter_control & CFSTERM) ?
+			      AIC_ENABLED : AIC_DISABLED;
+	config.high_term = (sc.adapter_control & CFWSTERM) ?
+			      AIC_ENABLED : AIC_DISABLED;
+	config.busrtime = (sc.brtime_id & CFBRTIME) >> 8;
+      }
+
+      /*
+       * XXX - force data fifo threshold to 100%. Why does this
+       *       need to be done?
+       */
+      outb(inb(DSPCISTATUS(config.base)) | DFTHRESH, DSPCISTATUS(config.base));
+      outb(config.scsi_id | DFTHRESH, HA_SCSICONF(config.base));
+
+      /*
+       * In case we are a wide card, place scsi ID in second conf byte.
+       */
+      outb(config.scsi_id, (HA_SCSICONF(config.base) + 1));
+
+      /*
+       * A reminder until this can be detected automatically.
+       */
+      printk("aic%s: extended translation %sabled\n", board_name[type],
+	     config.extended ? "en" : "dis");
+      break;
+
+    default:
+      panic("aic7xxx_register: internal error\n");
+  }
+
+  config.maxscb = detect_maxscb(type, base);
+
+  if ((config.type == AIC_274x) || (config.type == AIC_284x))
+  {
+    if (config.pause & IRQMS)
+    {
+      printk("aic7xxx: Using Level Sensitive Interrupts\n");
+    }
+    else
+    {
+      printk("aic7xxx: Using Edge Triggered Interrupts\n");
+    }
+  }
+
+  /*
+   * Read the bus type from the SBLKCTL register. Set the FLAGS
+   * register in the sequencer for twin and wide bus cards.
+   */
+  sblkctl = inb(SBLKCTL(base)) & 0x0F;  /* mask out upper two bits */
+  switch (sblkctl)
+  {
+    case 0:     /* narrow/normal bus */
+      config.scsi_id = inb(HA_SCSICONF(base)) & 0x07;
+      config.bus_type = AIC_SINGLE;
+      outb(0, HA_FLAGS(base));
+      break;
+
+    case 2:     /* Wide bus */
+      config.scsi_id = inb(HA_SCSICONF(base) + 1) & 0x0F;
+      config.bus_type = AIC_WIDE;
+      printk("aic7xxx : Enabling wide channel of %s-Wide\n",
+	     board_name[config.type]);
+      outb(WIDE_BUS, HA_FLAGS(base));
+      break;
+
+    case 8:     /* Twin bus */
+      config.scsi_id = inb(HA_SCSICONF(base)) & 0x07;
+#ifdef AIC7XXX_TWIN_SUPPORT
+      config.scsi_id_b = inb(HA_SCSICONF(base) + 1) & 0x07;
+      config.bus_type = AIC_TWIN;
+      printk("aic7xxx : Enabled channel B of %s-Twin\n",
+	     board_name[config.type]);
+      outb(TWIN_BUS, HA_FLAGS(base));
+#else
+      config.bus_type = AIC_SINGLE;
+      printk("aic7xxx : Channel B of %s-Twin will be ignored\n",
+	     board_name[config.type]);
+      outb(0, HA_FLAGS(base));
+#endif
+      break;
 
-	if (scb == p->maxscb)
-		return(k_absent);
+    default:
+      printk("aic7xxx is an unsupported type 0x%x, please "
+	     "mail deang@ims.com\n", inb(SBLKCTL(base)));
+      outb(0, HA_FLAGS(base));
+      return(0);
+  }
+
+  /*
+   * Clear the upper two bits. For the 294x cards, clearing the
+   * upper two bits, will take the card out of diagnostic mode
+   * and make the host adatper LED follow bus activity (will not
+   * always be on).
+   */
+  outb(sblkctl, SBLKCTL(base));
+
+  /*
+   * The IRQ level in i/o port 4 maps directly onto the real
+   * IRQ number. If it's ok, register it with the kernel.
+   *
+   * NB. the Adaptec documentation says the IRQ number is only
+   *     in the lower four bits; the ECU information shows the
+   *     high bit being used as well. Which is correct?
+   *
+   * The 294x cards (PCI) get their interrupt from PCI BIOS.
+   */
+  if (((config.type == AIC_274x) || (config.type == AIC_284x))
+      && (config.irq < 9 || config.irq > 15))
+  {
+    printk("aic7xxx uses unsupported IRQ level, ignoring\n");
+    return(0);
+  }
+
+  /*
+   * Check the IRQ to see if it is shared by another aic7xxx
+   * controller. If it is and sharing of IRQs is not defined,
+   * then return 0 hosts found. If sharing of IRQs is allowed
+   * or the IRQ is not shared by another host adapter, then
+   * proceed.
+   */
+#ifndef AIC7XXX_SHARE_IRQS
+   if (aic7xxx_boards[config.irq] != NULL)
+   {
+     printk("aic7xxx_register: Sharing of IRQs is not configured.\n");
+     return(0);
+   }
+#endif
 
-	PAUSE_SEQUENCER(p);
+  /*
+   * Print out debugging information before re-enabling
+   * the card - a lot of registers on it can't be read
+   * when the sequencer is active.
+   */
+  debug_config(&config);
+
+  /*
+   * Before registry, make sure that the offsets of the
+   * struct scatterlist are what the sequencer will expect,
+   * otherwise disable scatter-gather altogether until someone
+   * can fix it. This is important since the sequencer will
+   * DMA elements of the SG array in while executing commands.
+   */
+  if (template->sg_tablesize != SG_NONE)
+  {
+    struct scatterlist sg;
+
+    if (SG_STRUCT_CHECK(sg))
+    {
+      printk("aic7xxx warning: kernel scatter-gather "
+	     "structures changed, disabling it\n");
+      template->sg_tablesize = SG_NONE;
+    }
+  }
+
+  /*
+   * Register each "host" and fill in the returned Scsi_Host
+   * structure as best we can. Some of the parameters aren't
+   * really relevant for bus types beyond ISA, and none of the
+   * high-level SCSI code looks at it anyway. Why are the fields
+   * there? Also save the pointer so that we can find the
+   * information when an IRQ is triggered.
+   */
+  host = scsi_register(template, sizeof(struct aic7xxx_host));
+  host->can_queue = config.maxscb;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+  host->cmd_per_lun = 2;
+#else
+  host->cmd_per_lun = 1;
+#endif
+  host->this_id = config.scsi_id;
+  host->irq = config.irq;
+  if (config.bus_type == AIC_WIDE)
+  {
+    host->max_id = 16;
+  }
+  if (config.bus_type == AIC_TWIN)
+  {
+    host->max_channel = 1;
+  }
+
+  p = (struct aic7xxx_host *) host->hostdata;
+
+  /* Initialize the scb array by setting the state to free. */
+  for (i = 0; i < AIC7XXX_MAXSCB; i = i + 1)
+  {
+    p->scb_array[i].state = SCB_FREE;
+    p->scb_array[i].next = NULL;
+    p->scb_array[i].cmd = NULL;
+  }
+
+  p->isr_count = 0;
+  p->a_scanned = 0;
+  p->b_scanned = 0;
+  p->base = config.base;
+  p->maxscb = config.maxscb;
+  p->numscb = 0;
+  p->extended = config.extended;
+  p->type = config.type;
+  p->bus_type = config.bus_type;
+  p->have_seeprom = have_seeprom;
+  p->seeprom = sc;
+  p->free_scb = NULL;
+  p->next = NULL;
+
+  p->unpause = config.unpause;
+  p->pause = config.pause;
+
+  if (aic7xxx_boards[config.irq] == NULL)
+  {
+    /*
+     * Register IRQ with the kernel.
+     */
+    if (request_irq(config.irq, aic7xxx_isr, SA_INTERRUPT, "aic7xxx"))
+    {
+      printk("aic7xxx couldn't register irq %d, ignoring\n", config.irq);
+      return(0);
+    }
+    aic7xxx_boards[config.irq] = host;
+  }
+  else
+  {
+    /*
+     * We have found a host adapter sharing an IRQ of a previously
+     * registered host adapter. Add this host adapter's Scsi_Host
+     * to the beginning of the linked list of hosts at the same IRQ.
+     */
+    p->next = aic7xxx_boards[config.irq];
+    aic7xxx_boards[config.irq] = host;
+  }
+
+  /*
+   * Load the sequencer program, then re-enable the board -
+   * resetting the AIC-7770 disables it, leaving the lights
+   * on with nobody home. On the PCI bus you *may* be home,
+   * but then your mailing address is dynamically assigned
+   * so no one can find you anyway :-)
+   */
+  printk("aic7xxx: Downloading sequencer code..");
+  aic7xxx_loadseq(base);
+
+  /* Set Fast Mode and Enable the board */
+  outb(FASTMODE, SEQCTL(base));
+
+  if ((p->type == AIC_274x || p->type == AIC_284x))
+  {
+    outb(ENABLE, BCTL(base));
+  }
+
+  printk("done.\n");
+
+  /*
+   * Set the SCSI Id, SXFRCTL1, and SIMODE1, for both channels
+   */
+  if (p->bus_type == AIC_TWIN)
+  {
+    /*
+     * The device is gated to channel B after a chip reset,
+     * so set those values first.
+     */
+    outb(config.scsi_id_b, SCSIID(base));
+    scsi_conf = inb(HA_SCSICONF(base) + 1) & (ENSPCHK | STIMESEL);
+    scsi_conf = scsi_conf | ENSTIMER | ACTNEGEN | STPWEN;
+    outb(scsi_conf, SXFRCTL1(base));
+    outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base));
+    /* Select Channel A */
+    outb(0, SBLKCTL(base));
+  }
+  outb(config.scsi_id, SCSIID(base));
+  scsi_conf = inb(HA_SCSICONF(base)) & (ENSPCHK | STIMESEL);
+  outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base));
+  outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base));
+
+  /* Look at the information that board initialization or the board
+   * BIOS has left us. In the lower four bits of each target's
+   * scratch space any value other than 0 indicates that we should
+   * initiate synchronous transfers. If it's zero, the user or the
+   * BIOS has decided to disable synchronous negotiation to that
+   * target so we don't activate the needsdtr flag.
+   */
+  p->needsdtr_copy = 0;
+  p->sdtr_pending = 0;
+  p->needwdtr_copy = 0;
+  p->wdtr_pending = 0;
+  if (p->bus_type == AIC_SINGLE)
+  {
+    max_targets = 8;
+  }
+  else
+  {
+    max_targets = 16;
+  }
+
+  for (i = 0; i < max_targets; i = i + 1)
+  {
+    if (have_seeprom)
+    {
+      target_settings = (sc.device_flags[i] & CFXFER) << 4;
+      if (sc.device_flags[i] & CFSYNCH)
+      {
+	p->needsdtr_copy = p->needsdtr_copy | (0x01 << i);
+      }
+      if ((sc.device_flags[i] & CFWIDEB) && (p->bus_type == AIC_WIDE))
+      {
+	p->needwdtr_copy = p->needwdtr_copy | (0x01 << i);
+      }
+    }
+    else
+    {
+      target_settings = inb(HA_TARG_SCRATCH(base) + i);
+      if (target_settings & 0x0F)
+      {
+	p->needsdtr_copy = p->needsdtr_copy | (0x01 << i);
+	/*
+	 * Default to asynchronous transfers (0 offset)
+	 */
+	target_settings = target_settings & 0xF0;
+      }
+      /*
+       * If we are not wide, forget WDTR. This makes the driver
+       * work on some cards that don't leave these fields cleared
+       * when BIOS is not installed.
+       */
+      if ((target_settings & 0x80) && (p->bus_type == AIC_WIDE))
+      {
+	p->needwdtr_copy = p->needwdtr_copy | (0x01 << i);
+	target_settings = target_settings & 0x7F;
+      }
+    }
+    outb(target_settings, (HA_TARG_SCRATCH(base) + i));
+  }
+
+  p->needsdtr = p->needsdtr_copy;
+  p->needwdtr = p->needwdtr_copy;
+  printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
+  printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
+
+  /* 
+   * Clear the control byte for every SCB so that the sequencer
+   * doesn't get confused and think that one of them is valid
+   */
+  for (i = 0; i < config.maxscb; i = i + 1)
+  {
+    outb(i, SCBPTR(base));
+    outb(0, SCBARRAY(base));
+  }
+
+  /*
+   * For reconnecting targets, the sequencer code needs to
+   * know how many SCBs it has to search through.
+   */
+  outb(config.maxscb, HA_SCBCOUNT(base));
+
+  /*
+   * Clear the active flags - no targets are busy.
+   */
+  outb(0, HA_ACTIVE0(base));
+  outb(0, HA_ACTIVE1(base));
+
+  /* We don't have any waiting selections */
+  outb (SCB_LIST_NULL, WAITING_SCBH(base));
+  outb (SCB_LIST_NULL, WAITING_SCBT(base));
+
+  /*
+   * Reset the SCSI bus. Is this necessary?
+   *   There may be problems for a warm boot without resetting
+   *   the SCSI bus. Either BIOS settings in scratch RAM
+   *   will not get reinitialized, or devices may stay at
+   *   previous negotiated settings (SDTR and WDTR) while
+   *   the driver will think that no negotiations have been
+   *   performed.
+   *
+   * Some devices need a long time to "settle" after a SCSI
+   * bus reset.
+   */
+
+  if (!aic7xxx_no_reset)
+  {
+    printk("Resetting the SCSI bus...\n");
+    outb(SCSIRSTO, SCSISEQ(base));
+    udelay(1000);
+    outb(0, SCSISEQ(base));
+    aic7xxx_delay(AIC7XXX_RESET_DELAY);
+  }
+
+  /*
+   * Unpause the sequencer before returning and enable
+   * interrupts - we shouldn't get any until the first
+   * command is sent to us by the high-level SCSI code.
+   */
+  UNPAUSE_SEQUENCER(p);
+  return(found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_detect
+ *
+ * Description:
+ *   Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *-F*************************************************************************/
+int
+aic7xxx_detect(Scsi_Host_Template *template)
+{
+  aha_type type = AIC_NONE;
+  int found = 0, slot, base;
+  unsigned char irq = 0;
+  int i;
+
+  /*
+   * Since we may allow sharing of IRQs, it is imperative
+   * that we "null-out" the aic7xxx_boards array. It is
+   * not guaranteed to be initialized to 0 (NULL). We use
+   * a NULL entry to indicate that no prior hosts have
+   * been found/registered for that IRQ.
+   */
+  for (i = 0; i <= MAXIRQ; i++)
+  {
+    aic7xxx_boards[i] = NULL;
+  }
+
+  /*
+   * EISA/VL-bus card signature probe.
+   */
+  for (slot = MINSLOT; slot <= MAXSLOT; slot++)
+  {
+    base = SLOTBASE(slot);
+
+    if (check_region(MINREG(base), MAXREG(base) - MINREG(base)))
+    {
+      /*
+       * Some other driver has staked a
+       * claim to this i/o region already.
+       */
+      continue;
+    }
+
+    type = aic7xxx_probe(slot, HID0(base));
+    if (type != AIC_NONE)
+    {
+      printk("aic7xxx: hcntrl=0x%x\n", inb(HCNTRL(base)));
+#if 0
+      outb(inb(HCNTRL(base)) | CHIPRST, HCNTRL(base));
+      irq = inb(HA_INTDEF(base)) & 0x0F;
+#endif
 
-	/*
-	 *  This is the best case, really.  Check to see if the
-	 *  command is still in the sequencer's input queue.  If
-	 *  so, simply remove it.  Reload the queue afterward.
-	 */
-	queued = inb(O_QINCNT(p->base));
-	
-	for (i = found = 0; i < queued; i++) {
-		scbsave[i] = inb(O_QINFIFO(p->base));
-
-		if (scbsave[i] == scb) {
-			found = 1;
-			i -= 1;
-		}
+      /*
+       * We "find" a AIC-7770 if we locate the card
+       * signature and we can set it up and register
+       * it with the kernel without incident.
+       */
+      found += aic7xxx_register(template, type, base, irq);
+    }
+  }
+
+#ifdef CONFIG_PCI
+
+#define DEVREVID  0x08
+#define DEVCONFIG 0x40
+#define DEVSTATUS 0x41
+#define RAMPSM    0x02
+
+/* This should be defined in pci.h */
+#define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
+#define PCI_DEVICE_ID_ADAPTEC_7872	0x7278
+
+  /*
+   * PCI-bus probe.
+   */
+  if (pcibios_present())
+  {
+    int error;
+    int done = 0;
+    unsigned int io_port;
+    unsigned short index = 0;
+    unsigned char pci_bus, pci_device_fn;
+    unsigned char devrevid, devconfig, devstatus;
+    char rev_id[] = {'B', 'C', 'D'};
+
+    while (!done)
+    {
+      if ((!pcibios_find_device(PCI_VENDOR_ID_ADAPTEC,
+				PCI_DEVICE_ID_ADAPTEC_294x,
+				index, &pci_bus, &pci_device_fn)) ||
+	   (!pcibios_find_device(PCI_VENDOR_ID_ADAPTEC,
+				PCI_DEVICE_ID_ADAPTEC_2940,
+				index, &pci_bus, &pci_device_fn)))
+      {
+	type = AIC_7870;
+      }
+      else
+      {
+	if (!pcibios_find_device(PCI_VENDOR_ID_ADAPTEC,
+				PCI_DEVICE_ID_ADAPTEC_7850,
+				index, &pci_bus, &pci_device_fn))
+	{
+	  type = AIC_7850;
 	}
-
-	queued -= found;
-	for (i = 0; i < queued; i++)
-		outb(scbsave[i], O_QINFIFO(p->base));
-
-	if (found)
-		goto complete;
-
+	else
+	{
+	  if (!pcibios_find_device(PCI_VENDOR_ID_ADAPTEC,
+				  PCI_DEVICE_ID_ADAPTEC_7872,
+				  index, &pci_bus, &pci_device_fn))
+	  {
+	    type = AIC_7872;
+	  }
+	  else
+	  {
+	    type = AIC_NONE;
+	    done = 1;
+	  }
+	}
+      }
+
+      if (!done)
+      {
 	/*
-	 *  Check the current SCB bank.  If it's not the one belonging
-	 *  to the command we want to kill, assume that the command
-	 *  is disconnected.  It's rather a pain to force a reconnect
-	 *  and send a message to the target, so we abdicate responsibility
-	 *  in this case.
+	 * Read esundry information from PCI BIOS.
 	 */
-	if (inb(O_SCBPTR(p->base)) != scb) {
-		if (unpause)
-			UNPAUSE_SEQUENCER(p);
-		return(k_disconnect);
-	}
+	error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+					  PCI_BASE_ADDRESS_0, &io_port);
 
-	/*
-	 *  Presumably at this point our target command is active.  Check
-	 *  to see if there's a message already in effect.  If not, place
-	 *  our message in and assert ATN so the target goes into MESSAGE
-	 *  OUT phase.
-	 */
-	if (inb(HA_MSG_FLAGS(p->base)) & 0x80) {
-		if (unpause)
-			UNPAUSE_SEQUENCER(p);
-		return(k_busy);
+	if (error)
+	{
+	  panic("aic7xxx_detect: error 0x%x reading i/o port.\n", error);
 	}
 
-	outb(0x80, HA_MSG_FLAGS(p->base));		/* active message */
-	outb(1, HA_MSG_LEN(p->base));			/* length = 1 */
-	outb(message, HA_MSG_START(p->base));		/* message body */
-
-	/*
-	 *  Assert ATN.  Use the value of SCSISIGO saved by the
-	 *  sequencer code so we don't alter its contents radically
-	 *  in the middle of something critical.
-	 */
-	outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base));
+	error = pcibios_read_config_byte(pci_bus, pci_device_fn,
+					 PCI_INTERRUPT_LINE, &irq);
+	if (error)
+	{
+	  panic("aic7xxx_detect: error %d reading irq.\n", error);
+	}
 
 	/*
-	 *  The command has been killed.  Do the bookkeeping, unpause
-	 *  the sequencer, and notify the higher-level SCSI code.
+	 * Make the base I/O register look like EISA and VL-bus.
 	 */
-complete:
-	p->SCB_array[scb] = NULL;
-	if (unpause)
-		UNPAUSE_SEQUENCER(p);
-
-	cmd->result = result << 16;
-	cmd->scsi_done(cmd);
-	return(k_ok);
-}
-
-int aic7xxx_abort(Scsi_Cmnd *cmd)
-{
-	int rv;
-	long flags;
+	base = io_port - 0xC01;
 
-	save_flags(flags);
-	cli();
+	printk("aic7xxx: hcntrl=0x%x\n", inb(HCNTRL(base)));
+	outb(inb(HCNTRL(base)) | CHIPRST, HCNTRL(base));
 
-	switch (aic7xxx_kill(cmd, ABORT, DID_ABORT, !0)) {
-	    case k_ok:		rv = SCSI_ABORT_SUCCESS;	break;
-	    case k_busy:	rv = SCSI_ABORT_BUSY;		break;
-	    case k_absent:	rv = SCSI_ABORT_NOT_RUNNING;	break;
-	    case k_disconnect:	rv = SCSI_ABORT_SNOOZE;		break;
-	    default:
-		panic("aic7xxx_do_abort: internal error\n");
+	error = pcibios_read_config_byte(pci_bus, pci_device_fn,
+					 DEVREVID, &devrevid);
+	if (devrevid < 3)
+	{
+	  printk ("aic7xxx_detect: AIC-7870 Rev %c\n", rev_id[devrevid]);
+	}
+	error = pcibios_read_config_byte(pci_bus, pci_device_fn,
+					 DEVCONFIG, &devconfig);
+	error = pcibios_read_config_byte(pci_bus, pci_device_fn,
+					 DEVSTATUS, &devstatus);
+	printk ("aic7xxx_detect: devconfig 0x%x, devstatus 0x%x\n",
+		devconfig, devstatus);
+	if (devstatus & RAMPSM)
+	{
+	  printk ("aic7xxx_detect: detected external SCB RAM, "
+		  "mail deang@ims.com for test patch");
 	}
 
-	restore_flags(flags);
-	return(rv);
-}
+	found += aic7xxx_register(template, type, base, irq);
+	index += 1;
+      }
+    }
+  }
+#endif CONFIG_PCI
 
-/*
- *  Resetting the bus always succeeds - is has to, otherwise the
- *  kernel will panic!  Try a surgical technique - sending a BUS
- *  DEVICE RESET message - on the offending target before pulling
- *  the SCSI bus reset line.
- */
+  template->name = (char *) aic7xxx_info(NULL);
+  return(found);
+}
 
-int aic7xxx_reset(Scsi_Cmnd *cmd)
-{
-	int i;
-	long flags;
-	Scsi_Cmnd *reset;
-	struct aic7xxx_host *p;
 
-	p = (struct aic7xxx_host *)cmd->host->hostdata;
-	save_flags(flags);
-	cli();
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_buildscb
+ *
+ * Description:
+ *   Build a SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_buildscb(struct aic7xxx_host *p,
+		 Scsi_Cmnd *cmd,
+		 struct aic7xxx_scb *scb)
+{
+  void *addr;
+  unsigned length;
+  unsigned short mask;
+
+  /*
+   * Setup the control byte if we need negotiation and have not
+   * already requested it.
+   */
+#ifdef AIC7XXX_TAGGED_QUEUEING
+  if (cmd->device->tagged_supported)
+  {
+    if (cmd->device->tagged_queue == 0)
+    {
+      printk ("aic7xxx_buildscb: Enabling tagged queuing for target %d, "
+	      "channel %d\n", cmd->target, cmd->channel);
+      cmd->device->tagged_queue = 1;
+      cmd->device->current_tag = 1;  /* enable tagging */
+    }
+    cmd->tag = cmd->device->current_tag;
+    cmd->device->current_tag = cmd->device->current_tag + 1;
+    scb->control = scb->control | SCB_TE;
+  }
+#endif
+  mask = (0x01 << cmd->target);
+  if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
+  {
+    p->wdtr_pending = p->wdtr_pending | mask;
+    scb->control = scb->control | SCB_NEEDWDTR;
+#if 0
+    printk("Sending WDTR request to target %d.\n", cmd->target);
+#endif
+  }
+  else
+  {
+    if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
+    {
+      p->sdtr_pending = p->sdtr_pending | mask;
+      scb->control = scb->control | SCB_NEEDSDTR;
+#if 0
+      printk("Sending SDTR request to target %d.\n", cmd->target);
+#endif
+    }
+  }
 
-	switch (aic7xxx_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) {
+#if 0
+  printk("aic7xxx_queue: target %d, cmd 0x%x (size %u), wdtr 0x%x, mask 0x%x\n",
+	 cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
+#endif
+  scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+	((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
 
-	    case k_ok:
-		/*
-		 *  The RESET message was sent to the target
-		 *  with no problems.  Flag that target as
-		 *  needing a SDTR negotiation on the next
-		 *  connection and restart the sequencer.
-		 */
-		outb((1 << cmd->target), HA_NEEDSDTR(p->base));
-		UNPAUSE_SEQUENCER(p);
-		break;
+  /*
+   * The interpretation of request_buffer and request_bufflen
+   * changes depending on whether or not use_sg is zero; a
+   * non-zero use_sg indicates the number of elements in the
+   * scatter-gather array.
+   *
+   * The AIC-7770 can't support transfers of any sort larger
+   * than 2^24 (three-byte count) without backflips. For what
+   * the kernel is doing, this shouldn't occur. I hope.
+   */
+  length = aic7xxx_length(cmd, 0);
+
+  if (length > 0xFFFFFF)
+  {
+    panic("aic7xxx_buildscb: can't transfer > 2^24 - 1 bytes\n");
+  }
+
+  /*
+   * XXX - this relies on the host data being stored in a
+   *       little-endian format.
+   */
+  addr = cmd->cmnd;
+  scb->SCSI_cmd_length = cmd->cmd_len;
+  memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
 
-	    case k_absent:
-		/*
-		 *  The sequencer will not be paused if aic7xxx_kill()
-		 *  couldn't find the command.
-		 */
-		PAUSE_SEQUENCER(p);
-		/* falls through */
+  if (cmd->use_sg)
+  {
+#if 0
+    debug("aic7xxx_buildscb: SG used, %d segments, length %u\n",
+	  cmd->use_sg, length);
+#endif
+    scb->SG_segment_count = cmd->use_sg;
+    memcpy(scb->SG_list_pointer, &cmd->request_buffer,
+	   sizeof(scb->SG_list_pointer));
+  }
+  else
+  {
+#if 0
+    debug ("aic7xxx_buildscb: Creating scatterlist, addr=0x%lx, length=%d.\n",
+	   (unsigned long) cmd->request_buffer, cmd->request_bufflen);
+#endif
+#ifdef AIC7XXX_USE_SG
+    scb->SG_segment_count = 1;
+    scb->sg.address = (char *) cmd->request_buffer;
+    scb->sg.length = cmd->request_bufflen;
+    addr = &scb->sg;
+    memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
+#else
+    scb->SG_segment_count = 0;
+    memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer));
+    memcpy(scb->data_count, &cmd->request_bufflen, 3);
+#endif
+  }
+}
 
-	    case k_busy:
-	    case k_disconnect:
-		/*
-		 *  Do a hard reset of the SCSI bus.  According to the
-		 *  SCSI-2 draft specification, reset has to be asserted
-		 *  for at least 25us.  I'm invoking the kernel delay
-		 *  function for 30us since I'm not totally trusting of
-		 *  the busy loop timing.
-		 *
-		 *  XXX - I'm not convinced this works.  I tried resetting
-		 *	  the bus before, trying to get the devices on the
-		 *	  bus to revert to asynchronous transfer, and it
-		 *	  never seemed to work.
-		 */
-		debug("aic7xxx: attempting to reset scsi bus and card\n");
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_queue
+ *
+ * Description:
+ *   Queue a SCB to the controller.
+ *-F*************************************************************************/
+int
+aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
+{
+  long flags;
+#ifndef AIC7XXX_USE_DMA
+  int old_scbptr;
+#endif
+  struct aic7xxx_host *p;
+  struct aic7xxx_scb *scb;
+  unsigned char curscb;
+
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+
+  /* Check to see if channel was scanned. */
+ if (!p->a_scanned && (cmd->channel == 0))
+  {
+    printk("aic7xxx: Scanning channel A for devices.\n");
+    p->a_scanned = 1;
+  }
+  else
+  {
+    if (!p->b_scanned && (cmd->channel == 1))
+    {
+      printk("aic7xxx: Scanning channel B for devices.\n");
+      p->b_scanned = 1;
+    }
+  }
 
-		outb(1, O_SCSISEQ(p->base));		/* SCSIRSTO */
-		udelay(30);
-		outb(0, O_SCSISEQ(p->base));		/* !SCSIRSTO */
+#if 0
+  debug("aic7xxx_queue: cmd 0x%x (size %u), target %d, channel %d, lun %d\n",
+	cmd->cmnd[0], cmd->cmd_len, cmd->target, cmd->channel,
+	cmd->lun & 0x07);
+#endif
 
-		outb(0xff, HA_NEEDSDTR(p->base));
-		UNPAUSE_SEQUENCER(p);
+  /*
+   * This is a critical section, since we don't want the
+   * interrupt routine mucking with the host data or the
+   * card. Since the kernel documentation is vague on
+   * whether or not we are in a cli/sti pair already, save
+   * the flags to be on the safe side.
+   */
+  save_flags(flags);
+  cli();
+
+  /*
+   * Find a free slot in the SCB array to load this command
+   * into. Since can_queue is set to the maximum number of
+   * SCBs for the card, we should always find one.
+   *
+   * First try to find an scb in the free list. If there are
+   * none in the free list, then check the current number of
+   * of scbs and take an unused one from the scb array.
+   */
+  scb = p->free_scb;
+  if (scb != NULL)
+  { /* found one in the free list */
+    p->free_scb = scb->next;   /* remove and update head of list */
+    /*
+     * Warning! For some unknown reason, the scb at the head
+     * of the free list is not the same address that it should
+     * be. That's why we set the scb pointer taken by the
+     * position in the array. The scb at the head of the list
+     * should match this address, but it doesn't.
+     */
+    scb = &(p->scb_array[scb->position]);
+    scb->control = 0;
+    scb->state = SCB_ACTIVE;
+  }
+  else
+  {
+    if (p->numscb >= p->maxscb)
+    {
+      panic("aic7xxx_queue: couldn't find a free scb\n");
+    }
+    else
+    {
+      /*
+       * Initialize the scb within the scb array. The
+       * position within the array is the position on
+       * the board that it will be loaded.
+       */
+      scb = &(p->scb_array[p->numscb]);
+      memset(scb, 0, sizeof(*scb));
+
+      scb->position = p->numscb;
+      p->numscb = p->numscb + 1;
+      scb->state = SCB_ACTIVE;
+      scb->next_waiting = SCB_LIST_NULL;
+      memcpy(scb->host_scb, &scb, sizeof(scb));
+#ifdef AIC7XXX_USE_DMA
+      scb->control = SCB_NEEDDMA;
+#endif
+      PAUSE_SEQUENCER(p);
+      curscb = inb(SCBPTR(p->base));
+      outb(scb->position, SCBPTR(p->base));
+      aic7xxx_putdmascb(p->base, scb);
+      outb(curscb, SCBPTR(p->base));
+      UNPAUSE_SEQUENCER(p);
+      scb->control = 0;
+    }
+  }
+
+  scb->cmd = cmd;
+  aic7xxx_position(cmd) = scb->position;
+
+  /*
+   * Construct the SCB beforehand, so the sequencer is
+   * paused a minimal amount of time.
+   */
+  aic7xxx_buildscb(p, cmd, scb);
 
-		/*
-		 *  Locate the command and return a "reset" status
-		 *  for it.  This is not completely correct and will
-		 *  probably return to haunt me later.
-		 */
-		for (i = 0; i < p->maxscb; i++) {
-			if (cmd == p->SCB_array[i]) {
-				reset = (Scsi_Cmnd *)p->SCB_array[i];
-				p->SCB_array[i] = NULL;
-				reset->result = DID_RESET << 16;
-				reset->scsi_done(reset);
-				break;
-			}
-		}
-		break;
+#if 0
+  if (scb != &p->scb_array[scb->position])
+  {
+    printk("aic7xxx_queue: address of scb by position does not match scb address\n");
+  }
+  printk("aic7xxx_queue: SCB pos=%d, cmdptr=0x%x, state=%d, freescb=0x%x\n",
+	 scb->position, (unsigned int) scb->cmd,
+	 scb->state, (unsigned int) p->free_scb);
+#endif
+  /*
+   * Pause the sequencer so we can play with its registers -
+   * wait for it to acknowledge the pause.
+   *
+   * XXX - should the interrupts be left on while doing this?
+   */
+  PAUSE_SEQUENCER(p);
+
+  /*
+   * Save the SCB pointer and put our own pointer in - this
+   * selects one of the four banks of SCB registers. Load
+   * the SCB, then write its pointer into the queue in FIFO
+   * and restore the saved SCB pointer.
+   */
+#ifdef AIC7XXX_USE_DMA
+  aic7xxx_putscb(p->base, scb);
+#else
+  old_scbptr = inb(SCBPTR(p->base));
+  outb(scb->position, SCBPTR(p->base));
 
-	    default:
-		panic("aic7xxx_reset: internal error\n");
-	}
+  aic7xxx_putscb(p->base, scb);
 
-	restore_flags(flags);
-	return(SCSI_RESET_SUCCESS);
+  outb(scb->position, QINFIFO(p->base));
+  outb(old_scbptr, SCBPTR(p->base));
+#endif
+  /*
+   * Make sure the Scsi_Cmnd pointer is saved, the struct it
+   * points to is set up properly, and the parity error flag
+   * is reset, then unpause the sequencer and watch the fun
+   * begin.
+   */
+  cmd->scsi_done = fn;
+  aic7xxx_error(cmd) = DID_OK;
+  aic7xxx_status(cmd) = 0;
+
+  cmd->result = 0;
+  memset (&cmd->sense_buffer, 0, sizeof (cmd->sense_buffer));
+
+  UNPAUSE_SEQUENCER(p);
+  restore_flags(flags);
+  return(0);
 }
 
-int aic7xxx_biosparam(Disk *disk, int devno, int geom[])
-{
-	int heads, sectors, cylinders;
-	struct aic7xxx_host *p;
-	
-	p = (struct aic7xxx_host *)disk->device->host->hostdata;
+/* return values from aic7xxx_kill */
+typedef enum {
+  k_ok,             /* scb found and message sent */
+  k_busy,           /* message already present */
+  k_absent,         /* couldn't locate scb */
+  k_disconnect,     /* scb found, but disconnected */
+} k_state;
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_kill
+ *
+ * Description:
+ *   This must be called with interrupts disabled - it's going to
+ *   be messing around with the host data, and an interrupt being
+ *   fielded in the middle could get ugly.
+ *
+ *   Since so much of the abort and reset code is shared, this
+ *   function performs more magic than it really should. If the
+ *   command completes ok, then it will call scsi_done with the
+ *   result code passed in. The unpause parameter controls whether
+ *   or not the sequencer gets unpaused - the reset function, for
+ *   instance, may want to do something more aggressive.
+ *
+ *   Note that the command is checked for in our SCB_array first
+ *   before the sequencer is paused, so if k_absent is returned,
+ *   then the sequencer is NOT paused.
+ *-F*************************************************************************/
+static k_state
+aic7xxx_kill(Scsi_Cmnd *cmd, unsigned char message,
+	     unsigned int result, int unpause)
+{
+  struct aic7xxx_host *p;
+  struct aic7xxx_scb *scb;
+  int i, active_scb, found, queued;
+  unsigned char scbsave[AIC7XXX_MAXSCB];
+  unsigned char flags;
+  int scb_control;
+  k_state status;
 
-	/*
-	 *  XXX - if I could portably find the card's configuration
-	 *	  information, then this could be autodetected instead
-	 *	  of left to a boot-time switch.
-	 */
-	heads = 64;
-	sectors = 32;
-	cylinders = disk->capacity / (heads * sectors);
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+  scb = &p->scb_array[aic7xxx_position(cmd)];
 
-	if (p->extended && cylinders > 1024) {
-		heads = 255;
-		sectors = 63;
-		cylinders = disk->capacity / (255 * 63);
-	}
+#if 0
+  printk("aic7xxx_kill: In the kill function...\n");
+#endif
+  PAUSE_SEQUENCER(p);
 
-	geom[0] = heads;
-	geom[1] = sectors;
-	geom[2] = cylinders;
+  /*
+   * Case 1: In the QINFIFO
+   *
+   * This is the best case, really. Check to see if the
+   * command is still in the sequencer's input queue. If
+   * so, simply remove it. Reload the queue afterward.
+   */
+  queued = inb(QINCNT(p->base));
+
+  for (i = found = 0; i < (queued - found); i++)
+  {
+    scbsave[i] = inb(QINFIFO(p->base));
+
+    if (scbsave[i] == scb->position)
+    {
+      found = 1;
+      i = i - 1;
+    }
+  }
+
+  for (queued = 0; queued < i; queued++)
+  {
+    outb(scbsave[queued], QINFIFO(p->base));
+  }
+
+  if (found)
+  {
+    status = k_ok;
+    goto complete;
+  }
+
+  active_scb = inb(SCBPTR(p->base));
+  /*
+   * Case 2: Not the active command
+   *
+   * Check the current SCB bank. If it's not the one belonging
+   * to the command we want to kill, select the scb we want to
+   * abort and turn off the disconnected bit. The driver will
+   * then abort the command and notify us of the abort.
+   */
+  if (active_scb != scb->position)
+  {
+    outb(scb->position, SCBPTR(p->base));
+    scb_control = inb(SCBARRAY(p->base));
+    scb_control = scb_control & ~SCB_DIS;
+    outb(scb_control, SCBARRAY(p->base));
+    outb(active_scb, SCBPTR(p->base));
+    status = k_disconnect;
+    goto complete;
+  }
+
+  scb_control = inb(SCBARRAY(p->base));
+  if (scb_control & SCB_DIS)
+  {
+    scb_control = scb_control & ~SCB_DIS;
+    outb(scb_control, SCBARRAY(p->base));
+    status = k_disconnect;
+    goto complete;
+  }
+
+  /*
+   * Presumably at this point our target command is active. Check
+   * to see if there's a message already in effect. If not, place
+   * our message in and assert ATN so the target goes into MESSAGE
+   * OUT phase.
+   */
+  flags = inb(HA_FLAGS(p->base));
+  if (flags & ACTIVE_MSG)
+  {
+    /*
+     * If there is a message in progress, reset the bus
+     * and have all devices renegotiate.
+     */
+    if (cmd->channel & 0x01)
+    {
+      p->needsdtr = p->needsdtr_copy & 0xFF00;
+      p->sdtr_pending = p->sdtr_pending & 0x00FF;
+      outb(0, HA_ACTIVE1(p->base));
+    }
+    else
+    {
+      if (p->bus_type == AIC_WIDE)
+      {
+	p->needsdtr = p->needsdtr_copy;
+	p->needwdtr = p->needwdtr_copy;
+	p->sdtr_pending = 0;
+	p->wdtr_pending = 0;
+	outb(0, HA_ACTIVE0(p->base));
+	outb(0, HA_ACTIVE1(p->base));
+      }
+      else
+      {
+	p->needsdtr = p->needsdtr_copy & 0x00FF;
+	p->sdtr_pending = p->sdtr_pending & 0xFF00;
+	outb(0, HA_ACTIVE0(p->base));
+      }
+    }
+    /* Reset the bus. */
+    outb(SCSIRSTO, SCSISEQ(p->base));
+    udelay(1000);
+    outb(0, SCSISEQ(p->base));
+    aic7xxx_delay(AIC7XXX_RESET_DELAY);
+
+    status = k_busy;
+    goto complete;
+  }
+
+  outb(flags | ACTIVE_MSG, HA_FLAGS(p->base));    /* active message */
+  outb(1, HA_MSG_LEN(p->base));                   /* length = 1 */
+  outb(message, HA_MSG_START(p->base));           /* message body */
+
+  /*
+   * Assert ATN. Use the value of SCSISIGO saved by the
+   * sequencer code so we don't alter its contents radically
+   * in the middle of something critical.
+   */
+  outb(inb(HA_SIGSTATE(p->base)) | 0x10, SCSISIGO(p->base));
+
+  status = k_ok;
+
+  /*
+   * The command has been killed. Do the bookkeeping, unpause
+   * the sequencer, and notify the higher-level SCSI code.
+   */
+complete:
+  if (unpause)
+  {
+    UNPAUSE_SEQUENCER(p);
+  }
+
+  /*
+   * Mark the scb as free and clear the scbs command pointer.
+   * Add the scb to the head of the free list being careful
+   * to preserve the next pointers.
+   */
+  scb->state = SCB_FREE;          /* mark the scb as free */
+  scb->cmd = NULL;                /* clear the command pointer */
+  scb->next = p->free_scb;        /* preserve next pointer */
+  p->free_scb = scb;              /* add at head of free list */
+  cmd->result = cmd->result << 16;
+  cmd->scsi_done(cmd);
+  return(status);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_abort
+ *
+ * Description:
+ *   Abort the current SCSI command(s).
+ *-F*************************************************************************/
+int
+aic7xxx_abort(Scsi_Cmnd *cmd)
+{
+  int rv;
+  long flags;
+
+  save_flags(flags);
+  cli();
+
+  switch (aic7xxx_kill(cmd, ABORT, DID_ABORT, !0))
+  {
+    case k_ok:          rv = SCSI_ABORT_SUCCESS;        break;
+    case k_busy:        rv = SCSI_ABORT_BUSY;           break;
+    case k_absent:      rv = SCSI_ABORT_NOT_RUNNING;    break;
+    case k_disconnect:  rv = SCSI_ABORT_SNOOZE;         break;
+    default:            panic("aic7xxx_abort: internal error\n");
+  }
+
+  restore_flags(flags);
+  return(rv);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset
+ *
+ * Description:
+ *   Resetting the bus always succeeds - is has to, otherwise the
+ *   kernel will panic! Try a surgical technique - sending a BUS
+ *   DEVICE RESET message - on the offending target before pulling
+ *   the SCSI bus reset line.
+ *-F*************************************************************************/
+int
+aic7xxx_reset(Scsi_Cmnd *cmd)
+{
+  long flags;
+  struct aic7xxx_host *p;
+
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+  save_flags(flags);
+  cli();
+
+  switch (aic7xxx_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0))
+  {
+    case k_ok:
+      /*
+       * The RESET message was sent to the target
+       * with no problems. Flag that target as
+       * needing a SDTR negotiation on the next
+       * connection and restart the sequencer.
+       */
+      p->needsdtr = p->needsdtr & (1 << cmd->target);
+      UNPAUSE_SEQUENCER(p);
+      break;
+
+    case k_absent:
+      /*
+       * The sequencer will not be paused if aic7xxx_kill()
+       * couldn't find the command.
+       */
+      PAUSE_SEQUENCER(p);
+      /* falls through */
+
+    case k_busy:
+      cmd->result = DID_RESET << 16;  /* return reset code */
+      cmd->scsi_done(cmd);
+      break;
+
+    case k_disconnect:
+      /*
+       * Do a hard reset of the SCSI bus. According to the
+       * SCSI-2 draft specification, reset has to be asserted
+       * for at least 25us. I'm invoking the kernel delay
+       * function for 30us since I'm not totally trusting of
+       * the busy loop timing.
+       *
+       * XXX - I'm not convinced this works. I tried resetting
+       *       the bus before, trying to get the devices on the
+       *       bus to revert to asynchronous transfer, and it
+       *       never seemed to work.
+       */
+      debug("aic7xxx: attempting to reset scsi bus and card\n");
+
+      outb(SCSIRSTO, SCSISEQ(p->base));
+      udelay(1000);
+      outb(0, SCSISEQ(p->base));
+      aic7xxx_delay(AIC7XXX_RESET_DELAY);
+
+      UNPAUSE_SEQUENCER(p);
+
+      /*
+       * Locate the command and return a "reset" status
+       * for it. This is not completely correct and will
+       * probably return to haunt me later.
+       */
+      cmd->result = DID_RESET << 16;  /* return reset code */
+      cmd->scsi_done(cmd);
+      break;
+
+    default:
+      panic("aic7xxx_reset: internal error\n");
+  }
+
+  restore_flags(flags);
+  return(SCSI_RESET_SUCCESS);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_biosparam
+ *
+ * Description:
+ *   Return the disk geometry for the given SCSI device.
+ *-F*************************************************************************/
+int
+aic7xxx_biosparam(Disk *disk, int devno, int geom[])
+{
+  int heads, sectors, cylinders;
+  struct aic7xxx_host *p;
+
+  p = (struct aic7xxx_host *) disk->device->host->hostdata;
+
+  /*
+   * XXX - if I could portably find the card's configuration
+   *       information, then this could be autodetected instead
+   *       of left to a boot-time switch.
+   */
+  heads = 64;
+  sectors = 32;
+  cylinders = disk->capacity / (heads * sectors);
+
+  if (p->extended && cylinders > 1024)
+  {
+    heads = 255;
+    sectors = 63;
+    cylinders = disk->capacity / (255 * 63);
+  }
+
+  geom[0] = heads;
+  geom[1] = sectors;
+  geom[2] = cylinders;
+
+  return(0);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AIC7XXX;
 
-	return(0);
-}
+#include "scsi_module.c"
+#endif
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this