File-copy from v4.4.100

This is the result of 'cp' from a linux-stable tree with the 'v4.4.100'
tag checked out (commit 26d6298789e695c9f627ce49a7bbd2286405798a) on
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

Please refer to that tree for all history prior to this point.

Change-Id: I8a9ee2aea93cd29c52c847d0ce33091a73ae6afe
diff --git a/drivers/scsi/aic7xxx/.gitignore b/drivers/scsi/aic7xxx/.gitignore
new file mode 100644
index 0000000..b8ee24d
--- /dev/null
+++ b/drivers/scsi/aic7xxx/.gitignore
@@ -0,0 +1,6 @@
+aic79xx_reg.h
+aic79xx_reg_print.c
+aic79xx_seq.h
+aic7xxx_reg.h
+aic7xxx_reg_print.c
+aic7xxx_seq.h
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic79xx b/drivers/scsi/aic7xxx/Kconfig.aic79xx
new file mode 100644
index 0000000..3b3d599
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx
@@ -0,0 +1,85 @@
+#
+# AIC79XX 2.5.X Kernel configuration File.
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Kconfig.aic79xx#4 $
+#
+config SCSI_AIC79XX
+	tristate "Adaptec AIC79xx U320 support"
+	depends on PCI && SCSI
+	select SCSI_SPI_ATTRS
+	help
+	This driver supports all of Adaptec's Ultra 320 PCI-X
+	based SCSI controllers.
+
+config AIC79XX_CMDS_PER_DEVICE
+	int "Maximum number of TCQ commands per device"
+	depends on SCSI_AIC79XX
+	default "32"
+	---help---
+	Specify the number of commands you would like to allocate per SCSI
+	device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+	This is an upper bound value for the number of tagged transactions
+	to be used for any device.  The aic7xxx driver will automatically
+	vary this number based on device behavior.  For devices with a
+	fixed maximum, the driver will eventually lock to this maximum
+	and display a console message indicating this value.
+
+	Due to resource allocation issues in the Linux SCSI mid-layer, using
+	a high number of commands per device may result in memory allocation
+	failures when many devices are attached to the system.  For this reason,
+	the default is set to 32.  Higher values may result in higher performance
+	on some devices.  The upper bound is 253.  0 disables tagged queueing.
+
+	Per device tag depth can be controlled via the kernel command line
+	"tag_info" option.  See Documentation/scsi/aic79xx.txt for details.
+
+config AIC79XX_RESET_DELAY_MS
+	int "Initial bus reset delay in milli-seconds"
+	depends on SCSI_AIC79XX
+	default "5000"
+	---help---
+	The number of milliseconds to delay after an initial bus reset.
+	The bus settle delay following all error recovery actions is
+	dictated by the SCSI layer and is not affected by this value.
+
+	Default: 5000 (5 seconds)
+
+config AIC79XX_BUILD_FIRMWARE
+	bool "Build Adapter Firmware with Kernel Build"
+	depends on SCSI_AIC79XX && !PREVENT_FIRMWARE_BUILD
+	help
+	This option should only be enabled if you are modifying the firmware
+	source to the aic79xx driver and wish to have the generated firmware
+	include files updated during a normal kernel build.  The assembler
+	for the firmware requires lex and yacc or their equivalents, as well
+	as the db v1 library.  You may have to install additional packages
+	or modify the assembler Makefile or the files it includes if your
+	build environment is different than that of the author.
+
+config AIC79XX_DEBUG_ENABLE
+	bool "Compile in Debugging Code"
+	depends on SCSI_AIC79XX
+	default y
+	help
+	Compile in aic79xx debugging code that can be useful in diagnosing
+	driver errors.
+
+config AIC79XX_DEBUG_MASK
+	int "Debug code enable mask (16383 for all debugging)"
+	depends on SCSI_AIC79XX
+	default "0"
+	help
+	Bit mask of debug options that is only valid if the
+	CONFIG_AIC79XX_DEBUG_ENABLE option is enabled.  The bits in this mask
+	are defined in the drivers/scsi/aic7xxx/aic79xx.h - search for the
+	variable ahd_debug in that file to find them.
+
+config AIC79XX_REG_PRETTY_PRINT
+	bool "Decode registers during diagnostics"
+	depends on SCSI_AIC79XX
+	default y
+	help
+	Compile in register value tables for the output of expanded register
+	contents in diagnostics.  This make it much easier to understand debug
+	output without having to refer to a data book and/or the aic7xxx.reg
+	file.
diff --git a/drivers/scsi/aic7xxx/Kconfig.aic7xxx b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
new file mode 100644
index 0000000..55ac55e
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
@@ -0,0 +1,90 @@
+#
+# AIC7XXX and AIC79XX 2.5.X Kernel configuration File.
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Kconfig.aic7xxx#7 $
+#
+config SCSI_AIC7XXX
+	tristate "Adaptec AIC7xxx Fast -> U160 support (New Driver)"
+	depends on (PCI || EISA) && SCSI
+	select SCSI_SPI_ATTRS
+	---help---
+	This driver supports all of Adaptec's Fast through Ultra 160 PCI
+	based SCSI controllers as well as the aic7770 based EISA and VLB
+	SCSI controllers (the 274x and 284x series).  For AAA and ARO based
+	configurations, only SCSI functionality is provided.
+
+	To compile this driver as a module, choose M here: the
+	module will be called aic7xxx.
+
+config AIC7XXX_CMDS_PER_DEVICE
+	int "Maximum number of TCQ commands per device"
+	depends on SCSI_AIC7XXX
+	default "32"
+	---help---
+	Specify the number of commands you would like to allocate per SCSI
+	device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+	This is an upper bound value for the number of tagged transactions
+	to be used for any device.  The aic7xxx driver will automatically
+	vary this number based on device behavior.  For devices with a
+	fixed maximum, the driver will eventually lock to this maximum
+	and display a console message indicating this value.
+
+	Due to resource allocation issues in the Linux SCSI mid-layer, using
+	a high number of commands per device may result in memory allocation
+	failures when many devices are attached to the system.  For this reason,
+	the default is set to 32.  Higher values may result in higher performance
+	on some devices.  The upper bound is 253.  0 disables tagged queueing.
+
+	Per device tag depth can be controlled via the kernel command line
+	"tag_info" option.  See Documentation/scsi/aic7xxx.txt for details.
+
+config AIC7XXX_RESET_DELAY_MS
+	int "Initial bus reset delay in milli-seconds"
+	depends on SCSI_AIC7XXX
+	default "5000"
+	---help---
+	The number of milliseconds to delay after an initial bus reset.
+	The bus settle delay following all error recovery actions is
+	dictated by the SCSI layer and is not affected by this value.
+
+	Default: 5000 (5 seconds)
+
+config AIC7XXX_BUILD_FIRMWARE
+	bool "Build Adapter Firmware with Kernel Build"
+	depends on SCSI_AIC7XXX && !PREVENT_FIRMWARE_BUILD
+	help
+	This option should only be enabled if you are modifying the firmware
+	source to the aic7xxx driver and wish to have the generated firmware
+	include files updated during a normal kernel build.  The assembler
+	for the firmware requires lex and yacc or their equivalents, as well
+	as the db v1 library.  You may have to install additional packages
+	or modify the assembler Makefile or the files it includes if your
+	build environment is different than that of the author.
+
+config AIC7XXX_DEBUG_ENABLE
+	bool "Compile in Debugging Code"
+	depends on SCSI_AIC7XXX
+	default y
+	help
+	Compile in aic7xxx debugging code that can be useful in diagnosing
+	driver errors.
+
+config AIC7XXX_DEBUG_MASK
+        int "Debug code enable mask (2047 for all debugging)"
+        depends on SCSI_AIC7XXX
+        default "0"
+        help
+	Bit mask of debug options that is only valid if the
+	CONFIG_AIC7XXX_DEBUG_ENABLE option is enabled.  The bits in this mask
+	are defined in the drivers/scsi/aic7xxx/aic7xxx.h - search for the
+	variable ahc_debug in that file to find them.
+
+config AIC7XXX_REG_PRETTY_PRINT
+        bool "Decode registers during diagnostics"
+        depends on SCSI_AIC7XXX
+	default y
+        help
+	Compile in register value tables for the output of expanded register
+	contents in diagnostics.  This make it much easier to understand debug
+	output without having to refer to a data book and/or the aic7xxx.reg
+	file.
diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile
new file mode 100644
index 0000000..741d818
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Makefile
@@ -0,0 +1,85 @@
+#
+# Makefile for the Linux aic7xxx SCSI driver.
+#
+# $Id: //depot/linux-aic79xx-2.5.0/drivers/scsi/aic7xxx/Makefile#8 $
+#
+
+# Let kbuild descend into aicasm when cleaning
+subdir-				+= aicasm
+
+obj-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx.o
+obj-$(CONFIG_SCSI_AIC79XX)	+= aic79xx.o
+
+# Core Fast -> U160 files
+aic7xxx-y					+= aic7xxx_core.o	\
+						   aic7xxx_93cx6.o
+aic7xxx-$(CONFIG_EISA)				+= aic7770.o
+aic7xxx-$(CONFIG_PCI)				+= aic7xxx_pci.o
+aic7xxx-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT)	+= aic7xxx_reg_print.o
+
+# Platform Specific Fast -> U160 Files
+aic7xxx-y					+= aic7xxx_osm.o	\
+						   aic7xxx_proc.o
+aic7xxx-$(CONFIG_EISA)				+= aic7770_osm.o
+aic7xxx-$(CONFIG_PCI)				+= aic7xxx_osm_pci.o
+
+# Core U320 files
+aic79xx-y					+= aic79xx_core.o	\
+						   aic79xx_pci.o
+aic79xx-$(CONFIG_AIC79XX_REG_PRETTY_PRINT)	+= aic79xx_reg_print.o
+
+# Platform Specific U320 Files
+aic79xx-y					+= aic79xx_osm.o	\
+						   aic79xx_proc.o	\
+						   aic79xx_osm_pci.o
+
+ccflags-y += -Idrivers/scsi
+ifdef WARNINGS_BECOME_ERRORS
+ccflags-y += -Werror
+endif
+
+# Files generated that shall be removed upon make clean
+clean-files := aic7xxx_seq.h aic7xxx_reg.h aic7xxx_reg_print.c
+clean-files += aic79xx_seq.h aic79xx_reg.h aic79xx_reg_print.c
+
+# Dependencies for generated files need to be listed explicitly
+
+$(addprefix $(obj)/,$(aic7xxx-y)): $(obj)/aic7xxx_seq.h $(obj)/aic7xxx_reg.h
+$(addprefix $(obj)/,$(aic79xx-y)): $(obj)/aic79xx_seq.h $(obj)/aic79xx_reg.h
+
+aic7xxx-gen-$(CONFIG_AIC7XXX_BUILD_FIRMWARE)	:= $(obj)/aic7xxx_reg.h
+aic7xxx-gen-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT)	+= $(obj)/aic7xxx_reg_print.c
+
+aicasm-7xxx-opts-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT) := \
+	-p $(obj)/aic7xxx_reg_print.c -i aic7xxx_osm.h
+
+ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y)
+$(obj)/aic7xxx_seq.h: $(src)/aic7xxx.seq $(src)/aic7xxx.reg $(obj)/aicasm/aicasm
+	$(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic7xxx_reg.h \
+			      $(aicasm-7xxx-opts-y) -o $(obj)/aic7xxx_seq.h \
+			      $(src)/aic7xxx.seq
+
+$(aic7xxx-gen-y): $(obj)/aic7xxx_seq.h
+else
+$(obj)/aic7xxx_reg_print.c: $(src)/aic7xxx_reg_print.c_shipped
+endif
+
+aic79xx-gen-$(CONFIG_AIC79XX_BUILD_FIRMWARE)	:= $(obj)/aic79xx_reg.h
+aic79xx-gen-$(CONFIG_AIC79XX_REG_PRETTY_PRINT)	+= $(obj)/aic79xx_reg_print.c
+
+aicasm-79xx-opts-$(CONFIG_AIC79XX_REG_PRETTY_PRINT) := \
+	-p $(obj)/aic79xx_reg_print.c -i aic79xx_osm.h
+
+ifeq ($(CONFIG_AIC79XX_BUILD_FIRMWARE),y)
+$(obj)/aic79xx_seq.h: $(src)/aic79xx.seq $(src)/aic79xx.reg $(obj)/aicasm/aicasm
+	$(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic79xx_reg.h \
+			      $(aicasm-79xx-opts-y) -o $(obj)/aic79xx_seq.h \
+			      $(src)/aic79xx.seq
+
+$(aic79xx-gen-y): $(obj)/aic79xx_seq.h
+else
+$(obj)/aic79xx_reg_print.c: $(src)/aic79xx_reg_print.c_shipped
+endif
+
+$(obj)/aicasm/aicasm: $(src)/aicasm/*.[chyl]
+	$(MAKE) -C $(src)/aicasm
diff --git a/drivers/scsi/aic7xxx/aic7770.c b/drivers/scsi/aic7xxx/aic7770.c
new file mode 100644
index 0000000..5000bd6
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7770.c
@@ -0,0 +1,391 @@
+/*
+ * Product specific probe and attach routines for:
+ * 	27/284X and aic7770 motherboard SCSI controllers
+ *
+ * Copyright (c) 1994-1998, 2000, 2001 Justin T. Gibbs.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7770.c#32 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#define ID_AIC7770	0x04907770
+#define ID_AHA_274x	0x04907771
+#define ID_AHA_284xB	0x04907756 /* BIOS enabled */
+#define ID_AHA_284x	0x04907757 /* BIOS disabled*/
+#define	ID_OLV_274x	0x04907782 /* Olivetti OEM */
+#define	ID_OLV_274xD	0x04907783 /* Olivetti OEM (Differential) */
+
+static int aic7770_chip_init(struct ahc_softc *ahc);
+static int aha2840_load_seeprom(struct ahc_softc *ahc);
+static ahc_device_setup_t ahc_aic7770_VL_setup;
+static ahc_device_setup_t ahc_aic7770_EISA_setup;
+static ahc_device_setup_t ahc_aic7770_setup;
+
+struct aic7770_identity aic7770_ident_table[] =
+{
+	{
+		ID_AHA_274x,
+		0xFFFFFFFF,
+		"Adaptec 274X SCSI adapter",
+		ahc_aic7770_EISA_setup
+	},
+	{
+		ID_AHA_284xB,
+		0xFFFFFFFE,
+		"Adaptec 284X SCSI adapter",
+		ahc_aic7770_VL_setup
+	},
+	{
+		ID_AHA_284x,
+		0xFFFFFFFE,
+		"Adaptec 284X SCSI adapter (BIOS Disabled)",
+		ahc_aic7770_VL_setup
+	},
+	{
+		ID_OLV_274x,
+		0xFFFFFFFF,
+		"Adaptec (Olivetti OEM) 274X SCSI adapter",
+		ahc_aic7770_EISA_setup
+	},
+	{
+		ID_OLV_274xD,
+		0xFFFFFFFF,
+		"Adaptec (Olivetti OEM) 274X Differential SCSI adapter",
+		ahc_aic7770_EISA_setup
+	},
+	/* Generic chip probes for devices we don't know 'exactly' */
+	{
+		ID_AIC7770,
+		0xFFFFFFFF,
+		"Adaptec aic7770 SCSI adapter",
+		ahc_aic7770_EISA_setup
+	}
+};
+const int ahc_num_aic7770_devs = ARRAY_SIZE(aic7770_ident_table);
+
+struct aic7770_identity *
+aic7770_find_device(uint32_t id)
+{
+	struct	aic7770_identity *entry;
+	int	i;
+
+	for (i = 0; i < ahc_num_aic7770_devs; i++) {
+		entry = &aic7770_ident_table[i];
+		if (entry->full_id == (id & entry->id_mask))
+			return (entry);
+	}
+	return (NULL);
+}
+
+int
+aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io)
+{
+	int	error;
+	int	have_seeprom;
+	u_int	hostconf;
+	u_int   irq;
+	u_int	intdef;
+
+	error = entry->setup(ahc);
+	have_seeprom = 0;
+	if (error != 0)
+		return (error);
+
+	error = aic7770_map_registers(ahc, io);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Before we continue probing the card, ensure that
+	 * its interrupts are *disabled*.  We don't want
+	 * a misstep to hang the machine in an interrupt
+	 * storm.
+	 */
+	ahc_intr_enable(ahc, FALSE);
+
+	ahc->description = entry->name;
+	error = ahc_softc_init(ahc);
+	if (error != 0)
+		return (error);
+
+	ahc->bus_chip_init = aic7770_chip_init;
+
+	error = ahc_reset(ahc, /*reinit*/FALSE);
+	if (error != 0)
+		return (error);
+
+	/* Make sure we have a valid interrupt vector */
+	intdef = ahc_inb(ahc, INTDEF);
+	irq = intdef & VECTOR;
+	switch (irq) {
+	case 9:
+	case 10:
+	case 11:
+	case 12:
+	case 14:
+	case 15:
+		break;
+	default:
+		printk("aic7770_config: invalid irq setting %d\n", intdef);
+		return (ENXIO);
+	}
+
+	if ((intdef & EDGE_TRIG) != 0)
+		ahc->flags |= AHC_EDGE_INTERRUPT;
+
+	switch (ahc->chip & (AHC_EISA|AHC_VL)) {
+	case AHC_EISA:
+	{
+		u_int biosctrl;
+		u_int scsiconf;
+		u_int scsiconf1;
+
+		biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL);
+		scsiconf = ahc_inb(ahc, SCSICONF);
+		scsiconf1 = ahc_inb(ahc, SCSICONF + 1);
+
+		/* Get the primary channel information */
+		if ((biosctrl & CHANNEL_B_PRIMARY) != 0)
+			ahc->flags |= 1;
+
+		if ((biosctrl & BIOSMODE) == BIOSDISABLED) {
+			ahc->flags |= AHC_USEDEFAULTS;
+		} else {
+			if ((ahc->features & AHC_WIDE) != 0) {
+				ahc->our_id = scsiconf1 & HWSCSIID;
+				if (scsiconf & TERM_ENB)
+					ahc->flags |= AHC_TERM_ENB_A;
+			} else {
+				ahc->our_id = scsiconf & HSCSIID;
+				ahc->our_id_b = scsiconf1 & HSCSIID;
+				if (scsiconf & TERM_ENB)
+					ahc->flags |= AHC_TERM_ENB_A;
+				if (scsiconf1 & TERM_ENB)
+					ahc->flags |= AHC_TERM_ENB_B;
+			}
+		}
+		if ((ahc_inb(ahc, HA_274_BIOSGLOBAL) & HA_274_EXTENDED_TRANS))
+			ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
+		break;
+	}
+	case AHC_VL:
+	{
+		have_seeprom = aha2840_load_seeprom(ahc);
+		break;
+	}
+	default:
+		break;
+	}
+	if (have_seeprom == 0) {
+		kfree(ahc->seep_config);
+		ahc->seep_config = NULL;
+	}
+
+	/*
+	 * Ensure autoflush is enabled
+	 */
+	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~AUTOFLUSHDIS);
+
+	/* Setup the FIFO threshold and the bus off time */
+	hostconf = ahc_inb(ahc, HOSTCONF);
+	ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH);
+	ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF);
+
+	ahc->bus_softc.aic7770_softc.busspd = hostconf & DFTHRSH;
+	ahc->bus_softc.aic7770_softc.bustime = (hostconf << 2) & BOFF;
+
+	/*
+	 * Generic aic7xxx initialization.
+	 */
+	error = ahc_init(ahc);
+	if (error != 0)
+		return (error);
+
+	error = aic7770_map_int(ahc, irq);
+	if (error != 0)
+		return (error);
+
+	ahc->init_level++;
+
+	/*
+	 * Enable the board's BUS drivers
+	 */
+	ahc_outb(ahc, BCTL, ENABLE);
+	return (0);
+}
+
+static int
+aic7770_chip_init(struct ahc_softc *ahc)
+{
+	ahc_outb(ahc, BUSSPD, ahc->bus_softc.aic7770_softc.busspd);
+	ahc_outb(ahc, BUSTIME, ahc->bus_softc.aic7770_softc.bustime);
+	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~AUTOFLUSHDIS);
+	ahc_outb(ahc, BCTL, ENABLE);
+	return (ahc_chip_init(ahc));
+}
+
+/*
+ * Read the 284x SEEPROM.
+ */
+static int
+aha2840_load_seeprom(struct ahc_softc *ahc)
+{
+	struct	seeprom_descriptor sd;
+	struct	seeprom_config *sc;
+	int	have_seeprom;
+	uint8_t scsi_conf;
+
+	sd.sd_ahc = ahc;
+	sd.sd_control_offset = SEECTL_2840;
+	sd.sd_status_offset = STATUS_2840;
+	sd.sd_dataout_offset = STATUS_2840;		
+	sd.sd_chip = C46;
+	sd.sd_MS = 0;
+	sd.sd_RDY = EEPROM_TF;
+	sd.sd_CS = CS_2840;
+	sd.sd_CK = CK_2840;
+	sd.sd_DO = DO_2840;
+	sd.sd_DI = DI_2840;
+	sc = ahc->seep_config;
+
+	if (bootverbose)
+		printk("%s: Reading SEEPROM...", ahc_name(ahc));
+	have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)sc,
+					/*start_addr*/0, sizeof(*sc)/2);
+
+	if (have_seeprom) {
+
+		if (ahc_verify_cksum(sc) == 0) {
+			if(bootverbose)
+				printk ("checksum error\n");
+			have_seeprom = 0;
+		} else if (bootverbose) {
+			printk("done.\n");
+		}
+	}
+
+	if (!have_seeprom) {
+		if (bootverbose)
+			printk("%s: No SEEPROM available\n", ahc_name(ahc));
+		ahc->flags |= AHC_USEDEFAULTS;
+	} else {
+		/*
+		 * Put the data we've collected down into SRAM
+		 * where ahc_init will find it.
+		 */
+		int	 i;
+		int	 max_targ;
+		uint16_t discenable;
+
+		max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8;
+		discenable = 0;
+		for (i = 0; i < max_targ; i++){
+			uint8_t target_settings;
+
+			target_settings = (sc->device_flags[i] & CFXFER) << 4;
+			if (sc->device_flags[i] & CFSYNCH)
+				target_settings |= SOFS;
+			if (sc->device_flags[i] & CFWIDEB)
+				target_settings |= WIDEXFER;
+			if (sc->device_flags[i] & CFDISC)
+				discenable |= (0x01 << i);
+			ahc_outb(ahc, TARG_SCSIRATE + i, target_settings);
+		}
+		ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+		ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+
+		ahc->our_id = sc->brtime_id & CFSCSIID;
+
+		scsi_conf = (ahc->our_id & 0x7);
+		if (sc->adapter_control & CFSPARITY)
+			scsi_conf |= ENSPCHK;
+		if (sc->adapter_control & CFRESETB)
+			scsi_conf |= RESET_SCSI;
+
+		if (sc->bios_control & CF284XEXTEND)		
+			ahc->flags |= AHC_EXTENDED_TRANS_A;
+		/* Set SCSICONF info */
+		ahc_outb(ahc, SCSICONF, scsi_conf);
+
+		if (sc->adapter_control & CF284XSTERM)
+			ahc->flags |= AHC_TERM_ENB_A;
+	}
+	return (have_seeprom);
+}
+
+static int
+ahc_aic7770_VL_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7770_setup(ahc);
+	ahc->chip |= AHC_VL;
+	return (error);
+}
+
+static int
+ahc_aic7770_EISA_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7770_setup(ahc);
+	ahc->chip |= AHC_EISA;
+	return (error);
+}
+
+static int
+ahc_aic7770_setup(struct ahc_softc *ahc)
+{
+	ahc->channel = 'A';
+	ahc->channel_b = 'B';
+	ahc->chip = AHC_AIC7770;
+	ahc->features = AHC_AIC7770_FE;
+	ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
+	ahc->flags |= AHC_PAGESCBS;
+	ahc->instruction_ram_size = 448;
+	return (0);
+}
diff --git a/drivers/scsi/aic7xxx/aic7770_osm.c b/drivers/scsi/aic7xxx/aic7770_osm.c
new file mode 100644
index 0000000..3d401d0
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7770_osm.c
@@ -0,0 +1,155 @@
+/*
+ * Linux driver attachment glue for aic7770 based controllers.
+ *
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7770_osm.c#14 $
+ */
+
+#include "aic7xxx_osm.h"
+
+#include <linux/device.h>
+#include <linux/eisa.h>
+
+int
+aic7770_map_registers(struct ahc_softc *ahc, u_int port)
+{
+	/*
+	 * Lock out other contenders for our i/o space.
+	 */
+	if (!request_region(port, AHC_EISA_IOSIZE, "aic7xxx"))
+		return (ENOMEM);
+	ahc->tag = BUS_SPACE_PIO;
+	ahc->bsh.ioport = port;
+	return (0);
+}
+
+int
+aic7770_map_int(struct ahc_softc *ahc, u_int irq)
+{
+	int error;
+	int shared;
+
+	shared = 0;
+	if ((ahc->flags & AHC_EDGE_INTERRUPT) == 0)
+		shared = IRQF_SHARED;
+
+	error = request_irq(irq, ahc_linux_isr, shared, "aic7xxx", ahc);
+	if (error == 0)
+		ahc->platform_data->irq = irq;
+	
+	return (-error);
+}
+
+static int
+aic7770_probe(struct device *dev)
+{
+	struct eisa_device *edev = to_eisa_device(dev);
+	u_int eisaBase = edev->base_addr+AHC_EISA_SLOT_OFFSET;
+	struct	ahc_softc *ahc;
+	char	buf[80];
+	char   *name;
+	int	error;
+
+	sprintf(buf, "ahc_eisa:%d", eisaBase >> 12);
+	name = kstrdup(buf, GFP_ATOMIC);
+	if (name == NULL)
+		return (ENOMEM);
+	ahc = ahc_alloc(&aic7xxx_driver_template, name);
+	if (ahc == NULL)
+		return (ENOMEM);
+	error = aic7770_config(ahc, aic7770_ident_table + edev->id.driver_data,
+			       eisaBase);
+	if (error != 0) {
+		ahc->bsh.ioport = 0;
+		ahc_free(ahc);
+		return (error);
+	}
+
+ 	dev_set_drvdata(dev, ahc);
+
+	error = ahc_linux_register_host(ahc, &aic7xxx_driver_template);
+	return (error);
+}
+
+static int
+aic7770_remove(struct device *dev)
+{
+	struct ahc_softc *ahc = dev_get_drvdata(dev);
+	u_long s;
+
+	if (ahc->platform_data && ahc->platform_data->host)
+			scsi_remove_host(ahc->platform_data->host);
+
+	ahc_lock(ahc, &s);
+	ahc_intr_enable(ahc, FALSE);
+	ahc_unlock(ahc, &s);
+
+	ahc_free(ahc);
+	return 0;
+}
+ 
+static struct eisa_device_id aic7770_ids[] = {
+	{ "ADP7771", 0 }, /* AHA 274x */
+	{ "ADP7756", 1 }, /* AHA 284x BIOS enabled */
+	{ "ADP7757", 2 }, /* AHA 284x BIOS disabled */
+	{ "ADP7782", 3 }, /* AHA 274x Olivetti OEM */
+	{ "ADP7783", 4 }, /* AHA 274x Olivetti OEM (Differential) */
+	{ "ADP7770", 5 }, /* AIC7770 generic */
+	{ "" }
+};
+MODULE_DEVICE_TABLE(eisa, aic7770_ids);
+
+static struct eisa_driver aic7770_driver = {
+	.id_table	= aic7770_ids,
+	.driver = {
+		.name   = "aic7xxx",
+		.probe  = aic7770_probe,
+		.remove = aic7770_remove,
+	}
+};
+  
+int
+ahc_linux_eisa_init(void)
+{
+	return eisa_driver_register(&aic7770_driver);
+}
+  
+void
+ahc_linux_eisa_exit(void)
+{
+	eisa_driver_unregister(&aic7770_driver);
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx.h b/drivers/scsi/aic7xxx/aic79xx.h
new file mode 100644
index 0000000..df2e0e5
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.h
@@ -0,0 +1,1478 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2002 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#109 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC79XX_H_
+#define _AIC79XX_H_
+
+/* Register Definitions */
+#include "aic79xx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahd_platform_data;
+struct scb_platform_data;
+
+/****************************** Useful Macros *********************************/
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD	(~0)
+#define	SCB_LIST_NULL		0xFF00
+#define	SCB_LIST_NULL_LE	(ahd_htole16(SCB_LIST_NULL))
+#define QOUTFIFO_ENTRY_VALID 0x80
+#define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL)
+
+#define SCSIID_TARGET(ahd, scsiid)	\
+	(((scsiid) & TID) >> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid)		\
+	((scsiid) & OID)
+#define SCSIID_CHANNEL(ahd, scsiid) ('A')
+#define	SCB_IS_SCSIBUS_B(ahd, scb) (0)
+#define	SCB_GET_OUR_ID(scb) \
+	SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define	SCB_GET_TARGET(ahd, scb) \
+	SCSIID_TARGET((ahd), (scb)->hscb->scsiid)
+#define	SCB_GET_CHANNEL(ahd, scb) \
+	SCSIID_CHANNEL(ahd, (scb)->hscb->scsiid)
+#define	SCB_GET_LUN(scb) \
+	((scb)->hscb->lun)
+#define SCB_GET_TARGET_OFFSET(ahd, scb)	\
+	SCB_GET_TARGET(ahd, scb)
+#define SCB_GET_TARGET_MASK(ahd, scb) \
+	(0x01 << (SCB_GET_TARGET_OFFSET(ahd, scb)))
+#ifdef AHD_DEBUG
+#define SCB_IS_SILENT(scb)					\
+	((ahd_debug & AHD_SHOW_MASKED_ERRORS) == 0		\
+      && (((scb)->flags & SCB_SILENT) != 0))
+#else
+#define SCB_IS_SILENT(scb)					\
+	(((scb)->flags & SCB_SILENT) != 0)
+#endif
+/*
+ * TCLs have the following format: TTTTLLLLLLLL
+ */
+#define TCL_TARGET_OFFSET(tcl) \
+	((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+	(tcl & (AHD_NUM_LUNS - 1))
+#define BUILD_TCL(scsiid, lun) \
+	((lun) | (((scsiid) & TID) << 4))
+#define BUILD_TCL_RAW(target, channel, lun) \
+	((lun) | ((target) << 8))
+
+#define SCB_GET_TAG(scb) \
+	ahd_le16toh(scb->hscb->tag)
+
+#ifndef	AHD_TARGET_MODE
+#undef	AHD_TMODE_ENABLE
+#define	AHD_TMODE_ENABLE 0
+#endif
+
+#define AHD_BUILD_COL_IDX(target, lun)				\
+	((((u8)lun) << 4) | target)
+
+#define AHD_GET_SCB_COL_IDX(ahd, scb)				\
+	((SCB_GET_LUN(scb) << 4) | SCB_GET_TARGET(ahd, scb))
+
+#define AHD_SET_SCB_COL_IDX(scb, col_idx)				\
+do {									\
+	(scb)->hscb->scsiid = ((col_idx) << TID_SHIFT) & TID;		\
+	(scb)->hscb->lun = ((col_idx) >> 4) & (AHD_NUM_LUNS_NONPKT-1);	\
+} while (0)
+
+#define AHD_COPY_SCB_COL_IDX(dst, src)				\
+do {								\
+	dst->hscb->scsiid = src->hscb->scsiid;			\
+	dst->hscb->lun = src->hscb->lun;			\
+} while (0)
+
+#define	AHD_NEVER_COL_IDX 0xFFFF
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHD_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in non-packetized transfers.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but until we see a need to support that many, we support 256.
+ */
+#define AHD_NUM_LUNS_NONPKT 64
+#define AHD_NUM_LUNS 256
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHD_MAXTRANSFER_SIZE	 0x00ffffff	/* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound.  Due to software design,
+ * we may not be able to use this number.
+ */
+#define AHD_SCB_MAX	512
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information.
+ */
+#define AHD_MAX_QUEUE	AHD_SCB_MAX
+
+/*
+ * Define the size of our QIN and QOUT FIFOs.  They must be a power of 2
+ * in size and accommodate as many transactions as can be queued concurrently.
+ */
+#define	AHD_QIN_SIZE	AHD_MAX_QUEUE
+#define	AHD_QOUT_SIZE	AHD_MAX_QUEUE
+
+#define AHD_QIN_WRAP(x) ((x) & (AHD_QIN_SIZE-1))
+/*
+ * The maximum amount of SCB storage we allocate in host memory.
+ */
+#define AHD_SCB_MAX_ALLOC AHD_MAX_QUEUE
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHD_TMODE_CMDS	256
+
+/* Reset line assertion time in us */
+#define AHD_BUSRESET_DELAY	25
+
+/******************* Chip Characteristics/Operating Settings  *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+	AHD_NONE	= 0x0000,
+	AHD_CHIPID_MASK	= 0x00FF,
+	AHD_AIC7901	= 0x0001,
+	AHD_AIC7902	= 0x0002,
+	AHD_AIC7901A	= 0x0003,
+	AHD_PCI		= 0x0100,	/* Bus type PCI */
+	AHD_PCIX	= 0x0200,	/* Bus type PCIX */
+	AHD_BUS_MASK	= 0x0F00
+} ahd_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+	AHD_FENONE		= 0x00000,
+	AHD_WIDE  		= 0x00001,/* Wide Channel */
+	AHD_AIC79XXB_SLOWCRC    = 0x00002,/* SLOWCRC bit should be set */
+	AHD_MULTI_FUNC		= 0x00100,/* Multi-Function/Channel Device */
+	AHD_TARGETMODE		= 0x01000,/* Has tested target mode support */
+	AHD_MULTIROLE		= 0x02000,/* Space for two roles at a time */
+	AHD_RTI			= 0x04000,/* Retained Training Support */
+	AHD_NEW_IOCELL_OPTS	= 0x08000,/* More Signal knobs in the IOCELL */
+	AHD_NEW_DFCNTRL_OPTS	= 0x10000,/* SCSIENWRDIS bit */
+	AHD_FAST_CDB_DELIVERY	= 0x20000,/* CDB acks released to Output Sync */
+	AHD_REMOVABLE		= 0x00000,/* Hot-Swap supported - None so far*/
+	AHD_AIC7901_FE		= AHD_FENONE,
+	AHD_AIC7901A_FE		= AHD_FENONE,
+	AHD_AIC7902_FE		= AHD_MULTI_FUNC
+} ahd_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+	AHD_BUGNONE		= 0x0000,
+	/*
+	 * Rev A hardware fails to update LAST/CURR/NEXTSCB
+	 * correctly in certain packetized selection cases.
+	 */
+	AHD_SENT_SCB_UPDATE_BUG	= 0x0001,
+	/* The wrong SCB is accessed to check the abort pending bit. */
+	AHD_ABORT_LQI_BUG	= 0x0002,
+	/* Packetized bitbucket crosses packet boundaries. */
+	AHD_PKT_BITBUCKET_BUG	= 0x0004,
+	/* The selection timer runs twice as long as its setting. */
+	AHD_LONG_SETIMO_BUG	= 0x0008,
+	/* The Non-LQ CRC error status is delayed until phase change. */
+	AHD_NLQICRC_DELAYED_BUG	= 0x0010,
+	/* The chip must be reset for all outgoing bus resets.  */
+	AHD_SCSIRST_BUG		= 0x0020,
+	/* Some PCIX fields must be saved and restored across chip reset. */
+	AHD_PCIX_CHIPRST_BUG	= 0x0040,
+	/* MMAPIO is not functional in PCI-X mode.  */
+	AHD_PCIX_MMAPIO_BUG	= 0x0080,
+	/* Reads to SCBRAM fail to reset the discard timer. */
+	AHD_PCIX_SCBRAM_RD_BUG  = 0x0100,
+	/* Bug workarounds that can be disabled on non-PCIX busses. */
+	AHD_PCIX_BUG_MASK	= AHD_PCIX_CHIPRST_BUG
+				| AHD_PCIX_MMAPIO_BUG
+				| AHD_PCIX_SCBRAM_RD_BUG,
+	/*
+	 * LQOSTOP0 status set even for forced selections with ATN
+	 * to perform non-packetized message delivery.
+	 */
+	AHD_LQO_ATNO_BUG	= 0x0200,
+	/* FIFO auto-flush does not always trigger.  */
+	AHD_AUTOFLUSH_BUG	= 0x0400,
+	/* The CLRLQO registers are not self-clearing. */
+	AHD_CLRLQO_AUTOCLR_BUG	= 0x0800,
+	/* The PACKETIZED status bit refers to the previous connection. */
+	AHD_PKTIZED_STATUS_BUG  = 0x1000,
+	/* "Short Luns" are not placed into outgoing LQ packets correctly. */
+	AHD_PKT_LUN_BUG		= 0x2000,
+	/*
+	 * Only the FIFO allocated to the non-packetized connection may
+	 * be in use during a non-packetzied connection.
+	 */
+	AHD_NONPACKFIFO_BUG	= 0x4000,
+	/*
+	 * Writing to a DFF SCBPTR register may fail if concurent with
+	 * a hardware write to the other DFF SCBPTR register.  This is
+	 * not currently a concern in our sequencer since all chips with
+	 * this bug have the AHD_NONPACKFIFO_BUG and all writes of concern
+	 * occur in non-packetized connections.
+	 */
+	AHD_MDFF_WSCBPTR_BUG	= 0x8000,
+	/* SGHADDR updates are slow. */
+	AHD_REG_SLOW_SETTLE_BUG	= 0x10000,
+	/*
+	 * Changing the MODE_PTR coincident with an interrupt that
+	 * switches to a different mode will cause the interrupt to
+	 * be in the mode written outside of interrupt context.
+	 */
+	AHD_SET_MODE_BUG	= 0x20000,
+	/* Non-packetized busfree revision does not work. */
+	AHD_BUSFREEREV_BUG	= 0x40000,
+	/*
+	 * Paced transfers are indicated with a non-standard PPR
+	 * option bit in the neg table, 160MHz is indicated by
+	 * sync factor 0x7, and the offset if off by a factor of 2.
+	 */
+	AHD_PACED_NEGTABLE_BUG	= 0x80000,
+	/* LQOOVERRUN false positives. */
+	AHD_LQOOVERRUN_BUG	= 0x100000,
+	/*
+	 * Controller write to INTSTAT will lose to a host
+	 * write to CLRINT.
+	 */
+	AHD_INTCOLLISION_BUG	= 0x200000,
+	/*
+	 * The GEM318 violates the SCSI spec by not waiting
+	 * the mandated bus settle delay between phase changes
+	 * in some situations.  Some aic79xx chip revs. are more
+	 * strict in this regard and will treat REQ assertions
+	 * that fall within the bus settle delay window as
+	 * glitches.  This flag tells the firmware to tolerate
+	 * early REQ assertions.
+	 */
+	AHD_EARLY_REQ_BUG	= 0x400000,
+	/*
+	 * The LED does not stay on long enough in packetized modes.
+	 */
+	AHD_FAINT_LED_BUG	= 0x800000
+} ahd_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+	AHD_FNONE	      = 0x00000,
+	AHD_BOOT_CHANNEL      = 0x00001,/* We were set as the boot channel. */
+	AHD_USEDEFAULTS	      = 0x00004,/*
+					 * For cards without an seeprom
+					 * or a BIOS to initialize the chip's
+					 * SRAM, we use the default target
+					 * settings.
+					 */
+	AHD_SEQUENCER_DEBUG   = 0x00008,
+	AHD_RESET_BUS_A	      = 0x00010,
+	AHD_EXTENDED_TRANS_A  = 0x00020,
+	AHD_TERM_ENB_A	      = 0x00040,
+	AHD_SPCHK_ENB_A	      = 0x00080,
+	AHD_STPWLEVEL_A	      = 0x00100,
+	AHD_INITIATORROLE     = 0x00200,/*
+					 * Allow initiator operations on
+					 * this controller.
+					 */
+	AHD_TARGETROLE	      = 0x00400,/*
+					 * Allow target operations on this
+					 * controller.
+					 */
+	AHD_RESOURCE_SHORTAGE = 0x00800,
+	AHD_TQINFIFO_BLOCKED  = 0x01000,/* Blocked waiting for ATIOs */
+	AHD_INT50_SPEEDFLEX   = 0x02000,/*
+					 * Internal 50pin connector
+					 * sits behind an aic3860
+					 */
+	AHD_BIOS_ENABLED      = 0x04000,
+	AHD_ALL_INTERRUPTS    = 0x08000,
+	AHD_39BIT_ADDRESSING  = 0x10000,/* Use 39 bit addressing scheme. */
+	AHD_64BIT_ADDRESSING  = 0x20000,/* Use 64 bit addressing scheme. */
+	AHD_CURRENT_SENSING   = 0x40000,
+	AHD_SCB_CONFIG_USED   = 0x80000,/* No SEEPROM but SCB had info. */
+	AHD_HP_BOARD	      = 0x100000,
+	AHD_BUS_RESET_ACTIVE  = 0x200000,
+	AHD_UPDATE_PEND_CMDS  = 0x400000,
+	AHD_RUNNING_QOUTFIFO  = 0x800000,
+	AHD_HAD_FIRST_SEL     = 0x1000000
+} ahd_flag;
+
+/************************* Hardware  SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory.  The SCB
+ * consists of a "hardware SCB" mirroring the fields available on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the  cdb) to be executed.  After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role.  See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target.  The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally.
+ */
+struct initiator_status {
+	uint32_t residual_datacnt;	/* Residual in the current S/G seg */
+	uint32_t residual_sgptr;	/* The next S/G for this transfer */
+	uint8_t	 scsi_status;		/* Standard SCSI status byte */
+};
+
+struct target_status {
+	uint32_t residual_datacnt;	/* Residual in the current S/G seg */
+	uint32_t residual_sgptr;	/* The next S/G for this transfer */
+	uint8_t  scsi_status;		/* SCSI status to give to initiator */
+	uint8_t  target_phases;		/* Bitmap of phases to execute */
+	uint8_t  data_phase;		/* Data-In or Data-Out */
+	uint8_t  initiator_tag;		/* Initiator's transaction tag */
+};
+
+/*
+ * Initiator mode SCB shared data area.
+ * If the embedded CDB is 12 bytes or less, we embed
+ * the sense buffer address in the SCB.  This allows
+ * us to retrieve sense information without interrupting
+ * the host in packetized mode.
+ */
+typedef uint32_t sense_addr_t;
+#define MAX_CDB_LEN 16
+#define MAX_CDB_LEN_WITH_SENSE_ADDR (MAX_CDB_LEN - sizeof(sense_addr_t))
+union initiator_data {
+	struct {
+		uint64_t cdbptr;
+		uint8_t  cdblen;
+	} cdb_from_host;
+	uint8_t	 cdb[MAX_CDB_LEN];
+	struct {
+		uint8_t	 cdb[MAX_CDB_LEN_WITH_SENSE_ADDR];
+		sense_addr_t sense_addr;
+	} cdb_plus_saddr;
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+	uint32_t spare[2];	
+	uint8_t  scsi_status;		/* SCSI status to give to initiator */
+	uint8_t  target_phases;		/* Bitmap of phases to execute */
+	uint8_t  data_phase;		/* Data-In or Data-Out */
+	uint8_t  initiator_tag;		/* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/	union {
+		union	initiator_data idata;
+		struct	target_data tdata;
+		struct	initiator_status istatus;
+		struct	target_status tstatus;
+	} shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer.  The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag.  Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields).  If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur.  Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields.  After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced.  After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set.  If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete.  If the
+ * sequencer determines that there is a residual in the tranfer, or
+ * there is non-zero status, it will set the SG_STATUS_VALID flag in
+ * sgptr and dma the scb back into host memory.  To sumarize:
+ *
+ * Sequencer:
+ *	o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ *	  or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ *	o We are transferring the last segment if residual_datacnt has
+ *	  the SG_LAST_SEG flag set.
+ *
+ * Host:
+ *	o A residual can only have occurred if a completed scb has the
+ *	  SG_STATUS_VALID flag set.  Inspection of the SCSI status field,
+ *	  the residual_datacnt, and the residual_sgptr field will tell
+ *	  for sure.
+ *
+ *	o residual_sgptr and sgptr refer to the "next" sg entry
+ *	  and so may point beyond the last valid sg entry for the
+ *	  transfer.
+ */ 
+#define SG_PTR_MASK	0xFFFFFFF8
+/*16*/	uint16_t tag;		/* Reused by Sequencer. */
+/*18*/	uint8_t  control;	/* See SCB_CONTROL in aic79xx.reg for details */
+/*19*/	uint8_t	 scsiid;	/*
+				 * Selection out Id
+				 * Our Id (bits 0-3) Their ID (bits 4-7)
+				 */
+/*20*/	uint8_t  lun;
+/*21*/	uint8_t  task_attribute;
+/*22*/	uint8_t  cdb_len;
+/*23*/	uint8_t  task_management;
+/*24*/	uint64_t dataptr;
+/*32*/	uint32_t datacnt;	/* Byte 3 is spare. */
+/*36*/	uint32_t sgptr;
+/*40*/	uint32_t hscb_busaddr;
+/*44*/	uint32_t next_hscb_busaddr;
+/********** Long lun field only downloaded for full 8 byte lun support ********/
+/*48*/  uint8_t	 pkt_long_lun[8];
+/******* Fields below are not Downloaded (Sequencer may use for scratch) ******/
+/*56*/  uint8_t	 spare[8];
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent.  Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transferred to the controller.
+ * The aic7xxx chips only support a 24bit length.  We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer.  This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahd_dma_seg {
+	uint32_t	addr;
+	uint32_t	len;
+#define	AHD_DMA_LAST_SEG	0x80000000
+#define	AHD_SG_HIGH_ADDR_MASK	0x7F000000
+#define	AHD_SG_LEN_MASK		0x00FFFFFF
+};
+
+struct ahd_dma64_seg {
+	uint64_t	addr;
+	uint32_t	len;
+	uint32_t	pad;
+};
+
+struct map_node {
+	bus_dmamap_t		 dmamap;
+	dma_addr_t		 physaddr;
+	uint8_t			*vaddr;
+	SLIST_ENTRY(map_node)	 links;
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+	SCB_FLAG_NONE		= 0x00000,
+	SCB_TRANSMISSION_ERROR	= 0x00001,/*
+					   * We detected a parity or CRC
+					   * error that has effected the
+					   * payload of the command.  This
+					   * flag is checked when normal
+					   * status is returned to catch
+					   * the case of a target not
+					   * responding to our attempt
+					   * to report the error.
+					   */
+	SCB_OTHERTCL_TIMEOUT	= 0x00002,/*
+					   * Another device was active
+					   * during the first timeout for
+					   * this SCB so we gave ourselves
+					   * an additional timeout period
+					   * in case it was hogging the
+					   * bus.
+				           */
+	SCB_DEVICE_RESET	= 0x00004,
+	SCB_SENSE		= 0x00008,
+	SCB_CDB32_PTR		= 0x00010,
+	SCB_RECOVERY_SCB	= 0x00020,
+	SCB_AUTO_NEGOTIATE	= 0x00040,/* Negotiate to achieve goal. */
+	SCB_NEGOTIATE		= 0x00080,/* Negotiation forced for command. */
+	SCB_ABORT		= 0x00100,
+	SCB_ACTIVE		= 0x00200,
+	SCB_TARGET_IMMEDIATE	= 0x00400,
+	SCB_PACKETIZED		= 0x00800,
+	SCB_EXPECT_PPR_BUSFREE	= 0x01000,
+	SCB_PKT_SENSE		= 0x02000,
+	SCB_EXTERNAL_RESET	= 0x04000,/* Device was reset externally */
+	SCB_ON_COL_LIST		= 0x08000,
+	SCB_SILENT		= 0x10000 /*
+					   * Be quiet about transmission type
+					   * errors.  They are expected and we
+					   * don't want to upset the user.  This
+					   * flag is typically used during DV.
+					   */
+} scb_flag;
+
+struct scb {
+	struct	hardware_scb	 *hscb;
+	union {
+		SLIST_ENTRY(scb)  sle;
+		LIST_ENTRY(scb)	  le;
+		TAILQ_ENTRY(scb)  tqe;
+	} links;
+	union {
+		SLIST_ENTRY(scb)  sle;
+		LIST_ENTRY(scb)	  le;
+		TAILQ_ENTRY(scb)  tqe;
+	} links2;
+#define pending_links links2.le
+#define collision_links links2.le
+	struct scb		 *col_scb;
+	ahd_io_ctx_t		  io_ctx;
+	struct ahd_softc	 *ahd_softc;
+	scb_flag		  flags;
+#ifndef __linux__
+	bus_dmamap_t		  dmamap;
+#endif
+	struct scb_platform_data *platform_data;
+	struct map_node	 	 *hscb_map;
+	struct map_node	 	 *sg_map;
+	struct map_node	 	 *sense_map;
+	void			 *sg_list;
+	uint8_t			 *sense_data;
+	dma_addr_t		  sg_list_busaddr;
+	dma_addr_t		  sense_busaddr;
+	u_int			  sg_count;/* How full ahd_dma_seg is */
+#define	AHD_MAX_LQ_CRC_ERRORS 5
+	u_int			  crc_retry_count;
+};
+
+TAILQ_HEAD(scb_tailq, scb);
+LIST_HEAD(scb_list, scb);
+
+struct scb_data {
+	/*
+	 * TAILQ of lists of free SCBs grouped by device
+	 * collision domains.
+	 */
+	struct scb_tailq free_scbs;
+
+	/*
+	 * Per-device lists of SCBs whose tag ID would collide
+	 * with an already active tag on the device.
+	 */
+	struct scb_list free_scb_lists[AHD_NUM_TARGETS * AHD_NUM_LUNS_NONPKT];
+
+	/*
+	 * SCBs that will not collide with any active device.
+	 */
+	struct scb_list any_dev_free_scb_list;
+
+	/*
+	 * Mapping from tag to SCB.
+	 */
+	struct	scb *scbindex[AHD_SCB_MAX];
+
+	/*
+	 * "Bus" addresses of our data structures.
+	 */
+	bus_dma_tag_t	 hscb_dmat;	/* dmat for our hardware SCB array */
+	bus_dma_tag_t	 sg_dmat;	/* dmat for our sg segments */
+	bus_dma_tag_t	 sense_dmat;	/* dmat for our sense buffers */
+	SLIST_HEAD(, map_node) hscb_maps;
+	SLIST_HEAD(, map_node) sg_maps;
+	SLIST_HEAD(, map_node) sense_maps;
+	int		 scbs_left;	/* unallocated scbs in head map_node */
+	int		 sgs_left;	/* unallocated sgs in head map_node */
+	int		 sense_left;	/* unallocated sense in head map_node */
+	uint16_t	 numscbs;
+	uint16_t	 maxhscbs;	/* Number of SCBs on the card */
+	uint8_t		 init_level;	/*
+					 * How far we've initialized
+					 * this structure.
+					 */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection descriptor for select-in requests in target mode.
+ */
+struct target_cmd {
+	uint8_t scsiid;		/* Our ID and the initiator's ID */
+	uint8_t identify;	/* Identify message */
+	uint8_t bytes[22];	/* 
+				 * Bytes contains any additional message
+				 * bytes terminated by 0xFF.  The remainder
+				 * is the cdb to execute.
+				 */
+	uint8_t cmd_valid;	/*
+				 * When a command is complete, the firmware
+				 * will set cmd_valid to all bits set.
+				 * After the host has seen the command,
+				 * the bits are cleared.  This allows us
+				 * to just peek at host memory to determine
+				 * if more work is complete. cmd_valid is on
+				 * an 8 byte boundary to simplify setting
+				 * it on aic7880 hardware which only has
+				 * limited direct access to the DMA FIFO.
+				 */
+	uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHD_TMODE_EVENT_BUFFER_SIZE 8
+struct ahd_tmode_event {
+	uint8_t initiator_id;
+	uint8_t event_type;	/* MSG type or EVENT_TYPE_BUS_RESET */
+#define	EVENT_TYPE_BUS_RESET 0xFF
+	uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it.  Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHD_TARGET_MODE 
+struct ahd_tmode_lstate {
+	struct cam_path *path;
+	struct ccb_hdr_slist accept_tios;
+	struct ccb_hdr_slist immed_notifies;
+	struct ahd_tmode_event event_buffer[AHD_TMODE_EVENT_BUFFER_SIZE];
+	uint8_t event_r_idx;
+	uint8_t event_w_idx;
+};
+#else
+struct ahd_tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHD_TRANS_CUR		0x01	/* Modify current neogtiation status */
+#define AHD_TRANS_ACTIVE	0x03	/* Assume this target is on the bus */
+#define AHD_TRANS_GOAL		0x04	/* Modify negotiation goal */
+#define AHD_TRANS_USER		0x08	/* Modify user negotiation settings */
+#define AHD_PERIOD_10MHz	0x19
+
+#define AHD_WIDTH_UNKNOWN	0xFF
+#define AHD_PERIOD_UNKNOWN	0xFF
+#define AHD_OFFSET_UNKNOWN	0xFF
+#define AHD_PPR_OPTS_UNKNOWN	0xFF
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahd_transinfo {
+	uint8_t protocol_version;	/* SCSI Revision level */
+	uint8_t transport_version;	/* SPI Revision level */
+	uint8_t width;			/* Bus width */
+	uint8_t period;			/* Sync rate factor */
+	uint8_t offset;			/* Sync offset */
+	uint8_t ppr_options;		/* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahd_initiator_tinfo {
+	struct ahd_transinfo curr;
+	struct ahd_transinfo goal;
+	struct ahd_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping.  For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct ahd_tmode_tstate {
+	struct ahd_tmode_lstate*	enabled_luns[AHD_NUM_LUNS];
+	struct ahd_initiator_tinfo	transinfo[AHD_NUM_TARGETS];
+
+	/*
+	 * Per initiator state bitmasks.
+	 */
+	uint16_t	 auto_negotiate;/* Auto Negotiation Required */
+	uint16_t	 discenable;	/* Disconnection allowed  */
+	uint16_t	 tagenable;	/* Tagged Queuing allowed */
+};
+
+/*
+ * Points of interest along the negotiated transfer scale.
+ */
+#define AHD_SYNCRATE_160	0x8
+#define AHD_SYNCRATE_PACED	0x8
+#define AHD_SYNCRATE_DT		0x9
+#define AHD_SYNCRATE_ULTRA2	0xa
+#define AHD_SYNCRATE_ULTRA	0xc
+#define AHD_SYNCRATE_FAST	0x19
+#define AHD_SYNCRATE_MIN_DT	AHD_SYNCRATE_FAST
+#define AHD_SYNCRATE_SYNC	0x32
+#define AHD_SYNCRATE_MIN	0x60
+#define	AHD_SYNCRATE_ASYNC	0xFF
+#define AHD_SYNCRATE_MAX	AHD_SYNCRATE_160
+
+/* Safe and valid period for async negotiations. */
+#define	AHD_ASYNC_XFER_PERIOD	0x44
+
+/*
+ * In RevA, the synctable uses a 120MHz rate for the period
+ * factor 8 and 160MHz for the period factor 7.  The 120MHz
+ * rate never made it into the official SCSI spec, so we must
+ * compensate when setting the negotiation table for Rev A
+ * parts.
+ */
+#define AHD_SYNCRATE_REVA_120	0x8
+#define AHD_SYNCRATE_REVA_160	0x7
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table. 
+ */
+struct ahd_phase_table_entry {
+        uint8_t phase;
+        uint8_t mesg_out; /* Message response to parity errors */
+	const char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+	uint16_t device_flags[16];	/* words 0-15 */
+#define		CFXFER		0x003F	/* synchronous transfer rate */
+#define			CFXFER_ASYNC	0x3F
+#define		CFQAS		0x0040	/* Negotiate QAS */
+#define		CFPACKETIZED	0x0080	/* Negotiate Packetized Transfers */
+#define		CFSTART		0x0100	/* send start unit SCSI command */
+#define		CFINCBIOS	0x0200	/* include in BIOS scan */
+#define		CFDISC		0x0400	/* enable disconnection */
+#define		CFMULTILUNDEV	0x0800	/* Probe multiple luns in BIOS scan */
+#define		CFWIDEB		0x1000	/* wide bus device */
+#define		CFHOSTMANAGED	0x8000	/* Managed by a RAID controller */
+
+/*
+ * BIOS Control Bits
+ */
+	uint16_t bios_control;		/* word 16 */
+#define		CFSUPREM	0x0001	/* support all removeable drives */
+#define		CFSUPREMB	0x0002	/* support removeable boot drives */
+#define		CFBIOSSTATE	0x000C	/* BIOS Action State */
+#define		    CFBS_DISABLED	0x00
+#define		    CFBS_ENABLED	0x04
+#define		    CFBS_DISABLED_SCAN	0x08
+#define		CFENABLEDV	0x0010	/* Perform Domain Validation */
+#define		CFCTRL_A	0x0020	/* BIOS displays Ctrl-A message */	
+#define		CFSPARITY	0x0040	/* SCSI parity */
+#define		CFEXTEND	0x0080	/* extended translation enabled */
+#define		CFBOOTCD	0x0100  /* Support Bootable CD-ROM */
+#define		CFMSG_LEVEL	0x0600	/* BIOS Message Level */
+#define			CFMSG_VERBOSE	0x0000
+#define			CFMSG_SILENT	0x0200
+#define			CFMSG_DIAG	0x0400
+#define		CFRESETB	0x0800	/* reset SCSI bus at boot */
+/*		UNUSED		0xf000	*/
+
+/*
+ * Host Adapter Control Bits
+ */
+	uint16_t adapter_control;	/* word 17 */	
+#define		CFAUTOTERM	0x0001	/* Perform Auto termination */
+#define		CFSTERM		0x0002	/* SCSI low byte termination */
+#define		CFWSTERM	0x0004	/* SCSI high byte termination */
+#define		CFSEAUTOTERM	0x0008	/* Ultra2 Perform secondary Auto Term*/
+#define		CFSELOWTERM	0x0010	/* Ultra2 secondary low term */
+#define		CFSEHIGHTERM	0x0020	/* Ultra2 secondary high term */
+#define		CFSTPWLEVEL	0x0040	/* Termination level control */
+#define		CFBIOSAUTOTERM	0x0080	/* Perform Auto termination */
+#define		CFTERM_MENU	0x0100	/* BIOS displays termination menu */	
+#define		CFCLUSTERENB	0x8000	/* Cluster Enable */
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+	uint16_t brtime_id;		/* word 18 */
+#define		CFSCSIID	0x000f	/* host adapter SCSI ID */
+/*		UNUSED		0x00f0	*/
+#define		CFBRTIME	0xff00	/* bus release time/PCI Latency Time */
+
+/*
+ * Maximum targets
+ */
+	uint16_t max_targets;		/* word 19 */	
+#define		CFMAXTARG	0x00ff	/* maximum targets */
+#define		CFBOOTLUN	0x0f00	/* Lun to boot from */
+#define		CFBOOTID	0xf000	/* Target to boot from */
+	uint16_t res_1[10];		/* words 20-29 */
+	uint16_t signature;		/* BIOS Signature */
+#define		CFSIGNATURE	0x400
+	uint16_t checksum;		/* word 31 */
+};
+
+/*
+ * Vital Product Data used during POST and by the BIOS.
+ */
+struct vpd_config {
+	uint8_t  bios_flags;
+#define		VPDMASTERBIOS	0x0001
+#define		VPDBOOTHOST	0x0002
+	uint8_t  reserved_1[21];
+	uint8_t  resource_type;
+	uint8_t  resource_len[2];
+	uint8_t  resource_data[8];
+	uint8_t  vpd_tag;
+	uint16_t vpd_len;
+	uint8_t  vpd_keyword[2];
+	uint8_t  length;
+	uint8_t  revision;
+	uint8_t  device_flags;
+	uint8_t  termination_menus[2];
+	uint8_t  fifo_threshold;
+	uint8_t  end_tag;
+	uint8_t  vpd_checksum;
+	uint16_t default_target_flags;
+	uint16_t default_bios_flags;
+	uint16_t default_ctrl_flags;
+	uint8_t  default_irq;
+	uint8_t  pci_lattime;
+	uint8_t  max_target;
+	uint8_t  boot_lun;
+	uint16_t signature;
+	uint8_t  reserved_2;
+	uint8_t  checksum;
+	uint8_t	 reserved_3[4];
+};
+
+/****************************** Flexport Logic ********************************/
+#define FLXADDR_TERMCTL			0x0
+#define		FLX_TERMCTL_ENSECHIGH	0x8
+#define		FLX_TERMCTL_ENSECLOW	0x4
+#define		FLX_TERMCTL_ENPRIHIGH	0x2
+#define		FLX_TERMCTL_ENPRILOW	0x1
+#define FLXADDR_ROMSTAT_CURSENSECTL	0x1
+#define		FLX_ROMSTAT_SEECFG	0xF0
+#define		FLX_ROMSTAT_EECFG	0x0F
+#define		FLX_ROMSTAT_SEE_93C66	0x00
+#define		FLX_ROMSTAT_SEE_NONE	0xF0
+#define		FLX_ROMSTAT_EE_512x8	0x0
+#define		FLX_ROMSTAT_EE_1MBx8	0x1
+#define		FLX_ROMSTAT_EE_2MBx8	0x2
+#define		FLX_ROMSTAT_EE_4MBx8	0x3
+#define		FLX_ROMSTAT_EE_16MBx8	0x4
+#define 		CURSENSE_ENB	0x1
+#define	FLXADDR_FLEXSTAT		0x2
+#define		FLX_FSTAT_BUSY		0x1
+#define FLXADDR_CURRENT_STAT		0x4
+#define		FLX_CSTAT_SEC_HIGH	0xC0
+#define		FLX_CSTAT_SEC_LOW	0x30
+#define		FLX_CSTAT_PRI_HIGH	0x0C
+#define		FLX_CSTAT_PRI_LOW	0x03
+#define		FLX_CSTAT_MASK		0x03
+#define		FLX_CSTAT_SHIFT		2
+#define		FLX_CSTAT_OKAY		0x0
+#define		FLX_CSTAT_OVER		0x1
+#define		FLX_CSTAT_UNDER		0x2
+#define		FLX_CSTAT_INVALID	0x3
+
+int		ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+				 u_int start_addr, u_int count, int bstream);
+
+int		ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+				  u_int start_addr, u_int count);
+int		ahd_verify_cksum(struct seeprom_config *sc);
+int		ahd_acquire_seeprom(struct ahd_softc *ahd);
+void		ahd_release_seeprom(struct ahd_softc *ahd);
+
+/****************************  Message Buffer *********************************/
+typedef enum {
+	MSG_FLAG_NONE			= 0x00,
+	MSG_FLAG_EXPECT_PPR_BUSFREE	= 0x01,
+	MSG_FLAG_IU_REQ_CHANGED		= 0x02,
+	MSG_FLAG_EXPECT_IDE_BUSFREE	= 0x04,
+	MSG_FLAG_EXPECT_QASREJ_BUSFREE	= 0x08,
+	MSG_FLAG_PACKETIZED		= 0x10
+} ahd_msg_flags;
+
+typedef enum {
+	MSG_TYPE_NONE			= 0x00,
+	MSG_TYPE_INITIATOR_MSGOUT	= 0x01,
+	MSG_TYPE_INITIATOR_MSGIN	= 0x02,
+	MSG_TYPE_TARGET_MSGOUT		= 0x03,
+	MSG_TYPE_TARGET_MSGIN		= 0x04
+} ahd_msg_type;
+
+typedef enum {
+	MSGLOOP_IN_PROG,
+	MSGLOOP_MSGCOMPLETE,
+	MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+struct ahd_suspend_channel_state {
+	uint8_t	scsiseq;
+	uint8_t	sxfrctl0;
+	uint8_t	sxfrctl1;
+	uint8_t	simode0;
+	uint8_t	simode1;
+	uint8_t	seltimer;
+	uint8_t	seqctl;
+};
+
+struct ahd_suspend_pci_state {
+	uint32_t  devconfig;
+	uint8_t   command;
+	uint8_t   csize_lattime;
+};
+
+struct ahd_suspend_state {
+	struct	ahd_suspend_channel_state channel[2];
+	struct  ahd_suspend_pci_state pci_state;
+	uint8_t	optionmode;
+	uint8_t	dscommand0;
+	uint8_t	dspcistatus;
+	/* hsmailbox */
+	uint8_t	crccontrol1;
+	uint8_t	scbbaddr;
+	/* Host and sequencer SCB counts */
+	uint8_t	dff_thrsh;
+	uint8_t	*scratch_ram;
+	uint8_t	*btt;
+};
+
+typedef void (*ahd_bus_intr_t)(struct ahd_softc *);
+
+typedef enum {
+	AHD_MODE_DFF0,
+	AHD_MODE_DFF1,
+	AHD_MODE_CCHAN,
+	AHD_MODE_SCSI,
+	AHD_MODE_CFG,
+	AHD_MODE_UNKNOWN
+} ahd_mode;
+
+#define AHD_MK_MSK(x) (0x01 << (x))
+#define AHD_MODE_DFF0_MSK	AHD_MK_MSK(AHD_MODE_DFF0)
+#define AHD_MODE_DFF1_MSK	AHD_MK_MSK(AHD_MODE_DFF1)
+#define AHD_MODE_CCHAN_MSK	AHD_MK_MSK(AHD_MODE_CCHAN)
+#define AHD_MODE_SCSI_MSK	AHD_MK_MSK(AHD_MODE_SCSI)
+#define AHD_MODE_CFG_MSK	AHD_MK_MSK(AHD_MODE_CFG)
+#define AHD_MODE_UNKNOWN_MSK	AHD_MK_MSK(AHD_MODE_UNKNOWN)
+#define AHD_MODE_ANY_MSK (~0)
+
+typedef uint8_t ahd_mode_state;
+
+typedef void ahd_callback_t (void *);
+
+struct ahd_completion
+{
+	uint16_t	tag;
+	uint8_t		sg_status;
+	uint8_t		valid_tag;
+};
+
+struct ahd_softc {
+	bus_space_tag_t           tags[2];
+	bus_space_handle_t        bshs[2];
+#ifndef __linux__
+	bus_dma_tag_t		  buffer_dmat;   /* dmat for buffer I/O */
+#endif
+	struct scb_data		  scb_data;
+
+	struct hardware_scb	 *next_queued_hscb;
+	struct map_node		 *next_queued_hscb_map;
+
+	/*
+	 * SCBs that have been sent to the controller
+	 */
+	LIST_HEAD(, scb)	  pending_scbs;
+
+	/*
+	 * Current register window mode information.
+	 */
+	ahd_mode		  dst_mode;
+	ahd_mode		  src_mode;
+
+	/*
+	 * Saved register window mode information
+	 * used for restore on next unpause.
+	 */
+	ahd_mode		  saved_dst_mode;
+	ahd_mode		  saved_src_mode;
+
+	/*
+	 * Platform specific data.
+	 */
+	struct ahd_platform_data *platform_data;
+
+	/*
+	 * Platform specific device information.
+	 */
+	ahd_dev_softc_t		  dev_softc;
+
+	/*
+	 * Bus specific device information.
+	 */
+	ahd_bus_intr_t		  bus_intr;
+
+	/*
+	 * Target mode related state kept on a per enabled lun basis.
+	 * Targets that are not enabled will have null entries.
+	 * As an initiator, we keep one target entry for our initiator
+	 * ID to store our sync/wide transfer settings.
+	 */
+	struct ahd_tmode_tstate  *enabled_targets[AHD_NUM_TARGETS];
+
+	/*
+	 * The black hole device responsible for handling requests for
+	 * disabled luns on enabled targets.
+	 */
+	struct ahd_tmode_lstate  *black_hole;
+
+	/*
+	 * Device instance currently on the bus awaiting a continue TIO
+	 * for a command that was not given the disconnect priveledge.
+	 */
+	struct ahd_tmode_lstate  *pending_device;
+
+	/*
+	 * Timer handles for timer driven callbacks.
+	 */
+	ahd_timer_t		  reset_timer;
+	ahd_timer_t		  stat_timer;
+
+	/*
+	 * Statistics.
+	 */
+#define	AHD_STAT_UPDATE_US	250000 /* 250ms */
+#define	AHD_STAT_BUCKETS	4
+	u_int			  cmdcmplt_bucket;
+	uint32_t		  cmdcmplt_counts[AHD_STAT_BUCKETS];
+	uint32_t		  cmdcmplt_total;
+
+	/*
+	 * Card characteristics
+	 */
+	ahd_chip		  chip;
+	ahd_feature		  features;
+	ahd_bug			  bugs;
+	ahd_flag		  flags;
+	struct seeprom_config	 *seep_config;
+
+	/* Command Queues */
+	struct ahd_completion	  *qoutfifo;
+	uint16_t		  qoutfifonext;
+	uint16_t		  qoutfifonext_valid_tag;
+	uint16_t		  qinfifonext;
+	uint16_t		  qinfifo[AHD_SCB_MAX];
+
+	/*
+	 * Our qfreeze count.  The sequencer compares
+	 * this value with its own counter to determine
+	 * whether to allow selections to occur.
+	 */
+	uint16_t		  qfreeze_cnt;
+
+	/* Values to store in the SEQCTL register for pause and unpause */
+	uint8_t			  unpause;
+	uint8_t			  pause;
+
+	/* Critical Section Data */
+	struct cs		 *critical_sections;
+	u_int			  num_critical_sections;
+
+	/* Buffer for handling packetized bitbucket. */
+	uint8_t			 *overrun_buf;
+
+	/* Links for chaining softcs */
+	TAILQ_ENTRY(ahd_softc)	  links;
+
+	/* Channel Names ('A', 'B', etc.) */
+	char			  channel;
+
+	/* Initiator Bus ID */
+	uint8_t			  our_id;
+
+	/*
+	 * Target incoming command FIFO.
+	 */
+	struct target_cmd	 *targetcmds;
+	uint8_t			  tqinfifonext;
+
+	/*
+	 * Cached verson of the hs_mailbox so we can avoid
+	 * pausing the sequencer during mailbox updates.
+	 */
+	uint8_t			  hs_mailbox;
+
+	/*
+	 * Incoming and outgoing message handling.
+	 */
+	uint8_t			  send_msg_perror;
+	ahd_msg_flags		  msg_flags;
+	ahd_msg_type		  msg_type;
+	uint8_t			  msgout_buf[12];/* Message we are sending */
+	uint8_t			  msgin_buf[12];/* Message we are receiving */
+	u_int			  msgout_len;	/* Length of message to send */
+	u_int			  msgout_index;	/* Current index in msgout */
+	u_int			  msgin_index;	/* Current index in msgin */
+
+	/*
+	 * Mapping information for data structures shared
+	 * between the sequencer and kernel.
+	 */
+	bus_dma_tag_t		  parent_dmat;
+	bus_dma_tag_t		  shared_data_dmat;
+	struct map_node		  shared_data_map;
+
+	/* Information saved through suspend/resume cycles */
+	struct ahd_suspend_state  suspend_state;
+
+	/* Number of enabled target mode device on this card */
+	u_int			  enabled_luns;
+
+	/* Initialization level of this data structure */
+	u_int			  init_level;
+
+	/* PCI cacheline size. */
+	u_int			  pci_cachesize;
+
+	/* IO Cell Parameters */
+	uint8_t			  iocell_opts[AHD_NUM_PER_DEV_ANNEXCOLS];
+
+	u_int			  stack_size;
+	uint16_t		 *saved_stack;
+
+	/* Per-Unit descriptive information */
+	const char		 *description;
+	const char		 *bus_description;
+	char			 *name;
+	int			  unit;
+
+	/* Selection Timer settings */
+	int			  seltime;
+
+	/*
+	 * Interrupt coalescing settings.
+	 */
+#define	AHD_INT_COALESCING_TIMER_DEFAULT		250 /*us*/
+#define	AHD_INT_COALESCING_MAXCMDS_DEFAULT		10
+#define	AHD_INT_COALESCING_MAXCMDS_MAX			127
+#define	AHD_INT_COALESCING_MINCMDS_DEFAULT		5
+#define	AHD_INT_COALESCING_MINCMDS_MAX			127
+#define	AHD_INT_COALESCING_THRESHOLD_DEFAULT		2000
+#define	AHD_INT_COALESCING_STOP_THRESHOLD_DEFAULT	1000
+	u_int			  int_coalescing_timer;
+	u_int			  int_coalescing_maxcmds;
+	u_int			  int_coalescing_mincmds;
+	u_int			  int_coalescing_threshold;
+	u_int			  int_coalescing_stop_threshold;
+
+	uint16_t	 	  user_discenable;/* Disconnection allowed  */
+	uint16_t		  user_tagenable;/* Tagged Queuing allowed */
+};
+
+/*************************** IO Cell Configuration ****************************/
+#define	AHD_PRECOMP_SLEW_INDEX						\
+    (AHD_ANNEXCOL_PRECOMP_SLEW - AHD_ANNEXCOL_PER_DEV0)
+
+#define	AHD_AMPLITUDE_INDEX						\
+    (AHD_ANNEXCOL_AMPLITUDE - AHD_ANNEXCOL_PER_DEV0)
+
+#define AHD_SET_SLEWRATE(ahd, new_slew)					\
+do {									\
+    (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_SLEWRATE_MASK;	\
+    (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] |=			\
+	(((new_slew) << AHD_SLEWRATE_SHIFT) & AHD_SLEWRATE_MASK);	\
+} while (0)
+
+#define AHD_SET_PRECOMP(ahd, new_pcomp)					\
+do {									\
+    (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_PRECOMP_MASK;	\
+    (ahd)->iocell_opts[AHD_PRECOMP_SLEW_INDEX] |=			\
+	(((new_pcomp) << AHD_PRECOMP_SHIFT) & AHD_PRECOMP_MASK);	\
+} while (0)
+
+#define AHD_SET_AMPLITUDE(ahd, new_amp)					\
+do {									\
+    (ahd)->iocell_opts[AHD_AMPLITUDE_INDEX] &= ~AHD_AMPLITUDE_MASK;	\
+    (ahd)->iocell_opts[AHD_AMPLITUDE_INDEX] |=				\
+	(((new_amp) << AHD_AMPLITUDE_SHIFT) & AHD_AMPLITUDE_MASK);	\
+} while (0)
+
+/************************ Active Device Information ***************************/
+typedef enum {
+	ROLE_UNKNOWN,
+	ROLE_INITIATOR,
+	ROLE_TARGET
+} role_t;
+
+struct ahd_devinfo {
+	int	 our_scsiid;
+	int	 target_offset;
+	uint16_t target_mask;
+	u_int	 target;
+	u_int	 lun;
+	char	 channel;
+	role_t	 role;		/*
+				 * Only guaranteed to be correct if not
+				 * in the busfree state.
+				 */
+};
+
+/****************************** PCI Structures ********************************/
+#define AHD_PCI_IOADDR0	PCIR_BAR(0)	/* I/O BAR*/
+#define AHD_PCI_MEMADDR	PCIR_BAR(1)	/* Memory BAR */
+#define AHD_PCI_IOADDR1	PCIR_BAR(3)	/* Second I/O BAR */
+
+typedef int (ahd_device_setup_t)(struct ahd_softc *);
+
+struct ahd_pci_identity {
+	uint64_t		 full_id;
+	uint64_t		 id_mask;
+	const char		*name;
+	ahd_device_setup_t	*setup;
+};
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+	uint32_t		 full_id;
+	uint32_t		 id_mask;
+	const char		*name;
+	ahd_device_setup_t	*setup;
+};
+extern struct aic7770_identity aic7770_ident_table [];
+extern const int ahd_num_aic7770_devs;
+
+#define AHD_EISA_SLOT_OFFSET	0xc00
+#define AHD_EISA_IOSIZE		0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+
+/***************************** PCI Front End *********************************/
+const struct	ahd_pci_identity *ahd_find_pci_device(ahd_dev_softc_t);
+int			  ahd_pci_config(struct ahd_softc *,
+					 const struct ahd_pci_identity *);
+int	ahd_pci_test_register_access(struct ahd_softc *);
+#ifdef CONFIG_PM
+void	ahd_pci_suspend(struct ahd_softc *);
+void	ahd_pci_resume(struct ahd_softc *);
+#endif
+
+/************************** SCB and SCB queue management **********************/
+void		ahd_qinfifo_requeue_tail(struct ahd_softc *ahd,
+					 struct scb *scb);
+
+/****************************** Initialization ********************************/
+struct ahd_softc	*ahd_alloc(void *platform_arg, char *name);
+int			 ahd_softc_init(struct ahd_softc *);
+void			 ahd_controller_info(struct ahd_softc *ahd, char *buf);
+int			 ahd_init(struct ahd_softc *ahd);
+#ifdef CONFIG_PM
+int			 ahd_suspend(struct ahd_softc *ahd);
+void			 ahd_resume(struct ahd_softc *ahd);
+#endif
+int			 ahd_default_config(struct ahd_softc *ahd);
+int			 ahd_parse_vpddata(struct ahd_softc *ahd,
+					   struct vpd_config *vpd);
+int			 ahd_parse_cfgdata(struct ahd_softc *ahd,
+					   struct seeprom_config *sc);
+void			 ahd_intr_enable(struct ahd_softc *ahd, int enable);
+void			 ahd_pause_and_flushwork(struct ahd_softc *ahd);
+void			 ahd_set_unit(struct ahd_softc *, int);
+void			 ahd_set_name(struct ahd_softc *, char *);
+struct scb		*ahd_get_scb(struct ahd_softc *ahd, u_int col_idx);
+void			 ahd_free_scb(struct ahd_softc *ahd, struct scb *scb);
+void			 ahd_free(struct ahd_softc *ahd);
+int			 ahd_reset(struct ahd_softc *ahd, int reinit);
+int			 ahd_write_flexport(struct ahd_softc *ahd,
+					    u_int addr, u_int value);
+int			 ahd_read_flexport(struct ahd_softc *ahd, u_int addr,
+					   uint8_t *value);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+	SEARCH_COMPLETE,
+	SEARCH_COUNT,
+	SEARCH_REMOVE,
+	SEARCH_PRINT
+} ahd_search_action;
+int			ahd_search_qinfifo(struct ahd_softc *ahd, int target,
+					   char channel, int lun, u_int tag,
+					   role_t role, uint32_t status,
+					   ahd_search_action action);
+int			ahd_search_disc_list(struct ahd_softc *ahd, int target,
+					     char channel, int lun, u_int tag,
+					     int stop_on_first, int remove,
+					     int save_state);
+int			ahd_reset_channel(struct ahd_softc *ahd, char channel,
+					  int initiate_reset);
+/*************************** Utility Functions ********************************/
+void			ahd_compile_devinfo(struct ahd_devinfo *devinfo,
+					    u_int our_id, u_int target,
+					    u_int lun, char channel,
+					    role_t role);
+/************************** Transfer Negotiation ******************************/
+void			ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
+					  u_int *ppr_options, u_int maxsync);
+/*
+ * Negotiation types.  These are used to qualify if we should renegotiate
+ * even if our goal and current transport parameters are identical.
+ */
+typedef enum {
+	AHD_NEG_TO_GOAL,	/* Renegotiate only if goal and curr differ. */
+	AHD_NEG_IF_NON_ASYNC,	/* Renegotiate so long as goal is non-async. */
+	AHD_NEG_ALWAYS		/* Renegotiat even if goal is async. */
+} ahd_neg_type;
+int			ahd_update_neg_request(struct ahd_softc*,
+					       struct ahd_devinfo*,
+					       struct ahd_tmode_tstate*,
+					       struct ahd_initiator_tinfo*,
+					       ahd_neg_type);
+void			ahd_set_width(struct ahd_softc *ahd,
+				      struct ahd_devinfo *devinfo,
+				      u_int width, u_int type, int paused);
+void			ahd_set_syncrate(struct ahd_softc *ahd,
+					 struct ahd_devinfo *devinfo,
+					 u_int period, u_int offset,
+					 u_int ppr_options,
+					 u_int type, int paused);
+typedef enum {
+	AHD_QUEUE_NONE,
+	AHD_QUEUE_BASIC,
+	AHD_QUEUE_TAGGED
+} ahd_queue_alg;
+
+/**************************** Target Mode *************************************/
+#ifdef AHD_TARGET_MODE
+void		ahd_send_lstate_events(struct ahd_softc *,
+				       struct ahd_tmode_lstate *);
+void		ahd_handle_en_lun(struct ahd_softc *ahd,
+				  struct cam_sim *sim, union ccb *ccb);
+cam_status	ahd_find_tmode_devs(struct ahd_softc *ahd,
+				    struct cam_sim *sim, union ccb *ccb,
+				    struct ahd_tmode_tstate **tstate,
+				    struct ahd_tmode_lstate **lstate,
+				    int notfound_failure);
+#ifndef AHD_TMODE_ENABLE
+#define AHD_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHD_DEBUG
+extern uint32_t ahd_debug;
+#define AHD_SHOW_MISC		0x00001
+#define AHD_SHOW_SENSE		0x00002
+#define AHD_SHOW_RECOVERY	0x00004
+#define AHD_DUMP_SEEPROM	0x00008
+#define AHD_SHOW_TERMCTL	0x00010
+#define AHD_SHOW_MEMORY		0x00020
+#define AHD_SHOW_MESSAGES	0x00040
+#define AHD_SHOW_MODEPTR	0x00080
+#define AHD_SHOW_SELTO		0x00100
+#define AHD_SHOW_FIFOS		0x00200
+#define AHD_SHOW_QFULL		0x00400
+#define	AHD_SHOW_DV		0x00800
+#define AHD_SHOW_MASKED_ERRORS	0x01000
+#define AHD_SHOW_QUEUE		0x02000
+#define AHD_SHOW_TQIN		0x04000
+#define AHD_SHOW_SG		0x08000
+#define AHD_SHOW_INT_COALESCING	0x10000
+#define AHD_DEBUG_SEQUENCER	0x20000
+#endif
+void			ahd_print_devinfo(struct ahd_softc *ahd,
+					  struct ahd_devinfo *devinfo);
+void			ahd_dump_card_state(struct ahd_softc *ahd);
+int			ahd_print_register(const ahd_reg_parse_entry_t *table,
+					   u_int num_entries,
+					   const char *name,
+					   u_int address,
+					   u_int value,
+					   u_int *cur_column,
+					   u_int wrap_point);
+#endif /* _AIC79XX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx.reg b/drivers/scsi/aic7xxx/aic79xx.reg
new file mode 100644
index 0000000..7e12c31
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.reg
@@ -0,0 +1,4281 @@
+/*
+ * Aic79xx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-2001, 2004 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#77 $"
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic79xx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic79xx driver.
+ */
+
+/* Register window Modes */
+#define M_DFF0		0
+#define M_DFF1		1
+#define M_CCHAN		2
+#define M_SCSI		3
+#define M_CFG		4
+#define M_DST_SHIFT	4
+
+#define MK_MODE(src, dst) ((src) | ((dst) << M_DST_SHIFT))
+#define SET_MODE(src, dst)						\
+	SET_SRC_MODE	src;						\
+	SET_DST_MODE	dst;						\
+	if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {			\
+		mvi	MK_MODE(src, dst) call set_mode_work_around;	\
+	} else {							\
+		mvi	MODE_PTR, MK_MODE(src, dst);			\
+	}
+
+#define RESTORE_MODE(mode)						\
+	if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {			\
+		mov	mode call set_mode_work_around;			\
+	} else {							\
+		mov	MODE_PTR, mode;					\
+	}
+
+#define SET_SEQINTCODE(code)						\
+	if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {			\
+		mvi	code call set_seqint_work_around;		\
+	} else {							\
+		mvi	SEQINTCODE, code;				\
+	}
+
+/*
+ * Registers marked "dont_generate_debug_code" are not (yet) referenced
+ * from the driver code, and this keyword inhibit generation
+ * of debug code for them.
+ *
+ * REG_PRETTY_PRINT config will complain if dont_generate_debug_code
+ * is added to the register which is referenced in the driver.
+ * Unreferenced register with no dont_generate_debug_code will result
+ * in dead code. No warning is issued.
+ */
+
+/*
+ * Mode Pointer
+ * Controls which of the 5, 512byte, address spaces should be used
+ * as the source and destination of any register accesses in our
+ * register window.
+ */
+register MODE_PTR {
+	address			0x000
+	access_mode	RW
+	field	DST_MODE	0x70
+	field	SRC_MODE	0x07
+	mode_pointer
+	dont_generate_debug_code
+}
+
+const SRC_MODE_SHIFT	0
+const DST_MODE_SHIFT	4
+
+/*
+ * Host Interrupt Status
+ */
+register INTSTAT {
+	address			0x001
+	access_mode	RW
+	field	HWERRINT	0x80
+	field	BRKADRINT	0x40
+	field	SWTMINT		0x20
+	field	PCIINT		0x10
+	field	SCSIINT		0x08
+	field	SEQINT		0x04
+	field	CMDCMPLT	0x02
+	field	SPLTINT		0x01
+	mask	INT_PEND 0xFF
+}
+
+/*
+ * Sequencer Interrupt Code
+ */
+register SEQINTCODE {
+	address			0x002
+	access_mode	RW
+	field {
+		NO_SEQINT,			/* No seqint pending. */
+		BAD_PHASE,			/* unknown scsi bus phase */
+		SEND_REJECT,			/* sending a message reject */
+		PROTO_VIOLATION, 		/* Protocol Violation */
+		NO_MATCH,			/* no cmd match for reconnect */
+		IGN_WIDE_RES,			/* Complex IGN Wide Res Msg */
+		PDATA_REINIT,			/*
+						 * Returned to data phase
+						 * that requires data
+						 * transfer pointers to be
+						 * recalculated from the
+						 * transfer residual.
+						 */
+		HOST_MSG_LOOP,			/*
+						 * The bus is ready for the
+						 * host to perform another
+						 * message transaction.  This
+						 * mechanism is used for things
+						 * like sync/wide negotiation
+						 * that require a kernel based
+						 * message state engine.
+						 */
+		BAD_STATUS,			/* Bad status from target */
+		DATA_OVERRUN,			/*
+						 * Target attempted to write
+						 * beyond the bounds of its
+						 * command.
+						 */
+		MKMSG_FAILED,			/*
+						 * Target completed command
+						 * without honoring our ATN
+						 * request to issue a message. 
+						 */
+		MISSED_BUSFREE,			/*
+						 * The sequencer never saw
+						 * the bus go free after
+						 * either a command complete
+						 * or disconnect message.
+						 */
+		DUMP_CARD_STATE,
+		ILLEGAL_PHASE,
+		INVALID_SEQINT,
+		CFG4ISTAT_INTR,
+		STATUS_OVERRUN,
+		CFG4OVERRUN,
+		ENTERING_NONPACK,
+		TASKMGMT_FUNC_COMPLETE,		/*
+						 * Task management function
+						 * request completed with
+						 * an expected busfree.
+						 */
+		TASKMGMT_CMD_CMPLT_OKAY,	/*
+						 * A command with a non-zero
+						 * task management function
+						 * has completed via the normal
+						 * command completion method
+						 * for commands with a zero
+						 * task management function.
+						 * This happens when an attempt
+						 * to abort a command loses
+						 * the race for the command to
+						 * complete normally.
+						 */
+		TRACEPOINT0,
+		TRACEPOINT1,
+		TRACEPOINT2,
+		TRACEPOINT3,
+		SAW_HWERR,
+		BAD_SCB_STATUS
+	}
+	dont_generate_debug_code
+}
+
+/*
+ * Clear Host Interrupt
+ */
+register CLRINT {
+	address			0x003
+	access_mode	WO
+	count		19
+	field	CLRHWERRINT	0x80 /* Rev B or greater */
+	field	CLRBRKADRINT	0x40
+	field	CLRSWTMINT	0x20
+	field	CLRPCIINT	0x10
+	field	CLRSCSIINT	0x08
+	field	CLRSEQINT	0x04
+	field	CLRCMDINT	0x02
+	field	CLRSPLTINT	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Error Register
+ */
+register ERROR {
+	address			0x004
+	access_mode	RO
+	field	CIOPARERR	0x80
+	field	CIOACCESFAIL	0x40 /* Rev B or greater */
+	field	MPARERR		0x20
+	field	DPARERR		0x10
+	field	SQPARERR	0x08
+	field	ILLOPCODE	0x04
+	field	DSCTMOUT	0x02
+	dont_generate_debug_code
+}
+
+/*
+ * Clear Error
+ */
+register CLRERR {
+	address			0x004
+	access_mode 	WO
+	field	CLRCIOPARERR	0x80
+	field	CLRCIOACCESFAIL	0x40 /* Rev B or greater */
+	field	CLRMPARERR	0x20
+	field	CLRDPARERR	0x10
+	field	CLRSQPARERR	0x08
+	field	CLRILLOPCODE	0x04
+	field	CLRDSCTMOUT	0x02
+}
+
+/*
+ * Host Control Register
+ * Overall host control of the device.
+ */
+register HCNTRL {
+	address			0x005
+	access_mode	RW
+	count		12
+	field	SEQ_RESET	0x80 /* Rev B or greater */
+	field	POWRDN		0x40
+	field	SWINT		0x10
+	field	SWTIMER_START_B	0x08 /* Rev B or greater */
+	field	PAUSE		0x04
+	field	INTEN		0x02
+	field	CHIPRST		0x01
+	field	CHIPRSTACK	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Host New SCB Queue Offset
+ */
+register HNSCB_QOFF {
+	address			0x006
+	access_mode	RW
+	size		2
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * Host Empty SCB Queue Offset
+ */
+register HESCB_QOFF {
+	address			0x008
+	access_mode	RW
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * Host Mailbox
+ */
+register HS_MAILBOX {
+	address			0x00B
+	access_mode	RW
+	mask	HOST_TQINPOS	0x80	/* Boundary at either 0 or 128 */
+	mask	ENINT_COALESCE	0x40	/* Perform interrupt coalescing */
+}
+
+/*
+ * Sequencer Interrupt Status
+ */
+register SEQINTSTAT {
+	address			0x00C
+	count		1
+	access_mode	RO
+	field	SEQ_SWTMRTO	0x10
+	field	SEQ_SEQINT	0x08
+	field	SEQ_SCSIINT	0x04
+	field	SEQ_PCIINT	0x02
+	field	SEQ_SPLTINT	0x01
+}
+
+/*
+ * Clear SEQ Interrupt
+ */
+register CLRSEQINTSTAT {
+	address			0x00C
+	access_mode	WO
+	field	CLRSEQ_SWTMRTO	0x10
+	field	CLRSEQ_SEQINT	0x08
+	field	CLRSEQ_SCSIINT	0x04
+	field	CLRSEQ_PCIINT	0x02
+	field	CLRSEQ_SPLTINT	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Software Timer
+ */
+register SWTIMER {
+	address			0x00E
+	access_mode	RW
+	size		2
+	dont_generate_debug_code
+}
+
+/*
+ * SEQ New SCB Queue Offset
+ */
+register SNSCB_QOFF {
+	address			0x010
+	access_mode	RW
+	size		2
+	modes		M_CCHAN
+	dont_generate_debug_code
+}
+
+/*
+ * SEQ Empty SCB Queue Offset
+ */
+register SESCB_QOFF {
+	address			0x012
+	count		2
+	access_mode	RW
+	modes		M_CCHAN
+	dont_generate_debug_code
+}
+
+/*
+ * SEQ Done SCB Queue Offset
+ */
+register SDSCB_QOFF {
+	address			0x014
+	access_mode	RW
+	modes		M_CCHAN
+	size		2
+	dont_generate_debug_code
+}
+
+/*
+ * Queue Offset Control & Status
+ */
+register QOFF_CTLSTA {
+	address			0x016
+	access_mode	RW
+	modes		M_CCHAN
+	field	EMPTY_SCB_AVAIL	0x80
+	field	NEW_SCB_AVAIL	0x40
+	field	SDSCB_ROLLOVR	0x20
+	field	HS_MAILBOX_ACT	0x10
+	field	SCB_QSIZE	0x0F {
+		SCB_QSIZE_4,
+		SCB_QSIZE_8,
+		SCB_QSIZE_16,
+		SCB_QSIZE_32,
+		SCB_QSIZE_64,
+		SCB_QSIZE_128,
+		SCB_QSIZE_256,
+		SCB_QSIZE_512,
+		SCB_QSIZE_1024,
+		SCB_QSIZE_2048,
+		SCB_QSIZE_4096,
+		SCB_QSIZE_8192,
+		SCB_QSIZE_16384
+	}
+	dont_generate_debug_code
+}
+
+/*
+ * Interrupt Control
+ */
+register INTCTL {
+	address			0x018
+	access_mode	RW
+	field	SWTMINTMASK	0x80
+	field	SWTMINTEN	0x40
+	field	SWTIMER_START	0x20
+	field	AUTOCLRCMDINT	0x10
+	field	PCIINTEN	0x08
+	field	SCSIINTEN	0x04
+	field	SEQINTEN	0x02
+	field	SPLTINTEN	0x01
+}
+
+/*
+ * Data FIFO Control
+ */
+register DFCNTRL {
+	address			0x019
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	count		11
+	field	PRELOADEN	0x80
+	field	SCSIENWRDIS	0x40	/* Rev B only. */
+	field	SCSIEN		0x20
+	field	SCSIENACK	0x20
+	field	HDMAEN		0x08
+	field	HDMAENACK	0x08
+	field	DIRECTION	0x04
+	field	DIRECTIONACK	0x04
+	field	FIFOFLUSH	0x02
+	field	FIFOFLUSHACK	0x02
+	field	DIRECTIONEN	0x01
+}
+
+/*
+ * Device Space Command 0
+ */
+register DSCOMMAND0 {
+	address			0x019
+	count		1
+	access_mode	RW
+	modes		M_CFG
+	field	CACHETHEN	0x80	/* Cache Threshold enable */
+	field	DPARCKEN	0x40	/* Data Parity Check Enable */
+	field	MPARCKEN	0x20	/* Memory Parity Check Enable */
+	field	EXTREQLCK	0x10	/* External Request Lock */
+	field	DISABLE_TWATE	0x02	/* Rev B or greater */
+	field	CIOPARCKEN	0x01	/* Internal bus parity error enable */
+	dont_generate_debug_code
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFSTATUS {
+	address			0x01A
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	PRELOAD_AVAIL		0x80
+	field	PKT_PRELOAD_AVAIL	0x40
+	field	MREQPEND		0x10
+	field	HDONE			0x08
+	field	DFTHRESH		0x04
+	field	FIFOFULL		0x02
+	field	FIFOEMP			0x01
+}
+
+/*
+ * S/G Cache Pointer
+ */
+register SG_CACHE_PRE {
+	address			0x01B
+	access_mode	WO
+	modes		M_DFF0, M_DFF1
+	field	SG_ADDR_MASK	0xf8
+	field	ODD_SEG		0x04
+	field	LAST_SEG	0x02
+	dont_generate_debug_code
+}
+
+register SG_CACHE_SHADOW {
+	address			0x01B
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	SG_ADDR_MASK	0xf8
+	field	ODD_SEG		0x04
+	field	LAST_SEG	0x02
+	field	LAST_SEG_DONE	0x01
+}
+
+/*
+ * Arbiter Control
+ */
+register ARBCTL {
+	address			0x01B
+	access_mode	RW
+	modes		M_CFG
+	field	RESET_HARB	0x80
+	field	RETRY_SWEN	0x08
+	field	USE_TIME	0x07
+}
+
+/*
+ * Data Channel Host Address
+ */
+register HADDR {
+	address			0x070
+	access_mode	RW
+	size		8
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * Host Overlay DMA Address
+ */
+register HODMAADR {
+	address			0x070
+	access_mode	RW
+	size		8
+	modes		M_SCSI
+}
+
+/*
+ * PCI PLL Delay.
+ */
+register PLLDELAY {
+	address			0x070
+	access_mode	RW
+	size		1
+	modes		M_CFG
+	field	SPLIT_DROP_REQ	0x80
+}
+
+/*
+ * Data Channel Host Count
+ */
+register HCNT {
+	address			0x078
+	access_mode	RW
+	size		3
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * Host Overlay DMA Count
+ */
+register HODMACNT {
+	address			0x078
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+}
+
+/*
+ * Host Overlay DMA Enable
+ */
+register HODMAEN {
+	address			0x07A
+	access_mode	RW
+	modes		M_SCSI
+}
+
+/*
+ * Scatter/Gather Host Address
+ */
+register SGHADDR {
+	address			0x07C
+	access_mode	RW
+	size		8
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * SCB Host Address
+ */
+register SCBHADDR {
+	address			0x07C
+	access_mode	RW
+	size		8
+	modes		M_CCHAN
+	dont_generate_debug_code
+}
+
+/*
+ * Scatter/Gather Host Count
+ */
+register SGHCNT {
+	address			0x084
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * SCB Host Count
+ */
+register SCBHCNT {
+	address			0x084
+	access_mode	RW
+	modes		M_CCHAN
+	dont_generate_debug_code
+}
+
+/*
+ * Data FIFO Threshold
+ */
+register DFF_THRSH {
+	address			0x088
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	field	WR_DFTHRSH	0x70 {
+		WR_DFTHRSH_MIN,
+		WR_DFTHRSH_25,
+		WR_DFTHRSH_50,
+		WR_DFTHRSH_63,
+		WR_DFTHRSH_75,
+		WR_DFTHRSH_85,
+		WR_DFTHRSH_90,
+		WR_DFTHRSH_MAX
+	}
+	field	RD_DFTHRSH	0x07 {
+		RD_DFTHRSH_MIN,
+		RD_DFTHRSH_25,
+		RD_DFTHRSH_50,
+		RD_DFTHRSH_63,
+		RD_DFTHRSH_75,
+		RD_DFTHRSH_85,
+		RD_DFTHRSH_90,
+		RD_DFTHRSH_MAX
+	}
+	dont_generate_debug_code
+}
+
+/*
+ * ROM Address
+ */
+register ROMADDR {
+	address			0x08A
+	access_mode	RW
+	size		3
+}
+
+/*
+ * ROM Control
+ */
+register ROMCNTRL {
+	address			0x08D
+	access_mode	RW
+	field	ROMOP		0xE0
+	field	ROMSPD		0x18
+	field	REPEAT		0x02
+	field	RDY		0x01
+}
+
+/*
+ * ROM Data
+ */
+register ROMDATA {
+	address			0x08E
+	access_mode	RW
+}
+
+/*
+ * Data Channel Receive Message 0
+ */
+register DCHRXMSG0 {
+	address			0x090
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field		CDNUM	0xF8
+	field		CFNUM	0x07
+}
+
+/*
+ * CMC Receive Message 0
+ */
+register CMCRXMSG0 {
+	address			0x090
+	access_mode	RO
+	modes		M_CCHAN
+	field		CDNUM	0xF8
+	field		CFNUM	0x07
+}
+
+/*
+ * Overlay Receive Message 0
+ */
+register OVLYRXMSG0 {
+	address			0x090
+	access_mode	RO
+	modes		M_SCSI
+	field		CDNUM	0xF8
+	field		CFNUM	0x07
+}
+
+/*
+ * Relaxed Order Enable
+ */
+register ROENABLE {
+	address			0x090
+	access_mode	RW
+	modes		M_CFG
+	field	MSIROEN		0x20
+	field	OVLYROEN	0x10
+	field	CMCROEN		0x08
+	field	SGROEN		0x04
+	field	DCH1ROEN	0x02
+	field	DCH0ROEN	0x01
+}
+
+/*
+ * Data Channel Receive Message 1
+ */
+register DCHRXMSG1 {
+	address			0x091
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	CBNUM		0xFF
+}
+
+/*
+ * CMC Receive Message 1
+ */
+register CMCRXMSG1 {
+	address			0x091
+	access_mode	RO
+	modes		M_CCHAN
+	field	CBNUM		0xFF
+}
+
+/*
+ * Overlay Receive Message 1
+ */
+register OVLYRXMSG1 {
+	address			0x091
+	access_mode	RO
+	modes		M_SCSI
+	field	CBNUM		0xFF
+}
+
+/*
+ * No Snoop Enable
+ */
+register NSENABLE {
+	address			0x091
+	access_mode	RW
+	modes		M_CFG
+	field	MSINSEN		0x20
+	field	OVLYNSEN	0x10
+	field	CMCNSEN		0x08
+	field	SGNSEN		0x04
+	field	DCH1NSEN	0x02
+	field	DCH0NSEN	0x01
+}
+
+/*
+ * Data Channel Receive Message 2
+ */
+register DCHRXMSG2 {
+	address			0x092
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	MINDEX		0xFF
+}
+
+/*
+ * CMC Receive Message 2
+ */
+register CMCRXMSG2 {
+	address			0x092
+	access_mode	RO
+	modes		M_CCHAN
+	field	MINDEX		0xFF
+}
+
+/*
+ * Overlay Receive Message 2
+ */
+register OVLYRXMSG2 {
+	address			0x092
+	access_mode	RO
+	modes		M_SCSI
+	field	MINDEX		0xFF
+}
+
+/*
+ * Outstanding Split Transactions
+ */
+register OST {
+	address			0x092
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Data Channel Receive Message 3
+ */
+register DCHRXMSG3 {
+	address			0x093
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	MCLASS		0x0F
+}
+
+/*
+ * CMC Receive Message 3
+ */
+register CMCRXMSG3 {
+	address			0x093
+	access_mode	RO
+	modes		M_CCHAN
+	field	MCLASS		0x0F
+}
+
+/*
+ * Overlay Receive Message 3
+ */
+register OVLYRXMSG3 {
+	address			0x093
+	access_mode	RO
+	modes		M_SCSI
+	field	MCLASS		0x0F
+}
+
+/*
+ * PCI-X Control
+ */
+register PCIXCTL {
+	address			0x093
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	field	SERRPULSE	0x80
+	field	UNEXPSCIEN	0x20
+	field	SPLTSMADIS	0x10
+	field	SPLTSTADIS	0x08
+	field	SRSPDPEEN	0x04
+	field	TSCSERREN	0x02
+	field	CMPABCDIS	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * CMC Sequencer Byte Count
+ */
+register CMCSEQBCNT {
+	address			0x094
+	access_mode	RO
+	modes		M_CCHAN
+}
+
+/*
+ * Overlay Sequencer Byte Count
+ */
+register OVLYSEQBCNT {
+	address			0x094
+	access_mode	RO
+	modes		M_SCSI
+}
+
+/*
+ * Data Channel Sequencer Byte Count
+ */
+register DCHSEQBCNT {
+	address			0x094
+	access_mode	RO
+	size		2
+	modes		M_DFF0, M_DFF1
+}
+
+/*
+ * Data Channel Split Status 0
+ */
+register DCHSPLTSTAT0 {
+	address			0x096
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	count		2
+	field	STAETERM	0x80
+	field	SCBCERR		0x40
+	field	SCADERR		0x20
+	field	SCDATBUCKET	0x10
+	field	CNTNOTCMPLT	0x08
+	field	RXOVRUN		0x04
+	field	RXSCEMSG	0x02
+	field	RXSPLTRSP	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * CMC Split Status 0
+ */
+register CMCSPLTSTAT0 {
+	address			0x096
+	access_mode	RW
+	modes		M_CCHAN
+	field	STAETERM	0x80
+	field	SCBCERR		0x40
+	field	SCADERR		0x20
+	field	SCDATBUCKET	0x10
+	field	CNTNOTCMPLT	0x08
+	field	RXOVRUN		0x04
+	field	RXSCEMSG	0x02
+	field	RXSPLTRSP	0x01
+}
+
+/*
+ * Overlay Split Status 0
+ */
+register OVLYSPLTSTAT0 {
+	address			0x096
+	access_mode	RW
+	modes		M_SCSI
+	field	STAETERM	0x80
+	field	SCBCERR		0x40
+	field	SCADERR		0x20
+	field	SCDATBUCKET	0x10
+	field	CNTNOTCMPLT	0x08
+	field	RXOVRUN		0x04
+	field	RXSCEMSG	0x02
+	field	RXSPLTRSP	0x01
+}
+
+/*
+ * Data Channel Split Status 1
+ */
+register DCHSPLTSTAT1 {
+	address			0x097
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	count		2
+	field	RXDATABUCKET	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * CMC Split Status 1
+ */
+register CMCSPLTSTAT1 {
+	address			0x097
+	access_mode	RW
+	modes		M_CCHAN
+	field	RXDATABUCKET	0x01
+}
+
+/*
+ * Overlay Split Status 1
+ */
+register OVLYSPLTSTAT1 {
+	address			0x097
+	access_mode	RW
+	modes		M_SCSI
+	field	RXDATABUCKET	0x01
+}
+
+/*
+ * S/G Receive Message 0
+ */
+register SGRXMSG0 {
+	address			0x098
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field		CDNUM	0xF8
+	field		CFNUM	0x07
+}
+
+/*
+ * S/G Receive Message 1
+ */
+register SGRXMSG1 {
+	address			0x099
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	CBNUM		0xFF
+}
+
+/*
+ * S/G Receive Message 2
+ */
+register SGRXMSG2 {
+	address			0x09A
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	MINDEX		0xFF
+}
+
+/*
+ * S/G Receive Message 3
+ */
+register SGRXMSG3 {
+	address			0x09B
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	MCLASS		0x0F
+}
+
+/*
+ * Slave Split Out Address 0
+ */
+register SLVSPLTOUTADR0 {
+	address			0x098
+	access_mode	RO
+	modes		M_SCSI
+	field	LOWER_ADDR	0x7F
+}
+
+/*
+ * Slave Split Out Address 1
+ */
+register SLVSPLTOUTADR1 {
+	address			0x099
+	access_mode	RO
+	modes		M_SCSI
+	field	REQ_DNUM	0xF8
+	field	REQ_FNUM	0x07
+}
+
+/*
+ * Slave Split Out Address 2
+ */
+register SLVSPLTOUTADR2 {
+	address			0x09A
+	access_mode	RO
+	modes		M_SCSI
+	field	REQ_BNUM	0xFF
+}
+
+/*
+ * Slave Split Out Address 3
+ */
+register SLVSPLTOUTADR3 {
+	address			0x09B
+	access_mode	RO
+	modes		M_SCSI
+	field	RLXORD		020
+	field	TAG_NUM		0x1F
+}
+
+/*
+ * SG Sequencer Byte Count
+ */
+register SGSEQBCNT {
+	address			0x09C
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+}
+
+/*
+ * Slave Split Out Attribute 0
+ */
+register SLVSPLTOUTATTR0 {
+	address			0x09C
+	access_mode	RO
+	modes		M_SCSI
+	field	LOWER_BCNT	0xFF
+}
+
+/*
+ * Slave Split Out Attribute 1
+ */
+register SLVSPLTOUTATTR1 {
+	address			0x09D
+	access_mode	RO
+	modes		M_SCSI
+	field	CMPLT_DNUM	0xF8
+	field	CMPLT_FNUM	0x07
+}
+
+/*
+ * Slave Split Out Attribute 2
+ */
+register SLVSPLTOUTATTR2 {
+	address			0x09E
+	access_mode	RO
+	size		2
+	modes		M_SCSI
+	field	CMPLT_BNUM	0xFF
+}
+/*
+ * S/G Split Status 0
+ */
+register SGSPLTSTAT0 {
+	address			0x09E
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	count		2
+	field	STAETERM	0x80
+	field	SCBCERR		0x40
+	field	SCADERR		0x20
+	field	SCDATBUCKET	0x10
+	field	CNTNOTCMPLT	0x08
+	field	RXOVRUN		0x04
+	field	RXSCEMSG	0x02
+	field	RXSPLTRSP	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * S/G Split Status 1
+ */
+register SGSPLTSTAT1 {
+	address			0x09F
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	count		2
+	field	RXDATABUCKET	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+	address			0x09f
+	access_mode	RW
+	modes		M_CFG
+	field	TEST_GROUP	0xF0
+	field	TEST_NUM	0x0F
+	dont_generate_debug_code
+}
+
+/*
+ * Data FIFO 0 PCI Status 
+ */
+register DF0PCISTAT {
+	address			0x0A0
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	field	DPE		0x80
+	field	SSE		0x40
+	field	RMA		0x20
+	field	RTA		0x10
+	field	SCAAPERR	0x08
+	field	RDPERR		0x04
+	field	TWATERR		0x02
+	field	DPR		0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Data FIFO 1 PCI Status 
+ */
+register DF1PCISTAT {
+	address			0x0A1
+	access_mode	RW
+	modes		M_CFG
+	field	DPE		0x80
+	field	SSE		0x40
+	field	RMA		0x20
+	field	RTA		0x10
+	field	SCAAPERR	0x08
+	field	RDPERR		0x04
+	field	TWATERR		0x02
+	field	DPR		0x01
+}
+
+/*
+ * S/G PCI Status 
+ */
+register SGPCISTAT {
+	address			0x0A2
+	access_mode	RW
+	modes		M_CFG
+	field	DPE		0x80
+	field	SSE		0x40
+	field	RMA		0x20
+	field	RTA		0x10
+	field	SCAAPERR	0x08
+	field	RDPERR		0x04
+	field	DPR		0x01
+}
+
+/*
+ * CMC PCI Status 
+ */
+register CMCPCISTAT {
+	address			0x0A3
+	access_mode	RW
+	modes		M_CFG
+	field	DPE		0x80
+	field	SSE		0x40
+	field	RMA		0x20
+	field	RTA		0x10
+	field	SCAAPERR	0x08
+	field	RDPERR		0x04
+	field	TWATERR		0x02
+	field	DPR		0x01
+}
+
+/*
+ * Overlay PCI Status 
+ */
+register OVLYPCISTAT {
+	address			0x0A4
+	access_mode	RW
+	modes		M_CFG
+	field	DPE		0x80
+	field	SSE		0x40
+	field	RMA		0x20
+	field	RTA		0x10
+	field	SCAAPERR	0x08
+	field	RDPERR		0x04
+	field	DPR		0x01
+}
+
+/*
+ * PCI Status for MSI Master DMA Transfer
+ */
+register MSIPCISTAT {
+	address			0x0A6
+	access_mode	RW
+	modes		M_CFG
+	field	SSE		0x40
+	field	RMA		0x20
+	field	RTA		0x10
+	field	CLRPENDMSI	0x08
+	field	TWATERR		0x02
+	field	DPR		0x01
+}
+
+/*
+ * PCI Status for Target
+ */
+register TARGPCISTAT {
+	address			0x0A7
+	access_mode	RW
+	modes		M_CFG
+	count		5
+	field	DPE		0x80
+	field	SSE		0x40
+	field	STA		0x08
+	field	TWATERR		0x02
+	dont_generate_debug_code
+}
+
+/*
+ * LQ Packet In
+ * The last LQ Packet received
+ */
+register LQIN {
+	address			0x020
+	access_mode	RW
+	size		20
+	count		2
+	modes		M_DFF0, M_DFF1, M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * SCB Type Pointer
+ * SCB offset for Target Mode SCB type information
+ */
+register TYPEPTR {
+	address			0x020
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Queue Tag Pointer
+ * SCB offset to the Two Byte tag identifier used for target mode.
+ */
+register TAGPTR {
+	address			0x021
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Logical Unit Number Pointer
+ * SCB offset to the LSB (little endian) of the lun field.
+ */
+register LUNPTR {
+	address			0x022
+	access_mode	RW
+	modes		M_CFG
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * Data Length Pointer
+ * SCB offset for the 4 byte data length field in target mode.
+ */
+register DATALENPTR {
+	address			0x023
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Status Length Pointer
+ * SCB offset to the two byte status field in target SCBs.
+ */
+register STATLENPTR {
+	address			0x024
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Command Length Pointer
+ * Scb offset for the CDB length field in initiator SCBs.
+ */
+register CMDLENPTR {
+	address			0x025
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Task Attribute Pointer
+ * Scb offset for the byte field specifying the attribute byte
+ * to be used in command packets.
+ */ 
+register ATTRPTR {
+	address			0x026
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Task Management Flags Pointer
+ * Scb offset for the byte field specifying the attribute flags
+ * byte to be used in command packets.
+ */ 
+register FLAGPTR {
+	address			0x027
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Command Pointer
+ * Scb offset for the first byte in the CDB for initiator SCBs.
+ */
+register CMDPTR {
+	address			0x028
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Queue Next Pointer
+ * Scb offset for the 2 byte "next scb link".
+ */
+register QNEXTPTR {
+	address			0x029
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI ID Pointer
+ * Scb offset to the value to place in the SCSIID register
+ * during target mode connections.
+ */
+register IDPTR {
+	address			0x02A
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Command Aborted Byte Pointer
+ * Offset to the SCB flags field that includes the
+ * "SCB aborted" status bit.
+ */
+register ABRTBYTEPTR {
+	address			0x02B
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Command Aborted Bit Pointer
+ * Bit offset in the SCB flags field for "SCB aborted" status.
+ */
+register ABRTBITPTR {
+	address			0x02C
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Rev B or greater.
+ */
+register MAXCMDBYTES {
+	address			0x02D
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Rev B or greater.
+ */
+register MAXCMD2RCV {
+	address			0x02E
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Rev B or greater.
+ */
+register SHORTTHRESH {
+	address			0x02F
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * Logical Unit Number Length
+ * The length, in bytes, of the SCB lun field.
+ */
+register LUNLEN {
+	address			0x030
+	access_mode	RW
+	modes		M_CFG
+	count		2
+	mask		ILUNLEN	0x0F
+	mask		TLUNLEN	0xF0
+	dont_generate_debug_code
+}
+const LUNLEN_SINGLE_LEVEL_LUN 0xF
+
+/*
+ * CDB Limit
+ * The size, in bytes, of the embedded CDB field in initator SCBs.
+ */
+register CDBLIMIT {
+	address			0x031
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Maximum Commands
+ * The maximum number of commands to issue during a
+ * single packetized connection.
+ */
+register MAXCMD {
+	address			0x032
+	access_mode	RW
+	modes		M_CFG
+	count		9
+	dont_generate_debug_code
+}
+
+/*
+ * Maximum Command Counter
+ * The number of commands already sent during this connection
+ */
+register MAXCMDCNT {
+	address			0x033
+	access_mode	RW
+	modes		M_CFG
+	dont_generate_debug_code
+}
+
+/*
+ * LQ Packet Reserved Bytes
+ * The bytes to be sent in the currently reserved fileds
+ * of all LQ packets.
+ */
+register LQRSVD01 {
+	address			0x034
+	access_mode	RW
+	modes		M_SCSI
+}
+register LQRSVD16 {
+	address			0x035
+	access_mode	RW
+	modes		M_SCSI
+}
+register LQRSVD17 {
+	address			0x036
+	access_mode	RW
+	modes		M_SCSI
+}
+
+/*
+ * Command Reserved 0
+ * The byte to be sent for the reserved byte 0 of
+ * outgoing command packets.
+ */
+register CMDRSVD0 {
+	address			0x037
+	access_mode	RW
+	modes		M_CFG
+}
+
+/*
+ * LQ Manager Control 0
+ */
+register LQCTL0 {
+	address			0x038
+	access_mode	RW
+	modes		M_CFG
+	field	LQITARGCLT	0xC0
+	field	LQIINITGCLT	0x30
+	field	LQ0TARGCLT	0x0C
+	field	LQ0INITGCLT	0x03
+}
+
+/*
+ * LQ Manager Control 1
+ */
+register LQCTL1 {
+	address			0x038
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		2
+	field	PCI2PCI		0x04
+	field	SINGLECMD	0x02
+	field	ABORTPENDING	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQ Manager Control 2
+ */
+register LQCTL2 {
+	address			0x039
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		5
+	field	LQIRETRY	0x80
+	field	LQICONTINUE	0x40
+	field	LQITOIDLE	0x20
+	field	LQIPAUSE	0x10
+	field	LQORETRY	0x08
+	field	LQOCONTINUE	0x04
+	field	LQOTOIDLE	0x02
+	field	LQOPAUSE	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI RAM BIST0
+ */
+register SCSBIST0 {
+	address			0x039
+	access_mode	RW
+	modes		M_CFG
+	field	GSBISTERR	0x40
+	field	GSBISTDONE	0x20
+	field	GSBISTRUN	0x10
+	field	OSBISTERR	0x04
+	field	OSBISTDONE	0x02
+	field	OSBISTRUN	0x01
+}
+
+/*
+ * SCSI Sequence Control0
+ */
+register SCSISEQ0 {
+	address			0x03A
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	TEMODEO		0x80
+	field	ENSELO		0x40
+	field	ENARBO		0x20
+	field	FORCEBUSFREE	0x10
+	field	SCSIRSTO	0x01
+}
+
+/*
+ * SCSI RAM BIST 1
+ */
+register SCSBIST1 {
+	address			0x03A
+	access_mode	RW
+	modes		M_CFG
+	field	NTBISTERR	0x04
+	field	NTBISTDONE	0x02
+	field	NTBISTRUN	0x01
+}
+
+/*
+ * SCSI Sequence Control 1
+ */
+register SCSISEQ1 {
+	address			0x03B
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		8
+	field	MANUALCTL	0x40
+	field	ENSELI		0x20
+	field	ENRSELI		0x10
+	field	MANUALP		0x0C
+	field	ENAUTOATNP	0x02
+	field	ALTSTIM		0x01
+}
+
+/*
+ * SCSI Transfer Control 0
+ */
+register SXFRCTL0 {
+	address			0x03C
+	access_mode	RW
+	modes		M_SCSI
+	field	DFON		0x80
+	field	DFPEXP		0x40
+	field	BIOSCANCELEN	0x10
+	field	SPIOEN		0x08
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Transfer Control 1
+ */
+register SXFRCTL1 {
+	address			0x03D
+	access_mode	RW
+	modes		M_SCSI
+	field	BITBUCKET	0x80
+	field	ENSACHK		0x40
+	field	ENSPCHK		0x20
+	field	STIMESEL	0x18
+	field	ENSTIMER	0x04
+	field	ACTNEGEN	0x02
+	field	STPWEN		0x01
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Transfer Control 2
+ */
+register SXFRCTL2 {
+	address			0x03E
+	access_mode	RW
+	modes		M_SCSI
+	field	AUTORSTDIS	0x10
+	field	CMDDMAEN	0x08
+	field	ASU		0x07
+}
+
+/*
+ * SCSI Bus Initiator IDs
+ * Bitmask of observed initiators on the bus.
+ */
+register BUSINITID {
+	address			0x03C
+	access_mode	RW
+	modes		M_CFG
+	size		2
+}
+
+/*
+ * Data Length Counters
+ * Packet byte counter.
+ */
+register DLCOUNT {
+	address			0x03C
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	size		3
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFFSTAT {
+	address			0x03F
+	access_mode	RW
+	modes		M_SCSI
+	field	FIFO1FREE	0x20
+	field	FIFO0FREE	0x10
+	/*
+	 * On the B, this enum only works
+	 * in the read direction.  For writes,
+	 * you must use the B version of the
+	 * CURRFIFO_0 definition which is defined
+	 * as a constant outside of this register
+	 * definition to avoid confusing the
+	 * register pretty printing code.
+	 */
+	enum	CURRFIFO	0x03 {
+		CURRFIFO_0,
+		CURRFIFO_1,
+		CURRFIFO_NONE	0x3
+	}
+}
+
+const B_CURRFIFO_0 0x2
+
+/*
+ * SCSI Bus Target IDs
+ * Bitmask of observed targets on the bus.
+ */
+register BUSTARGID {
+	address			0x03E
+	access_mode	RW
+	modes		M_CFG
+	size		2
+}
+
+/*
+ * SCSI Control Signal Out
+ */
+register SCSISIGO {
+	address			0x040
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	CDO		0x80
+	field	IOO		0x40
+	field	MSGO		0x20
+	field	ATNO		0x10
+	field	SELO		0x08
+	field	BSYO		0x04
+	field	REQO		0x02
+	field	ACKO		0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+	enum	PHASE_MASK  CDO|IOO|MSGO {
+		P_DATAOUT	0x0,
+		P_DATAIN	IOO,
+		P_DATAOUT_DT	P_DATAOUT|MSGO,
+		P_DATAIN_DT	P_DATAIN|MSGO,
+		P_COMMAND	CDO,
+		P_MESGOUT	CDO|MSGO,
+		P_STATUS	CDO|IOO,
+		P_MESGIN	CDO|IOO|MSGO
+	}
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Control Signal In
+ */
+register SCSISIGI {
+	address			0x041
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	CDI		0x80
+	field	IOI		0x40
+	field	MSGI		0x20
+	field	ATNI		0x10
+	field	SELI		0x08
+	field	BSYI		0x04
+	field	REQI		0x02
+	field	ACKI		0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+	enum	PHASE_MASK  CDO|IOO|MSGO {
+		P_DATAOUT	0x0,
+		P_DATAIN	IOO,
+		P_DATAOUT_DT	P_DATAOUT|MSGO,
+		P_DATAIN_DT	P_DATAIN|MSGO,
+		P_COMMAND	CDO,
+		P_MESGOUT	CDO|MSGO,
+		P_STATUS	CDO|IOO,
+		P_MESGIN	CDO|IOO|MSGO
+	}
+}
+
+/*
+ * Multiple Target IDs
+ * Bitmask of ids to respond as a target.
+ */
+register MULTARGID {
+	address			0x040
+	access_mode	RW
+	modes		M_CFG
+	size		2
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Phase
+ */
+register SCSIPHASE {
+	address			0x042
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	STATUS_PHASE	0x20
+	field	COMMAND_PHASE	0x10
+	field	MSG_IN_PHASE	0x08
+	field	MSG_OUT_PHASE	0x04
+	field	DATA_PHASE_MASK	0x03 {
+		DATA_OUT_PHASE	0x01,
+		DATA_IN_PHASE	0x02
+	}
+}
+
+/*
+ * SCSI Data 0 Image
+ */
+register SCSIDAT0_IMG {
+	address			0x043
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * SCSI Latched Data
+ */
+register SCSIDAT {
+	address			0x044
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	size		2
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Data Bus
+ */
+register SCSIBUS {
+	address			0x046
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	size		2
+}
+
+/*
+ * Target ID In
+ */
+register TARGIDIN {
+	address			0x048
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		2
+	field	CLKOUT		0x80
+	field	TARGID		0x0F
+	dont_generate_debug_code
+}
+
+/*
+ * Selection/Reselection ID
+ * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+	address			0x049
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	SELID_MASK	0xf0
+	field	ONEBIT		0x08
+}
+
+/*
+ * SCSI Block Control
+ * Controls Bus type and channel selection.  SELWIDE allows for the
+ * coexistence of 8bit and 16bit devices on a wide bus.
+ */
+register SBLKCTL {
+	address			0x04A
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	DIAGLEDEN	0x80
+	field	DIAGLEDON	0x40
+	field	ENAB40		0x08	/* LVD transceiver active */
+	field	ENAB20		0x04	/* SE/HVD transceiver active */
+	field	SELWIDE		0x02
+	dont_generate_debug_code
+}
+
+/*
+ * Option Mode
+ */
+register OPTIONMODE {
+	address			0x04A
+	access_mode	RW
+	modes		M_CFG
+	count		4
+	field	BIOSCANCTL		0x80
+	field	AUTOACKEN		0x40
+	field	BIASCANCTL		0x20
+	field	BUSFREEREV		0x10
+	field	ENDGFORMCHK		0x04
+	field	AUTO_MSGOUT_DE		0x02
+	mask	OPTIONMODE_DEFAULTS	AUTO_MSGOUT_DE
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Status 0
+ */
+register SSTAT0	{
+	address			0x04B
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	TARGET		0x80	/* Board acting as target */
+	field	SELDO		0x40	/* Selection Done */
+	field	SELDI		0x20	/* Board has been selected */
+	field	SELINGO		0x10	/* Selection In Progress */
+	field	IOERR		0x08	/* LVD Tranceiver mode changed */
+	field	OVERRUN		0x04	/* SCSI Offset overrun detected */
+	field	SPIORDY		0x02	/* SCSI PIO Ready */
+	field	ARBDO		0x01	/* Arbitration Done Out */
+}
+
+/*
+ * Clear SCSI Interrupt 0
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+	address			0x04B
+	access_mode	WO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	CLRSELDO	0x40
+	field	CLRSELDI	0x20
+	field	CLRSELINGO	0x10
+	field	CLRIOERR	0x08
+	field	CLROVERRUN	0x04
+	field	CLRSPIORDY	0x02
+	field	CLRARBDO	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Interrupt Mode 0
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+	address			0x04B
+	access_mode	RW
+	modes		M_CFG
+	count		8
+	field	ENSELDO		0x40
+	field	ENSELDI		0x20
+	field	ENSELINGO	0x10
+	field	ENIOERR		0x08
+	field	ENOVERRUN	0x04
+	field	ENSPIORDY	0x02
+	field	ENARBDO		0x01
+}
+
+/*
+ * SCSI Status 1
+ */
+register SSTAT1 {
+	address			0x04C
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	SELTO		0x80
+	field	ATNTARG 	0x40
+	field	SCSIRSTI	0x20
+	field	PHASEMIS	0x10
+	field	BUSFREE		0x08
+	field	SCSIPERR	0x04
+	field	STRB2FAST	0x02
+	field	REQINIT		0x01
+}
+
+/*
+ * Clear SCSI Interrupt 1
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+	address			0x04C
+	access_mode	WO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	CLRSELTIMEO	0x80
+	field	CLRATNO		0x40
+	field	CLRSCSIRSTI	0x20
+	field	CLRBUSFREE	0x08
+	field	CLRSCSIPERR	0x04
+	field	CLRSTRB2FAST	0x02
+	field	CLRREQINIT	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Status 2
+ */
+register SSTAT2 {
+	address			0x04d
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	BUSFREETIME	0xc0 {
+		BUSFREE_LQO	0x40,
+		BUSFREE_DFF0	0x80,
+		BUSFREE_DFF1	0xC0
+	}
+	field	NONPACKREQ	0x20
+	field	EXP_ACTIVE	0x10	/* SCSI Expander Active */
+	field	BSYX		0x08	/* Busy Expander */
+	field	WIDE_RES	0x04	/* Modes 0 and 1 only */
+	field	SDONE		0x02	/* Modes 0 and 1 only */
+	field	DMADONE		0x01	/* Modes 0 and 1 only */
+}
+
+/*
+ * Clear SCSI Interrupt 2
+ */
+register CLRSINT2 {
+	address			0x04D
+	access_mode	WO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	CLRNONPACKREQ	0x20
+	field	CLRWIDE_RES	0x04	/* Modes 0 and 1 only */
+	field	CLRSDONE	0x02	/* Modes 0 and 1 only */
+	field	CLRDMADONE	0x01	/* Modes 0 and 1 only */
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Interrupt Mode 2
+ */
+register SIMODE2 {
+	address			0x04D
+	access_mode	RW
+	modes		M_CFG
+	field	ENWIDE_RES	0x04
+	field	ENSDONE		0x02
+	field	ENDMADONE	0x01
+}
+
+/*
+ * Physical Error Diagnosis
+ */
+register PERRDIAG {
+	address			0x04E
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		3
+	field	HIZERO		0x80
+	field	HIPERR		0x40
+	field	PREVPHASE	0x20
+	field	PARITYERR	0x10
+	field	AIPERR		0x08
+	field	CRCERR		0x04
+	field	DGFORMERR	0x02
+	field	DTERR		0x01
+}
+
+/*
+ * LQI Manager Current State
+ */
+register LQISTATE {
+	address			0x04E
+	access_mode	RO
+	modes		M_CFG
+	count		6
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Offset Count
+ */
+register SOFFCNT {
+	address			0x04F
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		1
+}
+
+/*
+ * LQO Manager Current State
+ */
+register LQOSTATE {
+	address			0x04F
+	access_mode	RO
+	modes		M_CFG
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * LQI Manager Status
+ */
+register LQISTAT0 {
+	address			0x050
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		2
+	field	LQIATNQAS	0x20
+	field	LQICRCT1	0x10
+	field	LQICRCT2	0x08
+	field	LQIBADLQT	0x04
+	field	LQIATNLQ	0x02
+	field	LQIATNCMD	0x01
+}
+
+/*
+ * Clear LQI Interrupts 0
+ */
+register CLRLQIINT0 {
+	address			0x050
+	access_mode	WO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		1
+	field	CLRLQIATNQAS	0x20
+	field	CLRLQICRCT1	0x10
+	field	CLRLQICRCT2	0x08
+	field	CLRLQIBADLQT	0x04
+	field	CLRLQIATNLQ	0x02
+	field	CLRLQIATNCMD	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQI Manager Interrupt Mode 0
+ */
+register LQIMODE0 {
+	address			0x050
+	access_mode	RW
+	modes		M_CFG
+	count		3
+	field	ENLQIATNQASK	0x20
+	field	ENLQICRCT1	0x10
+	field	ENLQICRCT2	0x08
+	field	ENLQIBADLQT	0x04
+	field	ENLQIATNLQ	0x02
+	field	ENLQIATNCMD	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQI Manager Status 1
+ */
+register LQISTAT1 {
+	address			0x051
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		3
+	field	LQIPHASE_LQ	0x80
+	field	LQIPHASE_NLQ	0x40
+	field	LQIABORT	0x20
+	field	LQICRCI_LQ	0x10
+	field	LQICRCI_NLQ	0x08
+	field	LQIBADLQI	0x04
+	field	LQIOVERI_LQ	0x02
+	field	LQIOVERI_NLQ	0x01
+}
+
+/*
+ * Clear LQI Manager Interrupts1
+ */
+register CLRLQIINT1 {
+	address			0x051
+	access_mode	WO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		4
+	field	CLRLQIPHASE_LQ	0x80
+	field	CLRLQIPHASE_NLQ	0x40
+	field	CLRLIQABORT	0x20
+	field	CLRLQICRCI_LQ	0x10
+	field	CLRLQICRCI_NLQ	0x08
+	field	CLRLQIBADLQI	0x04
+	field	CLRLQIOVERI_LQ	0x02
+	field	CLRLQIOVERI_NLQ	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQI Manager Interrupt Mode 1
+ */
+register LQIMODE1 {
+	address			0x051
+	access_mode	RW
+	modes		M_CFG
+	count		4
+	field	ENLQIPHASE_LQ	0x80	/* LQIPHASE1 */
+	field	ENLQIPHASE_NLQ	0x40	/* LQIPHASE2 */
+	field	ENLIQABORT	0x20
+	field	ENLQICRCI_LQ	0x10	/* LQICRCI1 */
+	field	ENLQICRCI_NLQ	0x08	/* LQICRCI2 */
+	field	ENLQIBADLQI	0x04
+	field	ENLQIOVERI_LQ	0x02	/* LQIOVERI1 */
+	field	ENLQIOVERI_NLQ	0x01	/* LQIOVERI2 */
+	dont_generate_debug_code
+}
+
+/*
+ * LQI Manager Status 2
+ */
+register LQISTAT2 {
+	address			0x052
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	PACKETIZED	0x80
+	field	LQIPHASE_OUTPKT	0x40
+	field	LQIWORKONLQ	0x20
+	field	LQIWAITFIFO	0x10
+	field	LQISTOPPKT	0x08
+	field	LQISTOPLQ	0x04
+	field	LQISTOPCMD	0x02
+	field	LQIGSAVAIL	0x01
+}
+
+/*
+ * SCSI Status 3
+ */
+register SSTAT3 {
+	address			0x053
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		3
+	field	NTRAMPERR	0x02
+	field	OSRAMPERR	0x01
+}
+
+/*
+ * Clear SCSI Status 3
+ */
+register CLRSINT3 {
+	address			0x053
+	access_mode	WO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		3
+	field	CLRNTRAMPERR	0x02
+	field	CLROSRAMPERR	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Interrupt Mode 3
+ */
+register SIMODE3 {
+	address			0x053
+	access_mode	RW
+	modes		M_CFG
+	count		4
+	field	ENNTRAMPERR	0x02
+	field	ENOSRAMPERR	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQO Manager Status 0
+ */
+register LQOSTAT0 {
+	address			0x054
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		2
+	field	LQOTARGSCBPERR	0x10
+	field	LQOSTOPT2	0x08
+	field	LQOATNLQ	0x04
+	field	LQOATNPKT	0x02
+	field	LQOTCRC		0x01
+}
+
+/*
+ * Clear LQO Manager interrupt 0
+ */
+register CLRLQOINT0 {
+	address			0x054
+	access_mode	WO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		3
+	field	CLRLQOTARGSCBPERR	0x10
+	field	CLRLQOSTOPT2		0x08
+	field	CLRLQOATNLQ		0x04
+	field	CLRLQOATNPKT		0x02
+	field	CLRLQOTCRC		0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQO Manager Interrupt Mode 0
+ */
+register LQOMODE0 {
+	address			0x054
+	access_mode	RW
+	modes		M_CFG
+	count		4
+	field	ENLQOTARGSCBPERR	0x10
+	field	ENLQOSTOPT2		0x08
+	field	ENLQOATNLQ		0x04
+	field	ENLQOATNPKT		0x02
+	field	ENLQOTCRC		0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQO Manager Status 1
+ */
+register LQOSTAT1 {
+	address			0x055
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	LQOINITSCBPERR	0x10
+	field	LQOSTOPI2	0x08
+	field	LQOBADQAS	0x04
+	field	LQOBUSFREE	0x02
+	field	LQOPHACHGINPKT	0x01
+}
+
+/*
+ * Clear LOQ Interrupt 1
+ */
+register CLRLQOINT1 {
+	address			0x055
+	access_mode	WO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	count		7
+	field	CLRLQOINITSCBPERR	0x10
+	field	CLRLQOSTOPI2		0x08
+	field	CLRLQOBADQAS		0x04
+	field	CLRLQOBUSFREE		0x02
+	field	CLRLQOPHACHGINPKT	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQO Manager Interrupt Mode 1
+ */
+register LQOMODE1 {
+	address			0x055
+	access_mode	RW
+	modes		M_CFG
+	count		4
+	field	ENLQOINITSCBPERR	0x10
+	field	ENLQOSTOPI2		0x08
+	field	ENLQOBADQAS		0x04
+	field	ENLQOBUSFREE		0x02
+	field	ENLQOPHACHGINPKT	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * LQO Manager Status 2
+ */
+register LQOSTAT2 {
+	address			0x056
+	access_mode	RO
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	LQOPKT		0xE0
+	field	LQOWAITFIFO	0x10
+	field	LQOPHACHGOUTPKT	0x02	/* outside of packet boundaries. */
+	field	LQOSTOP0	0x01	/* Stopped after sending all packets */
+}
+
+/*
+ * Output Synchronizer Space Count
+ */
+register OS_SPACE_CNT {
+	address			0x056
+	access_mode	RO
+	modes		M_CFG
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Interrupt Mode 1
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+	address			0x057
+	access_mode	RW
+	modes		M_DFF0, M_DFF1, M_SCSI
+	field	ENSELTIMO	0x80
+	field	ENATNTARG	0x40
+	field	ENSCSIRST	0x20
+	field	ENPHASEMIS	0x10
+	field	ENBUSFREE	0x08
+	field	ENSCSIPERR	0x04
+	field	ENSTRB2FAST	0x02
+	field	ENREQINIT	0x01
+}
+
+/*
+ * Good Status FIFO
+ */
+register GSFIFO {
+	address			0x058
+	access_mode	RO
+	size		2
+	modes		M_DFF0, M_DFF1, M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * Data FIFO SCSI Transfer Control
+ */
+register DFFSXFRCTL {
+	address			0x05A
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	field	DFFBITBUCKET	0x08
+	field	CLRSHCNT	0x04
+	field	CLRCHN		0x02
+	field	RSTCHN		0x01
+}
+
+/*
+ * Next SCSI Control Block
+ */
+register NEXTSCB {
+	address			0x05A
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * LQO SCSI Control
+ * (Rev B only.)
+ */
+register LQOSCSCTL {
+	address			0x05A
+	access_mode	RW
+	size		1
+	modes		M_CFG
+	count		1
+	field		LQOH2A_VERSION	0x80
+	field		LQOBUSETDLY	0x40
+	field		LQONOHOLDLACK	0x02
+	field		LQONOCHKOVER	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * SEQ Interrupts
+ */
+register SEQINTSRC {
+	address			0x05B
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	CTXTDONE	0x40
+	field	SAVEPTRS	0x20
+	field	CFG4DATA	0x10
+	field	CFG4ISTAT	0x08
+	field	CFG4TSTAT	0x04
+	field	CFG4ICMD	0x02
+	field	CFG4TCMD	0x01
+}
+
+/*
+ * Clear Arp Interrupts
+ */
+register CLRSEQINTSRC {
+	address			0x05B
+	access_mode	WO
+	modes		M_DFF0, M_DFF1
+	field	CLRCTXTDONE	0x40
+	field	CLRSAVEPTRS	0x20
+	field	CLRCFG4DATA	0x10
+	field	CLRCFG4ISTAT	0x08
+	field	CLRCFG4TSTAT	0x04
+	field	CLRCFG4ICMD	0x02
+	field	CLRCFG4TCMD	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * SEQ Interrupt Enabled (Shared)
+ */
+register SEQIMODE {
+	address			0x05C
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	field	ENCTXTDONE	0x40
+	field	ENSAVEPTRS	0x20
+	field	ENCFG4DATA	0x10
+	field	ENCFG4ISTAT	0x08
+	field	ENCFG4TSTAT	0x04
+	field	ENCFG4ICMD	0x02
+	field	ENCFG4TCMD	0x01
+}
+
+/*
+ * Current SCSI Control Block
+ */
+register CURRSCB {
+	address			0x05C
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * Data FIFO Status
+ */
+register MDFFSTAT {
+	address			0x05D
+	access_mode	RO
+	modes		M_DFF0, M_DFF1
+	field	SHCNTNEGATIVE	0x40 /* Rev B or higher */
+	field	SHCNTMINUS1	0x20 /* Rev B or higher */
+	field	LASTSDONE	0x10
+	field	SHVALID		0x08
+	field	DLZERO		0x04 /* FIFO data ends on packet boundary. */
+	field	DATAINFIFO	0x02
+	field	FIFOFREE	0x01
+}
+
+/*
+ * CRC Control
+ */
+register CRCCONTROL {
+	address			0x05d
+	access_mode	RW
+	modes		M_CFG
+	field	CRCVALCHKEN		0x40
+}
+
+/*
+ * SCSI Test Control
+ */
+register SCSITEST {
+	address			0x05E
+	access_mode	RW
+	modes		M_CFG
+	field	CNTRTEST	0x08
+	field	SEL_TXPLL_DEBUG	0x04
+}
+
+/*
+ * Data FIFO Queue Tag
+ */
+register DFFTAG {
+	address			0x05E
+	access_mode	RW
+	size		2
+	modes		M_DFF0, M_DFF1
+}
+
+/*
+ * Last SCSI Control Block
+ */
+register LASTSCB {
+	address			0x05E
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI I/O Cell Power-down Control
+ */
+register IOPDNCTL {
+	address			0x05F
+	access_mode	RW
+	modes		M_CFG
+	field	DISABLE_OE	0x80
+	field	PDN_IDIST	0x04
+	field	PDN_DIFFSENSE	0x01
+}
+
+/*
+ * Shadow Host Address.
+ */
+register SHADDR {
+	address			0x060
+	access_mode	RO
+	size		8
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * Data Group CRC Interval.
+ */
+register DGRPCRCI {
+	address			0x060
+	access_mode	RW
+	size		2
+	modes		M_CFG
+}
+
+/*
+ * Data Transfer Negotiation Address
+ */
+register NEGOADDR {
+	address			0x060
+	access_mode	RW
+	modes		M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * Data Transfer Negotiation Data - Period Byte
+ */
+register NEGPERIOD {
+	address			0x061
+	access_mode	RW
+	modes		M_SCSI
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Packetized CRC Interval
+ */
+register PACKCRCI {
+	address			0x062
+	access_mode	RW
+	size		2
+	modes		M_CFG
+}
+
+/*
+ * Data Transfer Negotiation Data - Offset Byte
+ */
+register NEGOFFSET {
+	address			0x062
+	access_mode	RW
+	modes		M_SCSI
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Data Transfer Negotiation Data - PPR Options
+ */
+register NEGPPROPTS {
+	address			0x063
+	access_mode	RW
+	modes		M_SCSI
+	count		1
+	field	PPROPT_PACE	0x08
+	field	PPROPT_QAS	0x04
+	field	PPROPT_DT	0x02
+	field	PPROPT_IUT	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Data Transfer Negotiation Data -  Connection Options
+ */
+register NEGCONOPTS {
+	address			0x064
+	access_mode	RW
+	modes		M_SCSI
+	field	ENSNAPSHOT	0x40
+	field	RTI_WRTDIS	0x20
+	field	RTI_OVRDTRN	0x10
+	field	ENSLOWCRC	0x08
+	field	ENAUTOATNI	0x04
+	field	ENAUTOATNO	0x02
+	field	WIDEXFER	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Negotiation Table Annex Column Index.
+ */
+register ANNEXCOL {
+	address			0x065
+	access_mode	RW
+	modes		M_SCSI
+	count		7
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Check
+ * (Rev. B only)
+ */
+register SCSCHKN {
+	address			0x066
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	field	BIDICHKDIS	0x80
+	field	STSELSKIDDIS	0x40
+	field	CURRFIFODEF	0x20
+	field	WIDERESEN	0x10
+	field	SDONEMSKDIS	0x08
+	field	DFFACTCLR	0x04
+	field	SHVALIDSTDIS	0x02
+	field	LSTSGCLRDIS	0x01
+	dont_generate_debug_code
+}
+
+const AHD_ANNEXCOL_PER_DEV0	4
+const AHD_NUM_PER_DEV_ANNEXCOLS	4
+const AHD_ANNEXCOL_PRECOMP_SLEW	4
+const	AHD_PRECOMP_MASK	0x07
+const	AHD_PRECOMP_SHIFT	0
+const	AHD_PRECOMP_CUTBACK_17	0x04
+const	AHD_PRECOMP_CUTBACK_29	0x06
+const	AHD_PRECOMP_CUTBACK_37	0x07
+const	AHD_SLEWRATE_MASK	0x78
+const	AHD_SLEWRATE_SHIFT	3
+/*
+ * Rev A has only a single bit (high bit of field) of slew adjustment.
+ * Rev B has 4 bits.  The current default happens to be the same for both.
+ */
+const	AHD_SLEWRATE_DEF_REVA	0x08
+const	AHD_SLEWRATE_DEF_REVB	0x08
+
+/* Rev A does not have any amplitude setting. */
+const AHD_ANNEXCOL_AMPLITUDE	6
+const	AHD_AMPLITUDE_MASK	0x7
+const	AHD_AMPLITUDE_SHIFT	0
+const	AHD_AMPLITUDE_DEF	0x7
+
+/*
+ * Negotiation Table Annex Data Port.
+ */
+register ANNEXDAT {
+	address			0x066
+	access_mode	RW
+	modes		M_SCSI
+	count		3
+	dont_generate_debug_code
+}
+
+/*
+ * Initiator's Own Id.
+ * The SCSI ID to use for Selection Out and seen during a reselection..
+ */
+register IOWNID {
+	address			0x067
+	access_mode	RW
+	modes		M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * 960MHz Phase-Locked Loop Control 0
+ */
+register PLL960CTL0 {
+	address			0x068
+	access_mode	RW
+	modes		M_CFG
+	field	PLL_VCOSEL	0x80
+	field	PLL_PWDN	0x40
+	field	PLL_NS		0x30
+	field	PLL_ENLUD	0x08
+	field	PLL_ENLPF	0x04
+	field	PLL_DLPF	0x02
+	field	PLL_ENFBM	0x01
+}
+
+/*
+ * Target Own Id
+ */
+register TOWNID {
+	address			0x069
+	access_mode	RW
+	modes		M_SCSI
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * 960MHz Phase-Locked Loop Control 1
+ */
+register PLL960CTL1 {
+	address			0x069
+	access_mode	RW
+	modes		M_CFG
+	field	PLL_CNTEN	0x80
+	field	PLL_CNTCLR	0x40
+	field	PLL_RST		0x01
+}
+
+/*
+ * Expander Signature
+ */
+register XSIG {
+	address			0x06A
+	access_mode	RW
+	modes		M_SCSI
+}
+
+/*
+ * Shadow Byte Count
+ */
+register SHCNT {
+	address			0x068
+	access_mode	RW
+	size		3
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * Selection Out ID
+ */
+register SELOID {
+	address			0x06B
+	access_mode	RW
+	modes		M_SCSI
+}
+
+/*
+ * 960-MHz Phase-Locked Loop Test Count
+ */
+register PLL960CNT0 {
+	address			0x06A
+	access_mode	RO
+	size		2
+	modes		M_CFG
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Control 0
+ */
+register PLL400CTL0 {
+	address			0x06C
+	access_mode	RW
+	modes		M_CFG
+	field	PLL_VCOSEL	0x80
+	field	PLL_PWDN	0x40
+	field	PLL_NS		0x30
+	field	PLL_ENLUD	0x08
+	field	PLL_ENLPF	0x04
+	field	PLL_DLPF	0x02
+	field	PLL_ENFBM	0x01
+}
+
+/*
+ * Arbitration Fairness
+ */
+register FAIRNESS {
+	address			0x06C
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Control 1
+ */
+register PLL400CTL1 {
+	address			0x06D
+	access_mode	RW
+	modes		M_CFG
+	field	PLL_CNTEN	0x80
+	field	PLL_CNTCLR	0x40
+	field	PLL_RST		0x01
+}
+
+/*
+ * Arbitration Unfairness
+ */
+register UNFAIRNESS {
+	address			0x06E
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Test Count
+ */
+register PLL400CNT0 {
+	address			0x06E
+	access_mode	RO
+	size		2
+	modes		M_CFG
+}
+
+/*
+ * SCB Page Pointer
+ */
+register SCBPTR {
+	address			0x0A8
+	access_mode	RW
+	size		2
+	modes		M_DFF0, M_DFF1, M_CCHAN, M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * CMC SCB Array Count
+ * Number of bytes to transfer between CMC SCB memory and SCBRAM.
+ * Transfers must be 8byte aligned and sized.
+ */
+register CCSCBACNT {
+	address			0x0AB
+	access_mode	RW
+	modes		M_CCHAN
+}
+
+/*
+ * SCB Autopointer
+ * SCB-Next Address Snooping logic.  When an SCB is transferred to
+ * the card, the next SCB address to be used by the CMC array can
+ * be autoloaded from that transfer.
+ */
+register SCBAUTOPTR {
+	address			0x0AB
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	field	AUSCBPTR_EN	0x80
+	field	SCBPTR_ADDR	0x38
+	field	SCBPTR_OFF	0x07
+	dont_generate_debug_code
+}
+
+/*
+ * CMC SG Ram Address Pointer
+ */
+register CCSGADDR {
+	address			0x0AC
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * CMC SCB RAM Address Pointer
+ */
+register CCSCBADDR {
+	address			0x0AC
+	access_mode	RW
+	modes		M_CCHAN
+	dont_generate_debug_code
+}
+
+/*
+ * CMC SCB Ram Back-up Address Pointer
+ * Indicates the true stop location of transfers halted prior
+ * to SCBHCNT going to 0.
+ */
+register CCSCBADR_BK {
+	address			0x0AC
+	access_mode	RO
+	modes		M_CFG
+}
+
+/*
+ * CMC SG Control
+ */
+register CCSGCTL {
+	address			0x0AD
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	field	CCSGDONE	0x80
+	field	SG_CACHE_AVAIL	0x10
+	field	CCSGENACK	0x08
+	mask	CCSGEN		0x0C
+	field	SG_FETCH_REQ	0x02
+	field	CCSGRESET	0x01
+}
+
+/*
+ * CMD SCB Control
+ */
+register CCSCBCTL {
+	address			0x0AD
+	access_mode	RW
+	modes		M_CCHAN
+	field	CCSCBDONE	0x80
+	field	ARRDONE		0x40
+	field	CCARREN		0x10
+	field	CCSCBEN		0x08
+	field	CCSCBDIR	0x04
+	field	CCSCBRESET	0x01
+}
+
+/*
+ * CMC Ram BIST
+ */
+register CMC_RAMBIST {
+	address			0x0AD
+	access_mode	RW
+	modes		M_CFG
+	field	SG_ELEMENT_SIZE		0x80
+	field	SCBRAMBIST_FAIL		0x40
+	field	SG_BIST_FAIL		0x20
+	field	SG_BIST_EN		0x10
+	field	CMC_BUFFER_BIST_FAIL	0x02
+	field	CMC_BUFFER_BIST_EN	0x01
+}
+
+/*
+ * CMC SG RAM Data Port
+ */
+register CCSGRAM {
+	address			0x0B0
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * CMC SCB RAM Data Port
+ */
+register CCSCBRAM {
+	address			0x0B0
+	access_mode	RW
+	modes		M_CCHAN
+	dont_generate_debug_code
+}
+
+/*
+ * Flex DMA Address.
+ */
+register FLEXADR {
+	address			0x0B0
+	access_mode	RW
+	size		3
+	modes		M_SCSI
+}
+
+/*
+ * Flex DMA Byte Count
+ */
+register FLEXCNT {
+	address			0x0B3
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+}
+
+/*
+ * Flex DMA Status
+ */
+register FLEXDMASTAT {
+	address			0x0B5
+	access_mode	RW
+	modes		M_SCSI
+	field	FLEXDMAERR	0x02
+	field	FLEXDMADONE	0x01
+}
+
+/*
+ * Flex DMA Data Port
+ */
+register FLEXDATA {
+	address			0x0B6
+	access_mode	RW
+	modes		M_SCSI
+}
+
+/*
+ * Board Data
+ */
+register BRDDAT {
+	address			0x0B8
+	access_mode	RW
+	modes		M_SCSI
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * Board Control
+ */
+register BRDCTL {
+	address			0x0B9
+	access_mode	RW
+	modes		M_SCSI
+	count		7
+	field	FLXARBACK	0x80
+	field	FLXARBREQ	0x40
+	field	BRDADDR		0x38
+	field	BRDEN		0x04
+	field	BRDRW		0x02
+	field	BRDSTB		0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Serial EEPROM Address
+ */
+register SEEADR {
+	address			0x0BA
+	access_mode	RW
+	modes		M_SCSI
+	count		4
+	dont_generate_debug_code
+}
+
+/*
+ * Serial EEPROM Data
+ */
+register SEEDAT {
+	address			0x0BC
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+	count		4
+	dont_generate_debug_code
+}
+
+/*
+ * Serial EEPROM Status
+ */
+register SEESTAT {
+	address			0x0BE
+	access_mode	RO
+	modes		M_SCSI
+	count		1
+	field	INIT_DONE	0x80
+	field	SEEOPCODE	0x70
+	field	LDALTID_L	0x08
+	field	SEEARBACK	0x04
+	field	SEEBUSY		0x02
+	field	SEESTART	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Serial EEPROM Control
+ */
+register SEECTL {
+	address			0x0BE
+	access_mode	RW
+	modes		M_SCSI
+	count		4
+	field	SEEOPCODE	0x70 {
+		SEEOP_ERASE	0x70,
+		SEEOP_READ	0x60,
+		SEEOP_WRITE	0x50,
+	/*
+	 * The following four commands use special
+	 * addresses for differentiation.
+	 */
+		SEEOP_ERAL	0x40
+	}
+	mask	SEEOP_EWEN	0x40
+	mask	SEEOP_WALL	0x40
+	mask	SEEOP_EWDS	0x40
+	field	SEERST		0x02
+	field	SEESTART	0x01
+	dont_generate_debug_code
+}
+
+const SEEOP_ERAL_ADDR	0x80
+const SEEOP_EWEN_ADDR	0xC0
+const SEEOP_WRAL_ADDR	0x40
+const SEEOP_EWDS_ADDR	0x00
+
+/*
+ * SCB Counter
+ */
+register SCBCNT {
+	address			0x0BF
+	access_mode	RW
+	modes		M_SCSI
+	dont_generate_debug_code
+}
+
+/*
+ * Data FIFO Write Address
+ * Pointer to the next QWD location to be written to the data FIFO.
+ */
+register DFWADDR {
+	address			0x0C0
+	access_mode	RW
+	size		2
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * DSP Filter Control
+ */
+register DSPFLTRCTL {
+	address			0x0C0
+	access_mode	RW
+	modes		M_CFG
+	field	FLTRDISABLE	0x20
+	field	EDGESENSE	0x10
+	field	DSPFCNTSEL	0x0F
+}
+
+/*
+ * DSP Data Channel Control
+ */
+register DSPDATACTL {
+	address			0x0C1
+	access_mode	RW
+	modes		M_CFG
+	count		3
+	field	BYPASSENAB	0x80
+	field	DESQDIS		0x10
+	field	RCVROFFSTDIS	0x04
+	field	XMITOFFSTDIS	0x02
+	dont_generate_debug_code
+}
+
+/*
+ * Data FIFO Read Address
+ * Pointer to the next QWD location to be read from the data FIFO.
+ */
+register DFRADDR {
+	address			0x0C2
+	access_mode	RW
+	size		2
+	modes		M_DFF0, M_DFF1
+}
+
+/*
+ * DSP REQ Control
+ */
+register DSPREQCTL {
+	address			0x0C2
+	access_mode	RW
+	modes		M_CFG
+	field	MANREQCTL	0xC0
+	field	MANREQDLY	0x3F
+}
+
+/*
+ * DSP ACK Control
+ */
+register DSPACKCTL {
+	address			0x0C3
+	access_mode	RW
+	modes		M_CFG
+	field	MANACKCTL	0xC0
+	field	MANACKDLY	0x3F
+}
+
+/*
+ * Data FIFO Data
+ * Read/Write byte port into the data FIFO.  The read and write
+ * FIFO pointers increment with each read and write respectively
+ * to this port.
+ */
+register DFDAT {
+	address			0x0C4
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	dont_generate_debug_code
+}
+
+/*
+ * DSP Channel Select
+ */
+register DSPSELECT {
+	address			0x0C4
+	access_mode	RW
+	modes		M_CFG
+	count		1
+	field	AUTOINCEN	0x80
+	field	DSPSEL		0x1F
+	dont_generate_debug_code
+}
+
+const NUMDSPS 0x14
+
+/*
+ * Write Bias Control
+ */
+register WRTBIASCTL {
+	address			0x0C5
+	access_mode	WO
+	modes		M_CFG
+	count		3
+	field	AUTOXBCDIS	0x80
+	field	XMITMANVAL	0x3F
+	dont_generate_debug_code
+}
+
+/*
+ * Currently the WRTBIASCTL is the same as the default.
+ */
+const WRTBIASCTL_HP_DEFAULT 0x0
+
+/*
+ * Receiver Bias Control
+ */
+register RCVRBIOSCTL {
+	address			0x0C6
+	access_mode	WO
+	modes		M_CFG
+	field	AUTORBCDIS	0x80
+	field	RCVRMANVAL	0x3F
+}
+
+/*
+ * Write Bias Calculator
+ */
+register WRTBIASCALC {
+	address			0x0C7
+	access_mode	RO
+	modes		M_CFG
+}
+
+/*
+ * Data FIFO Pointers
+ * Contains the byte offset from DFWADDR and DWRADDR to the current
+ * FIFO write/read locations.
+ */
+register DFPTRS {
+	address			0x0C8
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+}
+
+/*
+ * Receiver Bias Calculator
+ */
+register RCVRBIASCALC {
+	address			0x0C8
+	access_mode	RO
+	modes		M_CFG
+}
+
+/*
+ * Data FIFO Backup Read Pointer
+ * Contains the data FIFO address to be restored if the last
+ * data accessed from the data FIFO was not transferred successfully.
+ */
+register DFBKPTR {
+	address			0x0C9
+	access_mode	RW
+	size		2
+	modes		M_DFF0, M_DFF1
+}
+
+/*
+ * Skew Calculator
+ */
+register SKEWCALC {
+	address			0x0C9
+	access_mode	RO
+	modes		M_CFG
+}
+
+/*
+ * Data FIFO Debug Control
+ */
+register DFDBCTL {
+	address				0x0CB
+	access_mode	RW
+	modes		M_DFF0, M_DFF1
+	field	DFF_CIO_WR_RDY		0x20
+	field	DFF_CIO_RD_RDY		0x10
+	field	DFF_DIR_ERR		0x08
+	field	DFF_RAMBIST_FAIL	0x04
+	field	DFF_RAMBIST_DONE	0x02
+	field	DFF_RAMBIST_EN		0x01
+}
+
+/*
+ * Data FIFO Space Count
+ * Number of FIFO locations that are free.
+ */
+register DFSCNT {
+	address			0x0CC
+	access_mode	RO
+	size		2
+	modes		M_DFF0, M_DFF1
+}
+
+/*
+ * Data FIFO Byte Count
+ * Number of filled FIFO locations.
+ */
+register DFBCNT {
+	address			0x0CE
+	access_mode	RO
+	size		2
+	modes		M_DFF0, M_DFF1
+}
+
+/*
+ * Sequencer Program Overlay Address.
+ * Low address must be written prior to high address.
+ */
+register OVLYADDR {
+	address			0x0D4
+	modes		M_SCSI
+	size		2
+	access_mode	RW
+}
+
+/*
+ * Sequencer Control 0
+ * Error detection mode, speed configuration,
+ * single step, breakpoints and program load.
+ */
+register SEQCTL0 {
+	address			0x0D6
+	access_mode	RW
+	count		11
+	field	PERRORDIS	0x80
+	field	PAUSEDIS	0x40
+	field	FAILDIS		0x20
+	field	FASTMODE	0x10
+	field	BRKADRINTEN	0x08
+	field	STEP		0x04
+	field	SEQRESET	0x02
+	field	LOADRAM		0x01
+}
+
+/*
+ * Sequencer Control 1
+ * Instruction RAM Diagnostics
+ */
+register SEQCTL1 {
+	address			0x0D7
+	access_mode RW
+	field	OVRLAY_DATA_CHK	0x08
+	field	RAMBIST_DONE	0x04
+	field	RAMBIST_FAIL	0x02
+	field	RAMBIST_EN	0x01
+}
+
+/*
+ * Sequencer Flags
+ * Zero and Carry state of the ALU.
+ */
+register FLAGS {
+	address			0x0D8
+	access_mode 	RO
+	count		23
+	field	ZERO		0x02
+	field	CARRY		0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Sequencer Interrupt Control
+ */ 
+register SEQINTCTL {
+	address			0x0D9
+	access_mode RW
+	field	INTVEC1DSL	0x80
+	field	INT1_CONTEXT	0x20
+	field	SCS_SEQ_INT1M1	0x10
+	field	SCS_SEQ_INT1M0	0x08
+	field	INTMASK2	0x04
+	field	INTMASK1	0x02
+	field	IRET		0x01
+}
+
+/*
+ * Sequencer RAM Data Port
+ * Single byte window into the Sequencer Instruction Ram area starting
+ * at the address specified by OVLYADDR.  To write a full instruction word,
+ * simply write four bytes in succession.  OVLYADDR will increment after the
+ * most significant instrution byte (the byte with the parity bit) is written.
+ */
+register SEQRAM {
+	address			0x0DA
+	access_mode 	RW
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * Sequencer Program Counter
+ * Low byte must be written prior to high byte.
+ */
+register PRGMCNT {
+	address			0x0DE
+	access_mode	RW
+	size		2
+	count		5
+	dont_generate_debug_code
+}
+
+/*
+ * Accumulator
+ */
+register ACCUM {
+	address			0x0E0
+	access_mode 	RW
+	accumulator
+	dont_generate_debug_code
+}
+
+/*
+ * Source Index Register
+ * Incrementing index for reads of SINDIR and the destination (low byte only)
+ * for any immediate operands passed in jmp, jc, jnc, call instructions.
+ * Example:
+ *		mvi	0xFF	call some_routine;
+ *
+ *  Will set SINDEX[0] to 0xFF and call the routine "some_routine.
+ */
+register SINDEX	{
+	address			0x0E2
+	access_mode	RW
+	size		2
+	sindex
+	dont_generate_debug_code
+}
+
+/*
+ * Destination Index Register
+ * Incrementing index for writes to DINDIR.  Can be used as a scratch register.
+ */
+register DINDEX {
+	address			0x0E4
+	access_mode	RW
+	size		2
+	dont_generate_debug_code
+}
+
+/*
+ * Break Address
+ * Sequencer instruction breakpoint address address.
+ */
+register BRKADDR0 {
+	address			0x0E6
+	access_mode	RW
+}
+
+register BRKADDR1 {
+	address			0x0E6
+	access_mode	RW
+	field	BRKDIS		0x80	/* Disable Breakpoint */
+}
+
+/*
+ * All Ones
+ * All reads to this register return the value 0xFF.
+ */
+register ALLONES {
+	address			0x0E8
+	access_mode RO
+	allones
+	dont_generate_debug_code
+}
+
+/*
+ * All Zeros
+ * All reads to this register return the value 0.
+ */
+register ALLZEROS {
+	address			0x0EA
+	access_mode RO
+	allzeros
+	dont_generate_debug_code
+}
+
+/*
+ * No Destination
+ * Writes to this register have no effect.
+ */
+register NONE {
+	address			0x0EA
+	access_mode WO
+	none
+	dont_generate_debug_code
+}
+
+/*
+ * Source Index Indirect
+ * Reading this register is equivalent to reading (register_base + SINDEX) and
+ * incrementing SINDEX by 1.
+ */
+register SINDIR	{
+	address			0x0EC
+	access_mode RO
+	dont_generate_debug_code
+}
+
+/*
+ * Destination Index Indirect
+ * Writing this register is equivalent to writing to (register_base + DINDEX)
+ * and incrementing DINDEX by 1.
+ */
+register DINDIR	 {
+	address			0x0ED
+	access_mode WO
+	dont_generate_debug_code
+}
+
+/*
+ * Function One
+ * 2's complement to bit value conversion.  Write the 2's complement value
+ * (0-7 only) to the top nibble and retrieve the bit indexed by that value
+ * on the next read of this register. 
+ * Example:
+ *	Write	0x60
+ *	Read	0x40
+ */
+register FUNCTION1 {
+	address			0x0F0
+	access_mode RW
+}
+
+/*
+ * Stack
+ * Window into the stack.  Each stack location is 10 bits wide reported
+ * low byte followed by high byte.  There are 8 stack locations.
+ */
+register STACK {
+	address			0x0F2
+	access_mode RW
+	dont_generate_debug_code
+}
+
+/*
+ * Interrupt Vector 1 Address
+ * Interrupt branch address for SCS SEQ_INT1 mode 0 and 1 interrupts.
+ */
+register INTVEC1_ADDR {
+	address			0x0F4
+	access_mode	RW
+	size		2
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Current Address
+ * Address of the SEQRAM instruction currently executing instruction.
+ */
+register CURADDR {
+	address			0x0F4
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * Interrupt Vector 2 Address
+ * Interrupt branch address for HST_SEQ_INT2 interrupts.
+ */
+register INTVEC2_ADDR {
+	address			0x0F6
+	access_mode	RW
+	size		2
+	modes		M_CFG
+	count		1
+	dont_generate_debug_code
+}
+
+/*
+ * Last Address
+ * Address of the SEQRAM instruction executed prior to the current instruction.
+ */
+register LASTADDR {
+	address			0x0F6
+	access_mode	RW
+	size		2
+	modes		M_SCSI
+}
+
+register AHD_PCI_CONFIG_BASE {
+	address			0x100
+	access_mode	RW
+	size		256
+	modes		M_CFG
+}
+
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+scratch_ram {
+	/* Mode Specific */
+	address			0x0A0
+	size	8
+	modes	0, 1, 2, 3
+	REG0 {
+		size		2
+		dont_generate_debug_code
+	}
+	REG1 {
+		size		2
+	}
+	REG_ISR {
+		size		2
+		dont_generate_debug_code
+	}
+	SG_STATE {
+		size		1
+		field	SEGS_AVAIL	0x01
+		field	LOADING_NEEDED	0x02
+		field	FETCH_INPROG	0x04
+	}
+	/*
+	 * Track whether the transfer byte count for
+	 * the current data phase is odd.
+	 */
+	DATA_COUNT_ODD {
+		size		1
+	}
+}
+
+scratch_ram {
+	/* Mode Specific */
+	address			0x0F8
+	size	8
+	modes	0, 1, 2, 3
+	LONGJMP_ADDR {
+		size		2
+		dont_generate_debug_code
+	}
+	ACCUM_SAVE {
+		size		1
+		dont_generate_debug_code
+	}
+}
+
+
+scratch_ram {
+	address			0x100
+	size	128
+	modes	0, 1, 2, 3
+	/*
+	 * Per "other-id" execution queues.  We use an array of
+	 * tail pointers into lists of SCBs sorted by "other-id".
+	 * The execution head pointer threads the head SCBs for
+	 * each list.
+	 */
+	WAITING_SCB_TAILS {
+		size		32
+		dont_generate_debug_code
+	}
+	WAITING_TID_HEAD {
+		size		2
+		dont_generate_debug_code
+	}
+	WAITING_TID_TAIL {
+		size		2
+		dont_generate_debug_code
+	}
+	/*
+	 * SCBID of the next SCB in the new SCB queue.
+	 */
+	NEXT_QUEUED_SCB_ADDR {
+		size		4
+		dont_generate_debug_code
+	}
+	/*
+	 * head of list of SCBs that have
+	 * completed but have not been
+	 * put into the qoutfifo.
+	 */
+	COMPLETE_SCB_HEAD {
+		size		2
+		dont_generate_debug_code
+	}
+	/*
+	 * The list of completed SCBs in
+	 * the active DMA.
+	 */
+	COMPLETE_SCB_DMAINPROG_HEAD {
+		size		2
+		dont_generate_debug_code
+	}
+	/*
+	 * head of list of SCBs that have
+	 * completed but need to be uploaded
+	 * to the host prior to being completed.
+	 */
+	COMPLETE_DMA_SCB_HEAD {
+		size		2
+		dont_generate_debug_code
+	}
+	/*
+	 * tail of list of SCBs that have
+	 * completed but need to be uploaded
+	 * to the host prior to being completed.
+	 */
+	COMPLETE_DMA_SCB_TAIL {
+		size		2
+		dont_generate_debug_code
+	}
+	/*
+	 * head of list of SCBs that have
+	 * been uploaded to the host, but cannot
+	 * be completed until the QFREEZE is in
+	 * full effect (i.e. no selections pending).
+	 */
+	COMPLETE_ON_QFREEZE_HEAD {
+		size		2
+		dont_generate_debug_code
+	}
+	/*
+	 * Counting semaphore to prevent new select-outs
+	 * The queue is frozen so long as the sequencer
+	 * and kernel freeze counts differ.
+	 */
+	QFREEZE_COUNT {
+		size		2
+	}
+	KERNEL_QFREEZE_COUNT {
+		size		2
+	}
+	/*
+	 * Mode to restore on legacy idle loop exit.
+	 */
+	SAVED_MODE {
+		size		1
+	}
+	/*
+	 * Single byte buffer used to designate the type or message
+	 * to send to a target.
+	 */
+	MSG_OUT {
+		size		1
+		dont_generate_debug_code
+	}
+	/* Parameters for DMA Logic */
+	DMAPARAMS {
+		size		1
+		count		8
+		field	PRELOADEN	0x80
+		field	WIDEODD		0x40
+		field	SCSIEN		0x20
+		field	SDMAEN		0x10
+		field	SDMAENACK	0x10
+		field	HDMAEN		0x08
+		field	HDMAENACK	0x08
+		field	DIRECTION	0x04	/* Set indicates PCI->SCSI */
+		field	FIFOFLUSH	0x02
+		field	FIFORESET	0x01
+		dont_generate_debug_code
+	}
+	SEQ_FLAGS {
+		size		1
+		field	NOT_IDENTIFIED		0x80
+		field	NO_CDB_SENT		0x40
+		field	TARGET_CMD_IS_TAGGED	0x40
+		field	DPHASE			0x20
+		/* Target flags */
+		field	TARG_CMD_PENDING	0x10
+		field	CMDPHASE_PENDING	0x08
+		field	DPHASE_PENDING		0x04
+		field	SPHASE_PENDING		0x02
+		field	NO_DISCONNECT		0x01
+	}
+	/*
+	 * Temporary storage for the
+	 * target/channel/lun of a
+	 * reconnecting target
+	 */
+	SAVED_SCSIID {
+		size		1
+		dont_generate_debug_code
+	}
+	SAVED_LUN {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * The last bus phase as seen by the sequencer. 
+	 */
+	LASTPHASE {
+		size		1
+		field	CDI		0x80
+		field	IOI		0x40
+		field	MSGI		0x20
+		field	P_BUSFREE	0x01
+		enum	PHASE_MASK  CDO|IOO|MSGO {
+			P_DATAOUT	0x0,
+			P_DATAIN	IOO,
+			P_DATAOUT_DT	P_DATAOUT|MSGO,
+			P_DATAIN_DT	P_DATAIN|MSGO,
+			P_COMMAND	CDO,
+			P_MESGOUT	CDO|MSGO,
+			P_STATUS	CDO|IOO,
+			P_MESGIN	CDO|IOO|MSGO
+		}
+	}
+	/*
+	 * Value to "or" into the SCBPTR[1] value to
+	 * indicate that an entry in the QINFIFO is valid.
+	 */
+	QOUTFIFO_ENTRY_VALID_TAG {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * Kernel and sequencer offsets into the queue of
+	 * incoming target mode command descriptors.  The
+	 * queue is full when the KERNEL_TQINPOS == TQINPOS.
+	 */
+	KERNEL_TQINPOS {
+		size		1
+		count		1
+		dont_generate_debug_code
+	}
+	TQINPOS {
+		size		1
+		count		8
+		dont_generate_debug_code
+	}
+	/*
+	 * Base address of our shared data with the kernel driver in host
+	 * memory.  This includes the qoutfifo and target mode
+	 * incoming command queue.
+	 */
+	SHARED_DATA_ADDR {
+		size		4
+		dont_generate_debug_code
+	}
+	/*
+	 * Pointer to location in host memory for next
+	 * position in the qoutfifo.
+	 */
+	QOUTFIFO_NEXT_ADDR {
+		size		4
+		dont_generate_debug_code
+	}
+	ARG_1 {
+		size		1
+		mask	SEND_MSG		0x80
+		mask	SEND_SENSE		0x40
+		mask	SEND_REJ		0x20
+		mask	MSGOUT_PHASEMIS		0x10
+		mask	EXIT_MSG_LOOP		0x08
+		mask	CONT_MSG_LOOP_WRITE	0x04
+		mask	CONT_MSG_LOOP_READ	0x03
+		mask	CONT_MSG_LOOP_TARG	0x02
+		alias	RETURN_1
+		dont_generate_debug_code
+	}
+	ARG_2 {
+		size		1
+		count		1
+		alias	RETURN_2
+		dont_generate_debug_code
+	}
+
+	/*
+	 * Snapshot of MSG_OUT taken after each message is sent.
+	 */
+	LAST_MSG {
+		size		1
+		dont_generate_debug_code
+	}
+
+	/*
+	 * Sequences the kernel driver has okayed for us.  This allows
+	 * the driver to do things like prevent initiator or target
+	 * operations.
+	 */
+	SCSISEQ_TEMPLATE {
+		size		1
+		count		7
+		field	MANUALCTL	0x40
+		field	ENSELI		0x20
+		field	ENRSELI		0x10
+		field	MANUALP		0x0C
+		field	ENAUTOATNP	0x02
+		field	ALTSTIM		0x01
+		dont_generate_debug_code
+	}
+
+	/*
+	 * The initiator specified tag for this target mode transaction.
+	 */
+	INITIATOR_TAG {
+		size		1
+		count		1
+		dont_generate_debug_code
+	}
+
+	SEQ_FLAGS2 {
+		size		1
+		field	PENDING_MK_MESSAGE	0x01
+		field	TARGET_MSG_PENDING	0x02
+		field	SELECTOUT_QFROZEN	0x04
+	}
+
+	ALLOCFIFO_SCBPTR {
+		size		2
+		dont_generate_debug_code
+	}
+
+	/*
+	 * The maximum amount of time to wait, when interrupt coalescing
+	 * is enabled, before issuing a CMDCMPLT interrupt for a completed
+	 * command.
+	 */
+	INT_COALESCING_TIMER {
+		size		2
+		dont_generate_debug_code
+	}
+
+	/*
+	 * The maximum number of commands to coalesce into a single interrupt.
+	 * Actually the 2's complement of that value to simplify sequencer
+	 * code.
+	 */
+	INT_COALESCING_MAXCMDS {
+		size		1
+		dont_generate_debug_code
+	}
+
+	/*
+	 * The minimum number of commands still outstanding required
+	 * to continue coalescing (2's complement of value).
+	 */
+	INT_COALESCING_MINCMDS {
+		size		1
+		dont_generate_debug_code
+	}
+
+	/*
+	 * Number of commands "in-flight".
+	 */
+	CMDS_PENDING {
+		size		2
+		dont_generate_debug_code
+	}
+
+	/*
+	 * The count of commands that have been coalesced.
+	 */
+	INT_COALESCING_CMDCOUNT {
+		size		1
+		dont_generate_debug_code
+	}
+
+	/*
+	 * Since the HS_MAIBOX is self clearing, copy its contents to
+	 * this position in scratch ram every time it changes.
+	 */
+	LOCAL_HS_MAILBOX {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * Target-mode CDB type to CDB length table used
+	 * in non-packetized operation.
+	 */
+	CMDSIZE_TABLE {
+		size		8
+		count		8
+		dont_generate_debug_code
+	}
+	/*
+	 * When an SCB with the MK_MESSAGE flag is
+	 * queued to the controller, it cannot enter
+	 * the waiting for selection list until the
+	 * selections for any previously queued
+	 * commands to that target complete.  During
+	 * the wait, the MK_MESSAGE SCB is queued
+	 * here.
+	 */
+	MK_MESSAGE_SCB {
+		size		2
+	}
+	/*
+	 * Saved SCSIID of MK_MESSAGE_SCB to avoid
+	 * an extra SCBPTR operation when deciding
+	 * if the MK_MESSAGE_SCB can be run.
+	 */
+	MK_MESSAGE_SCSIID {
+		size		1
+	}
+}
+
+/************************* Hardware SCB Definition ****************************/
+scb {
+	address			0x180
+	size		64
+	modes		0, 1, 2, 3
+	SCB_RESIDUAL_DATACNT {
+		size	4
+		alias	SCB_CDB_STORE
+		alias	SCB_HOST_CDB_PTR
+		dont_generate_debug_code
+	}
+	SCB_RESIDUAL_SGPTR {
+		size	4
+		field	SG_ADDR_MASK		0xf8	/* In the last byte */
+		field	SG_OVERRUN_RESID	0x02	/* In the first byte */
+		field	SG_LIST_NULL		0x01	/* In the first byte */
+		dont_generate_debug_code
+	}
+	SCB_SCSI_STATUS {
+		size	1
+		alias	SCB_HOST_CDB_LEN
+		dont_generate_debug_code
+	}
+	SCB_TARGET_PHASES {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_TARGET_DATA_DIR {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_TARGET_ITAG {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_SENSE_BUSADDR {
+		/*
+		 * Only valid if CDB length is less than 13 bytes or
+		 * we are using a CDB pointer.  Otherwise contains
+		 * the last 4 bytes of embedded cdb information.
+		 */
+		size	4
+		alias	SCB_NEXT_COMPLETE
+		dont_generate_debug_code
+	}
+	SCB_TAG {
+		alias	SCB_FIFO_USE_COUNT
+		size	2
+		dont_generate_debug_code
+	}
+	SCB_CONTROL {
+		size	1
+		field	TARGET_SCB	0x80
+		field	DISCENB		0x40
+		field	TAG_ENB		0x20
+		field	MK_MESSAGE	0x10
+		field	STATUS_RCVD	0x08
+		field	DISCONNECTED	0x04
+		field	SCB_TAG_TYPE	0x03
+	}
+	SCB_SCSIID {
+		size	1
+		field	TID	0xF0
+		field	OID	0x0F
+	}
+	SCB_LUN {
+		size	1
+		field	LID	0xff
+		dont_generate_debug_code
+	}
+	SCB_TASK_ATTRIBUTE {
+		size	1
+		/*
+		 * Overloaded field for non-packetized 
+		 * ignore wide residue message handling.
+		 */
+		field	SCB_XFERLEN_ODD	0x01
+		dont_generate_debug_code
+	}
+	SCB_CDB_LEN {
+		size	1
+		field	SCB_CDB_LEN_PTR	0x80	/* CDB in host memory */
+		dont_generate_debug_code
+	}
+	SCB_TASK_MANAGEMENT {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_DATAPTR {
+		size	8
+		dont_generate_debug_code
+	}
+	SCB_DATACNT {
+		/*
+		 * The last byte is really the high address bits for
+		 * the data address.
+		 */
+		size	4
+		field	SG_LAST_SEG		0x80	/* In the fourth byte */
+		field	SG_HIGH_ADDR_BITS	0x7F	/* In the fourth byte */
+		dont_generate_debug_code
+	}
+	SCB_SGPTR {
+		size	4
+		field	SG_STATUS_VALID	0x04	/* In the first byte */
+		field	SG_FULL_RESID	0x02	/* In the first byte */
+		field	SG_LIST_NULL	0x01	/* In the first byte */
+		dont_generate_debug_code
+	}
+	SCB_BUSADDR {
+		size	4
+		dont_generate_debug_code
+	}
+	SCB_NEXT {
+		alias	SCB_NEXT_SCB_BUSADDR
+		size	2
+		dont_generate_debug_code
+	}
+	SCB_NEXT2 {
+		size	2
+		dont_generate_debug_code
+	}
+	SCB_SPARE {
+		size	8
+		alias	SCB_PKT_LUN
+	}
+	SCB_DISCONNECTED_LISTS {
+		size	8
+		dont_generate_debug_code
+	}
+}
+
+/*********************************** Constants ********************************/
+const MK_MESSAGE_BIT_OFFSET	4
+const TID_SHIFT		4
+const TARGET_CMD_CMPLT	0xfe
+const INVALID_ADDR	0x80
+#define SCB_LIST_NULL	0xff
+#define QOUTFIFO_ENTRY_VALID_TOGGLE	0x80
+
+const CCSGADDR_MAX	0x80
+const CCSCBADDR_MAX	0x80
+const CCSGRAM_MAXSEGS	16
+
+/* Selection Timeout Timer Constants */
+const STIMESEL_SHIFT	3
+const STIMESEL_MIN	0x18
+const STIMESEL_BUG_ADJ	0x8
+
+/* WDTR Message values */
+const BUS_8_BIT			0x00
+const BUS_16_BIT		0x01
+const BUS_32_BIT		0x02
+
+/* Offset maximums */
+const MAX_OFFSET		0xfe
+const MAX_OFFSET_PACED		0xfe
+const MAX_OFFSET_PACED_BUG	0x7f
+/*
+ * Some 160 devices incorrectly accept 0xfe as a
+ * sync offset, but will overrun this value.  Limit
+ * to 0x7f for speed lower than U320 which will
+ * avoid the persistent sync offset overruns.
+ */
+const MAX_OFFSET_NON_PACED	0x7f
+const HOST_MSG			0xff
+
+/*
+ * The size of our sense buffers.
+ * Sense buffer mapping can be handled in either of two ways.
+ * The first is to allocate a dmamap for each transaction.
+ * Depending on the architecture, dmamaps can be costly. The
+ * alternative is to statically map the buffers in much the same
+ * way we handle our scatter gather lists.  The driver implements
+ * the later.
+ */
+const AHD_SENSE_BUFSIZE		256
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT	0x05
+
+const STATUS_BUSY		0x08
+const STATUS_QUEUE_FULL		0x28
+const STATUS_PKT_SENSE		0xFF
+const TARGET_DATA_IN		1
+
+const SCB_TRANSFER_SIZE_FULL_LUN	56
+const SCB_TRANSFER_SIZE_1BYTE_LUN	48
+/* PKT_OVERRUN_BUFSIZE must be a multiple of 256 less than 64K */
+const PKT_OVERRUN_BUFSIZE	512
+
+/*
+ * Timer parameters.
+ */
+const AHD_TIMER_US_PER_TICK	25
+const AHD_TIMER_MAX_TICKS	0xFFFF
+const AHD_TIMER_MAX_US		(AHD_TIMER_MAX_TICKS * AHD_TIMER_US_PER_TICK)
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_CNT_LIMIT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
+const SG_SIZEOF download
+const PKT_OVERRUN_BUFOFFSET download
+const SCB_TRANSFER_SIZE	download
+const CACHELINE_MASK download
+
+/*
+ * BIOS SCB offsets
+ */
+const NVRAM_SCB_OFFSET	0x2C
diff --git a/drivers/scsi/aic7xxx/aic79xx.seq b/drivers/scsi/aic7xxx/aic79xx.seq
new file mode 100644
index 0000000..3a36d93
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.seq
@@ -0,0 +1,2290 @@
+/*
+ * Adaptec U320 device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001, 2004 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#120 $"
+PATCH_ARG_LIST = "struct ahd_softc *ahd"
+PREFIX = "ahd_"
+
+#include "aic79xx.reg"
+#include "scsi_message.h"
+
+restart:
+if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+	test	SEQINTCODE, 0xFF jz idle_loop;
+	SET_SEQINTCODE(NO_SEQINT)
+}
+
+idle_loop:
+
+	if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+		/*
+		 * Convert ERROR status into a sequencer
+		 * interrupt to handle the case of an
+		 * interrupt collision on the hardware
+		 * setting of HWERR.
+		 */
+		test	ERROR, 0xFF jz no_error_set;
+		SET_SEQINTCODE(SAW_HWERR)
+no_error_set:
+	}
+	SET_MODE(M_SCSI, M_SCSI)
+	test	SCSISEQ0, ENSELO|ENARBO jnz idle_loop_checkbus;
+	test	SEQ_FLAGS2, SELECTOUT_QFROZEN jz check_waiting_list;
+	/*
+	 * If the kernel has caught up with us, thaw the queue.
+	 */
+	mov	A, KERNEL_QFREEZE_COUNT;
+	cmp	QFREEZE_COUNT, A jne check_frozen_completions;
+	mov	A, KERNEL_QFREEZE_COUNT[1];
+	cmp	QFREEZE_COUNT[1], A jne check_frozen_completions;
+	and	SEQ_FLAGS2, ~SELECTOUT_QFROZEN;
+	jmp	check_waiting_list;
+check_frozen_completions:
+	test	SSTAT0, SELDO|SELINGO jnz idle_loop_checkbus;
+BEGIN_CRITICAL;
+	/*
+	 * If we have completions stalled waiting for the qfreeze
+	 * to take effect, move them over to the complete_scb list
+	 * now that no selections are pending.
+	 */
+	cmp	COMPLETE_ON_QFREEZE_HEAD[1],SCB_LIST_NULL je idle_loop_checkbus;
+	/*
+	 * Find the end of the qfreeze list.  The first element has
+	 * to be treated specially.
+	 */
+	bmov	SCBPTR, COMPLETE_ON_QFREEZE_HEAD, 2;
+	cmp 	SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je join_lists;
+	/*
+	 * Now the normal loop.
+	 */
+	bmov	SCBPTR, SCB_NEXT_COMPLETE, 2;
+	cmp 	SCB_NEXT_COMPLETE[1], SCB_LIST_NULL jne . - 1;
+join_lists:
+	bmov	SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+	bmov	COMPLETE_SCB_HEAD, COMPLETE_ON_QFREEZE_HEAD, 2;
+	mvi	COMPLETE_ON_QFREEZE_HEAD[1], SCB_LIST_NULL;
+	jmp	idle_loop_checkbus;
+check_waiting_list:
+	cmp	WAITING_TID_HEAD[1], SCB_LIST_NULL je idle_loop_checkbus;
+	/*
+	 * ENSELO is cleared by a SELDO, so we must test for SELDO
+	 * one last time.
+	 */
+	test	SSTAT0, SELDO jnz select_out;
+	call	start_selection;
+idle_loop_checkbus:
+	test	SSTAT0, SELDO jnz select_out;
+END_CRITICAL;
+	test	SSTAT0, SELDI jnz select_in;
+	test	SCSIPHASE, ~DATA_PHASE_MASK jz idle_loop_check_nonpackreq;
+	test	SCSISIGO, ATNO jz idle_loop_check_nonpackreq;
+	call	unexpected_nonpkt_phase_find_ctxt;
+idle_loop_check_nonpackreq:
+	test	SSTAT2, NONPACKREQ jz . + 2;
+	call	unexpected_nonpkt_phase_find_ctxt;
+	if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
+		/*
+		 * On Rev A. hardware, the busy LED is only
+		 * turned on automaically during selections
+		 * and re-selections.  Make the LED status
+		 * more useful by forcing it to be on so
+		 * long as one of our data FIFOs is active.
+		 */
+		and	A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+		cmp	A, FIFO0FREE|FIFO1FREE jne . + 3;
+		and	SBLKCTL, ~DIAGLEDEN|DIAGLEDON;
+		jmp	. + 2;
+		or	SBLKCTL, DIAGLEDEN|DIAGLEDON;
+	}
+	call	idle_loop_gsfifo_in_scsi_mode;
+	call	idle_loop_service_fifos;
+	call	idle_loop_cchan;
+	jmp	idle_loop;
+
+idle_loop_gsfifo:
+	SET_MODE(M_SCSI, M_SCSI)
+BEGIN_CRITICAL;
+idle_loop_gsfifo_in_scsi_mode:
+	test	LQISTAT2, LQIGSAVAIL jz return;
+	/*
+	 * We have received good status for this transaction.  There may
+	 * still be data in our FIFOs draining to the host.  Complete
+	 * the SCB only if all data has transferred to the host.
+	 */
+good_status_IU_done:
+	bmov	SCBPTR, GSFIFO, 2;
+	clr	SCB_SCSI_STATUS;
+	/*
+	 * If a command completed before an attempted task management
+	 * function completed, notify the host after disabling any
+	 * pending select-outs.
+	 */
+	test	SCB_TASK_MANAGEMENT, 0xFF jz gsfifo_complete_normally;
+	test	SSTAT0, SELDO|SELINGO jnz . + 2;
+	and	SCSISEQ0, ~ENSELO;
+	SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY)
+gsfifo_complete_normally:
+	or	SCB_CONTROL, STATUS_RCVD;
+
+	/*
+	 * Since this status did not consume a FIFO, we have to
+	 * be a bit more dilligent in how we check for FIFOs pertaining
+	 * to this transaction.  There are two states that a FIFO still
+	 * transferring data may be in.
+	 *
+	 * 1) Configured and draining to the host, with a FIFO handler.
+	 * 2) Pending cfg4data, fifo not empty.
+	 *
+	 * Case 1 can be detected by noticing a non-zero FIFO active
+	 * count in the SCB.  In this case, we allow the routine servicing
+	 * the FIFO to complete the SCB.
+	 * 
+	 * Case 2 implies either a pending or yet to occur save data
+	 * pointers for this same context in the other FIFO.  So, if
+	 * we detect case 1, we will properly defer the post of the SCB
+	 * and achieve the desired result.  The pending cfg4data will
+	 * notice that status has been received and complete the SCB.
+	 */
+	test	SCB_FIFO_USE_COUNT, 0xFF jnz idle_loop_gsfifo_in_scsi_mode;
+	call	complete;
+END_CRITICAL;
+	jmp	idle_loop_gsfifo_in_scsi_mode;
+
+idle_loop_service_fifos:
+	SET_MODE(M_DFF0, M_DFF0)
+BEGIN_CRITICAL;
+	test	LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo;
+	call	longjmp;
+END_CRITICAL;
+idle_loop_next_fifo:
+	SET_MODE(M_DFF1, M_DFF1)
+BEGIN_CRITICAL;
+	test	LONGJMP_ADDR[1], INVALID_ADDR jz longjmp;
+END_CRITICAL;
+return:
+	ret;
+
+idle_loop_cchan:
+	SET_MODE(M_CCHAN, M_CCHAN)
+	test	QOFF_CTLSTA, HS_MAILBOX_ACT jz	hs_mailbox_empty;
+	or	QOFF_CTLSTA, HS_MAILBOX_ACT;
+	mov	LOCAL_HS_MAILBOX, HS_MAILBOX;
+hs_mailbox_empty:
+BEGIN_CRITICAL;
+	test	CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle;
+	test	CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog;
+	test	CCSCBCTL, CCSCBDONE jz return;
+	/* FALLTHROUGH */
+scbdma_tohost_done:
+	test	CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
+	/*
+	 * An SCB has been successfully uploaded to the host.
+	 * If the SCB was uploaded for some reason other than
+	 * bad SCSI status (currently only for underruns), we
+	 * queue the SCB for normal completion.  Otherwise, we
+	 * wait until any select-out activity has halted, and
+	 * then queue the completion.
+	 */
+	and	CCSCBCTL, ~(CCARREN|CCSCBEN);
+	bmov	COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+	cmp	SCB_NEXT_COMPLETE[1], SCB_LIST_NULL jne . + 2;
+	mvi	COMPLETE_DMA_SCB_TAIL[1], SCB_LIST_NULL;
+	test	SCB_SCSI_STATUS, 0xff jz scbdma_queue_completion;
+	bmov	SCB_NEXT_COMPLETE, COMPLETE_ON_QFREEZE_HEAD, 2;
+	bmov	COMPLETE_ON_QFREEZE_HEAD, SCBPTR, 2 ret;
+scbdma_queue_completion:
+	bmov	SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+	bmov	COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+fill_qoutfifo_dmadone:
+	and	CCSCBCTL, ~(CCARREN|CCSCBEN);
+	call	qoutfifo_updated;
+	mvi	COMPLETE_SCB_DMAINPROG_HEAD[1], SCB_LIST_NULL;
+	bmov	QOUTFIFO_NEXT_ADDR, SCBHADDR, 4;
+	test	QOFF_CTLSTA, SDSCB_ROLLOVR jz return;
+	bmov	QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4;
+	xor	QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID_TOGGLE ret;
+END_CRITICAL;
+
+qoutfifo_updated:
+	/*
+	 * If there are more commands waiting to be dma'ed
+	 * to the host, always coalesce.  Otherwise honor the
+	 * host's wishes.
+	 */
+	cmp	COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne coalesce_by_count;
+	cmp	COMPLETE_SCB_HEAD[1], SCB_LIST_NULL jne coalesce_by_count;
+	test	LOCAL_HS_MAILBOX, ENINT_COALESCE jz issue_cmdcmplt;
+
+	/*
+	 * If we have relatively few commands outstanding, don't
+	 * bother waiting for another command to complete.
+	 */
+	test	CMDS_PENDING[1], 0xFF jnz coalesce_by_count;
+	/* Add -1 so that jnc means <= not just < */
+	add	A, -1, INT_COALESCING_MINCMDS;
+	add	NONE, A, CMDS_PENDING;
+	jnc	issue_cmdcmplt;
+	
+	/*
+	 * If coalescing, only coalesce up to the limit
+	 * provided by the host driver.
+	 */
+coalesce_by_count:
+	mov	A, INT_COALESCING_MAXCMDS;
+	add	NONE, A, INT_COALESCING_CMDCOUNT;
+	jc	issue_cmdcmplt;
+	/*
+	 * If the timer is not currently active,
+	 * fire it up.
+	 */
+	test	INTCTL, SWTMINTMASK jz return;
+	bmov	SWTIMER, INT_COALESCING_TIMER, 2;
+	mvi	CLRSEQINTSTAT, CLRSEQ_SWTMRTO;
+	or	INTCTL, SWTMINTEN|SWTIMER_START;
+	and	INTCTL, ~SWTMINTMASK ret;
+
+issue_cmdcmplt:
+	mvi	INTSTAT, CMDCMPLT;
+	clr	INT_COALESCING_CMDCOUNT;
+	or	INTCTL, SWTMINTMASK ret;
+
+BEGIN_CRITICAL;
+fetch_new_scb_inprog:
+	test	CCSCBCTL, ARRDONE jz return;
+fetch_new_scb_done:
+	and	CCSCBCTL, ~(CCARREN|CCSCBEN);
+	clr	A;
+	add	CMDS_PENDING, 1;
+	adc	CMDS_PENDING[1], A;
+	if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+		/*
+		 * "Short Luns" are not placed into outgoing LQ
+		 * packets in the correct byte order.  Use a full
+		 * sized lun field instead and fill it with the
+		 * one byte of lun information we support.
+		 */
+		mov	SCB_PKT_LUN[6], SCB_LUN;
+	}
+	/*
+	 * The FIFO use count field is shared with the
+	 * tag set by the host so that our SCB dma engine
+	 * knows the correct location to store the SCB.
+	 * Set it to zero before processing the SCB.
+	 */
+	clr	SCB_FIFO_USE_COUNT;
+	/* Update the next SCB address to download. */
+	bmov	NEXT_QUEUED_SCB_ADDR, SCB_NEXT_SCB_BUSADDR, 4;
+	/*
+	 * NULL out the SCB links since these fields
+	 * occupy the same location as SCB_NEXT_SCB_BUSADDR.
+	 */
+	mvi	SCB_NEXT[1], SCB_LIST_NULL;
+	mvi	SCB_NEXT2[1], SCB_LIST_NULL;
+	/* Increment our position in the QINFIFO. */
+	mov	NONE, SNSCB_QOFF;
+
+	/*
+	 * Save SCBID of this SCB in REG0 since
+	 * SCBPTR will be clobbered during target
+	 * list updates.  We also record the SCB's
+	 * flags so that we can refer to them even
+	 * after SCBPTR has been changed.
+	 */
+	bmov	REG0, SCBPTR, 2;
+	mov	A, SCB_CONTROL;
+
+	/*
+	 * Find the tail SCB of the execution queue
+	 * for this target.
+	 */
+	shr	SINDEX, 3, SCB_SCSIID;
+	and	SINDEX, ~0x1;
+	mvi	SINDEX[1], (WAITING_SCB_TAILS >> 8);
+	bmov	DINDEX, SINDEX, 2;
+	bmov	SCBPTR, SINDIR, 2;
+
+	/*
+	 * Update the tail to point to the new SCB.
+	 */
+	bmov	DINDIR, REG0, 2;
+
+	/*
+	 * If the queue was empty, queue this SCB as
+	 * the first for this target.
+	 */
+	cmp	SCBPTR[1], SCB_LIST_NULL je first_new_target_scb;
+
+	/*
+	 * SCBs that want to send messages must always be
+	 * at the head of their per-target queue so that
+	 * ATN can be asserted even if the current
+	 * negotiation agreement is packetized.  If the
+	 * target queue is empty, the SCB can be queued
+	 * immediately.  If the queue is not empty, we must
+	 * wait for it to empty before entering this SCB
+	 * into the waiting for selection queue.  Otherwise
+	 * our batching and round-robin selection scheme 
+	 * could allow commands to be queued out of order.
+	 * To simplify the implementation, we stop pulling
+	 * new commands from the host until the MK_MESSAGE
+	 * SCB can be queued to the waiting for selection
+	 * list.
+	 */
+	test	A, MK_MESSAGE jz batch_scb; 
+
+	/*
+	 * If the last SCB is also a MK_MESSAGE SCB, then
+	 * order is preserved even if we batch.
+	 */
+	test	SCB_CONTROL, MK_MESSAGE jz batch_scb; 
+
+	/*
+	 * Defer this SCB and stop fetching new SCBs until
+	 * it can be queued.  Since the SCB_SCSIID of the
+	 * tail SCB must be the same as that of the newly
+	 * queued SCB, there is no need to restore the SCBID
+	 * here.
+	 */
+	or	SEQ_FLAGS2, PENDING_MK_MESSAGE;
+	bmov	MK_MESSAGE_SCB, REG0, 2;
+	mov	MK_MESSAGE_SCSIID, SCB_SCSIID ret;
+
+batch_scb:
+	/*
+	 * Otherwise just update the previous tail SCB to
+	 * point to the new tail.
+	 */
+	bmov	SCB_NEXT, REG0, 2 ret;
+
+first_new_target_scb:
+	/*
+	 * Append SCB to the tail of the waiting for
+	 * selection list.
+	 */
+	cmp	WAITING_TID_HEAD[1], SCB_LIST_NULL je first_new_scb;
+	bmov	SCBPTR, WAITING_TID_TAIL, 2;
+	bmov	SCB_NEXT2, REG0, 2;
+	bmov	WAITING_TID_TAIL, REG0, 2 ret;
+first_new_scb:
+	/*
+	 * Whole list is empty, so the head of
+	 * the list must be initialized too.
+	 */
+	bmov	WAITING_TID_HEAD, REG0, 2;
+	bmov	WAITING_TID_TAIL, REG0, 2 ret;
+END_CRITICAL;
+
+scbdma_idle:
+	/*
+	 * Don't bother downloading new SCBs to execute
+	 * if select-outs are currently frozen or we have
+	 * a MK_MESSAGE SCB waiting to enter the queue.
+	 */
+	test	SEQ_FLAGS2, SELECTOUT_QFROZEN|PENDING_MK_MESSAGE
+		jnz scbdma_no_new_scbs;
+BEGIN_CRITICAL;
+	test	QOFF_CTLSTA, NEW_SCB_AVAIL jnz fetch_new_scb;
+scbdma_no_new_scbs:
+	cmp	COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne dma_complete_scb;
+	cmp	COMPLETE_SCB_HEAD[1], SCB_LIST_NULL je return;
+	/* FALLTHROUGH */
+fill_qoutfifo:
+	/*
+	 * Keep track of the SCBs we are dmaing just
+	 * in case the DMA fails or is aborted.
+	 */
+	bmov	COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2;
+	mvi	CCSCBCTL, CCSCBRESET;
+	bmov	SCBHADDR, QOUTFIFO_NEXT_ADDR, 4;
+	mov	A, QOUTFIFO_NEXT_ADDR;
+	bmov	SCBPTR, COMPLETE_SCB_HEAD, 2;
+fill_qoutfifo_loop:
+	bmov	CCSCBRAM, SCBPTR, 2;
+	mov	CCSCBRAM, SCB_SGPTR[0];
+	mov	CCSCBRAM, QOUTFIFO_ENTRY_VALID_TAG;
+	mov	NONE, SDSCB_QOFF;
+	inc	INT_COALESCING_CMDCOUNT;
+	add	CMDS_PENDING, -1;
+	adc	CMDS_PENDING[1], -1;
+	cmp	SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done;
+	cmp	CCSCBADDR, CCSCBADDR_MAX je fill_qoutfifo_done;
+	test	QOFF_CTLSTA, SDSCB_ROLLOVR jnz fill_qoutfifo_done;
+	/*
+	 * Don't cross an ADB or Cachline boundary when DMA'ing
+	 * completion entries.  In PCI mode, at least in 32/33
+	 * configurations, the SCB DMA engine may lose its place
+	 * in the data-stream should the target force a retry on
+	 * something other than an 8byte aligned boundary. In
+	 * PCI-X mode, we do this to avoid split transactions since
+	 * many chipsets seem to be unable to format proper split
+	 * completions to continue the data transfer.
+	 */
+	add	SINDEX, A, CCSCBADDR;
+	test	SINDEX, CACHELINE_MASK jz fill_qoutfifo_done;
+	bmov	SCBPTR, SCB_NEXT_COMPLETE, 2;
+	jmp	fill_qoutfifo_loop;
+fill_qoutfifo_done:
+	mov	SCBHCNT, CCSCBADDR;
+	mvi	CCSCBCTL, CCSCBEN|CCSCBRESET;
+	bmov	COMPLETE_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+	mvi	SCB_NEXT_COMPLETE[1], SCB_LIST_NULL ret;
+
+fetch_new_scb:
+	bmov	SCBHADDR, NEXT_QUEUED_SCB_ADDR, 4;
+	mvi	CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET jmp dma_scb;
+dma_complete_scb:
+	bmov	SCBPTR, COMPLETE_DMA_SCB_HEAD, 2;
+	bmov	SCBHADDR, SCB_BUSADDR, 4;
+	mvi	CCARREN|CCSCBEN|CCSCBRESET jmp dma_scb;
+
+/*
+ * Either post or fetch an SCB from host memory.  The caller
+ * is responsible for polling for transfer completion.
+ *
+ * Prerequisits: Mode == M_CCHAN
+ *		 SINDEX contains CCSCBCTL flags
+ *		 SCBHADDR set to Host SCB address
+ *		 SCBPTR set to SCB src location on "push" operations
+ */
+SET_SRC_MODE	M_CCHAN;
+SET_DST_MODE	M_CCHAN;
+dma_scb:
+	mvi	SCBHCNT, SCB_TRANSFER_SIZE;
+	mov	CCSCBCTL, SINDEX ret;
+
+setjmp:
+	/*
+	 * At least on the A, a return in the same
+	 * instruction as the bmov results in a return
+	 * to the caller, not to the new address at the
+	 * top of the stack.  Since we want the latter
+	 * (we use setjmp to register a handler from an
+	 * interrupt context but not invoke that handler
+	 * until we return to our idle loop), use a
+	 * separate ret instruction.
+	 */
+	bmov	LONGJMP_ADDR, STACK, 2;
+	ret;
+setjmp_inline:
+	bmov	LONGJMP_ADDR, STACK, 2;
+longjmp:
+	bmov	STACK, LONGJMP_ADDR, 2 ret;
+END_CRITICAL;
+
+/*************************** Chip Bug Work Arounds ****************************/
+/*
+ * Must disable interrupts when setting the mode pointer
+ * register as an interrupt occurring mid update will
+ * fail to store the new mode value for restoration on
+ * an iret.
+ */
+if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {
+set_mode_work_around:
+	mvi	SEQINTCTL, INTVEC1DSL;
+	mov	MODE_PTR, SINDEX;
+	clr	SEQINTCTL ret;
+}
+
+
+if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+set_seqint_work_around:
+	mov	SEQINTCODE, SINDEX;
+	mvi	SEQINTCODE, NO_SEQINT ret;
+}
+
+/************************ Packetized LongJmp Routines *************************/
+SET_SRC_MODE	M_SCSI;
+SET_DST_MODE	M_SCSI;
+start_selection:
+BEGIN_CRITICAL;
+	if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+		/*
+		 * Razor #494
+		 * Rev A hardware fails to update LAST/CURR/NEXTSCB
+		 * correctly after a packetized selection in several
+		 * situations:
+		 *
+		 * 1) If only one command existed in the queue, the
+		 *    LAST/CURR/NEXTSCB are unchanged.
+		 *
+		 * 2) In a non QAS, protocol allowed phase change,
+		 *    the queue is shifted 1 too far.  LASTSCB is
+		 *    the last SCB that was correctly processed.
+		 * 
+		 * 3) In the QAS case, if the full list of commands
+		 *    was successfully sent, NEXTSCB is NULL and neither
+		 *    CURRSCB nor LASTSCB can be trusted.  We must
+		 *    manually walk the list counting MAXCMDCNT elements
+		 *    to find the last SCB that was sent correctly.
+		 *
+		 * To simplify the workaround for this bug in SELDO
+		 * handling, we initialize LASTSCB prior to enabling
+		 * selection so we can rely on it even for case #1 above.
+		 */
+		bmov	LASTSCB, WAITING_TID_HEAD, 2;
+	}
+	bmov	CURRSCB, WAITING_TID_HEAD, 2;
+	bmov	SCBPTR, WAITING_TID_HEAD, 2;
+	shr	SELOID, 4, SCB_SCSIID;
+	/*
+	 * If we want to send a message to the device, ensure
+	 * we are selecting with atn regardless of our packetized
+	 * agreement.  Since SPI4 only allows target reset or PPR
+	 * messages if this is a packetized connection, the change
+	 * to our negotiation table entry for this selection will
+	 * be cleared when the message is acted on.
+	 */
+	test	SCB_CONTROL, MK_MESSAGE jz . + 3;
+	mov	NEGOADDR, SELOID;
+	or	NEGCONOPTS, ENAUTOATNO;
+	or	SCSISEQ0, ENSELO ret;
+END_CRITICAL;
+
+/*
+ * Allocate a FIFO for a non-packetized transaction.
+ * In RevA hardware, both FIFOs must be free before we
+ * can allocate a FIFO for a non-packetized transaction.
+ */
+allocate_fifo_loop:
+	/*
+	 * Do whatever work is required to free a FIFO.
+	 */
+	call	idle_loop_service_fifos;
+	SET_MODE(M_SCSI, M_SCSI)
+allocate_fifo:
+	if ((ahd->bugs & AHD_NONPACKFIFO_BUG) != 0) {
+		and	A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+		cmp	A, FIFO0FREE|FIFO1FREE jne allocate_fifo_loop;
+	} else {
+		test	DFFSTAT, FIFO1FREE jnz allocate_fifo1;
+		test	DFFSTAT, FIFO0FREE jz allocate_fifo_loop;
+		mvi	DFFSTAT, B_CURRFIFO_0;
+		SET_MODE(M_DFF0, M_DFF0)
+		bmov	SCBPTR, ALLOCFIFO_SCBPTR, 2 ret;
+	}
+SET_SRC_MODE	M_SCSI;
+SET_DST_MODE	M_SCSI;
+allocate_fifo1:
+	mvi	DFFSTAT, CURRFIFO_1;
+	SET_MODE(M_DFF1, M_DFF1)
+	bmov	SCBPTR, ALLOCFIFO_SCBPTR, 2 ret;
+
+/*
+ * We have been reselected as an initiator
+ * or selected as a target.
+ */
+SET_SRC_MODE	M_SCSI;
+SET_DST_MODE	M_SCSI;
+select_in:
+	if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
+		/*
+		 * On Rev A. hardware, the busy LED is only
+		 * turned on automaically during selections
+		 * and re-selections.  Make the LED status
+		 * more useful by forcing it to be on from
+		 * the point of selection until our idle
+		 * loop determines that neither of our FIFOs
+		 * are busy.  This handles the non-packetized
+		 * case nicely as we will not return to the
+		 * idle loop until the busfree at the end of
+		 * each transaction.
+		 */
+		or	SBLKCTL, DIAGLEDEN|DIAGLEDON;
+	}
+	if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+		/*
+		 * Test to ensure that the bus has not
+		 * already gone free prior to clearing
+		 * any stale busfree status.  This avoids
+		 * a window whereby a busfree just after
+		 * a selection could be missed.
+		 */
+		test	SCSISIGI, BSYI jz . + 2;
+		mvi	CLRSINT1,CLRBUSFREE;
+		or	SIMODE1, ENBUSFREE;
+	}
+	or	SXFRCTL0, SPIOEN;
+	and	SAVED_SCSIID, SELID_MASK, SELID;
+	and	A, OID, IOWNID;
+	or	SAVED_SCSIID, A;
+	mvi	CLRSINT0, CLRSELDI;
+	jmp	ITloop;
+
+/*
+ * We have successfully selected out.
+ *
+ * Clear SELDO.
+ * Dequeue all SCBs sent from the waiting queue
+ * Requeue all SCBs *not* sent to the tail of the waiting queue
+ * Take Razor #494 into account for above.
+ *
+ * In Packetized Mode:
+ *	Return to the idle loop.  Our interrupt handler will take
+ *	care of any incoming L_Qs.
+ *
+ * In Non-Packetize Mode:
+ *	Continue to our normal state machine.
+ */
+SET_SRC_MODE	M_SCSI;
+SET_DST_MODE	M_SCSI;
+select_out:
+BEGIN_CRITICAL;
+	if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
+		/*
+		 * On Rev A. hardware, the busy LED is only
+		 * turned on automaically during selections
+		 * and re-selections.  Make the LED status
+		 * more useful by forcing it to be on from
+		 * the point of re-selection until our idle
+		 * loop determines that neither of our FIFOs
+		 * are busy.  This handles the non-packetized
+		 * case nicely as we will not return to the
+		 * idle loop until the busfree at the end of
+		 * each transaction.
+		 */
+		or	SBLKCTL, DIAGLEDEN|DIAGLEDON;
+	}
+	/* Clear out all SCBs that have been successfully sent. */
+	if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+		/*
+		 * For packetized, the LQO manager clears ENSELO on
+		 * the assertion of SELDO.  If we are non-packetized,
+		 * LASTSCB and CURRSCB are accurate.
+		 */
+		test	SCSISEQ0, ENSELO jnz use_lastscb;
+
+		/*
+		 * The update is correct for LQOSTAT1 errors.  All
+		 * but LQOBUSFREE are handled by kernel interrupts.
+		 * If we see LQOBUSFREE, return to the idle loop.
+		 * Once we are out of the select_out critical section,
+		 * the kernel will cleanup the LQOBUSFREE and we will
+		 * eventually restart the selection if appropriate.
+		 */
+		test	LQOSTAT1, LQOBUSFREE jnz idle_loop;
+
+		/*
+		 * On a phase change oustside of packet boundaries,
+		 * LASTSCB points to the currently active SCB context
+		 * on the bus.
+		 */
+		test	LQOSTAT2, LQOPHACHGOUTPKT jnz use_lastscb;
+
+		/*
+		 * If the hardware has traversed the whole list, NEXTSCB
+		 * will be NULL, CURRSCB and LASTSCB cannot be trusted,
+		 * but MAXCMDCNT is accurate.  If we stop part way through
+		 * the list or only had one command to issue, NEXTSCB[1] is
+		 * not NULL and LASTSCB is the last command to go out.
+		 */
+		cmp	NEXTSCB[1], SCB_LIST_NULL jne use_lastscb;
+
+		/*
+		 * Brute force walk.
+		 */
+		bmov	SCBPTR, WAITING_TID_HEAD, 2;
+		mvi	SEQINTCTL, INTVEC1DSL;
+		mvi	MODE_PTR, MK_MODE(M_CFG, M_CFG);
+		mov	A, MAXCMDCNT;
+		mvi	MODE_PTR, MK_MODE(M_SCSI, M_SCSI);
+		clr	SEQINTCTL;
+find_lastscb_loop:
+		dec	A;
+		test	A, 0xFF jz found_last_sent_scb;
+		bmov	SCBPTR, SCB_NEXT, 2;
+		jmp	find_lastscb_loop;
+use_lastscb:
+		bmov	SCBPTR, LASTSCB, 2;
+found_last_sent_scb:
+		bmov	CURRSCB, SCBPTR, 2;
+curscb_ww_done:
+	} else {
+		bmov	SCBPTR, CURRSCB, 2;
+	}
+
+	/*
+	 * The whole list made it.  Clear our tail pointer to indicate
+	 * that the per-target selection queue is now empty.
+	 */
+	cmp	SCB_NEXT[1], SCB_LIST_NULL je select_out_clear_tail;
+
+	/*
+	 * Requeue any SCBs not sent, to the tail of the waiting Q.
+	 * We know that neither the per-TID list nor the list of
+	 * TIDs is empty.  Use this knowledge to our advantage and
+	 * queue the remainder to the tail of the global execution
+	 * queue.
+	 */
+	bmov	REG0, SCB_NEXT, 2;
+select_out_queue_remainder:
+	bmov	SCBPTR, WAITING_TID_TAIL, 2;
+	bmov	SCB_NEXT2, REG0, 2;
+	bmov	WAITING_TID_TAIL, REG0, 2;
+	jmp	select_out_inc_tid_q;
+
+select_out_clear_tail:
+	/*
+	 * Queue any pending MK_MESSAGE SCB for this target now
+	 * that the queue is empty.
+	 */
+	test	SEQ_FLAGS2, PENDING_MK_MESSAGE jz select_out_no_mk_message_scb;
+	mov	A, MK_MESSAGE_SCSIID;
+	cmp	SCB_SCSIID, A jne select_out_no_mk_message_scb;
+	and	SEQ_FLAGS2, ~PENDING_MK_MESSAGE;
+	bmov	REG0, MK_MESSAGE_SCB, 2;
+	jmp select_out_queue_remainder;
+
+select_out_no_mk_message_scb:
+	/*
+	 * Clear this target's execution tail and increment the queue.
+	 */
+	shr	DINDEX, 3, SCB_SCSIID;
+	or	DINDEX, 1;	/* Want only the second byte */
+	mvi	DINDEX[1], ((WAITING_SCB_TAILS) >> 8);
+	mvi	DINDIR, SCB_LIST_NULL;
+select_out_inc_tid_q:
+	bmov	SCBPTR, WAITING_TID_HEAD, 2;
+	bmov	WAITING_TID_HEAD, SCB_NEXT2, 2;
+	cmp	WAITING_TID_HEAD[1], SCB_LIST_NULL jne . + 2;
+	mvi	WAITING_TID_TAIL[1], SCB_LIST_NULL;
+	bmov	SCBPTR, CURRSCB, 2;
+	mvi	CLRSINT0, CLRSELDO;
+	test	LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_mode_cleared;
+	test	LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_mode_cleared;
+
+	/*
+	 * If this is a packetized connection, return to our
+	 * idle_loop and let our interrupt handler deal with
+	 * any connection setup/teardown issues.  The only
+	 * exceptions are the case of MK_MESSAGE and task management
+	 * SCBs.
+	 */
+	if ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0) {
+		/*
+		 * In the A, the LQO manager transitions to LQOSTOP0 even if
+		 * we have selected out with ATN asserted and the target
+		 * REQs in a non-packet phase.
+		 */
+		test 	SCB_CONTROL, MK_MESSAGE jz select_out_no_message;
+		test	SCSISIGO, ATNO jnz select_out_non_packetized;
+select_out_no_message:
+	}
+	test	LQOSTAT2, LQOSTOP0 jz select_out_non_packetized;
+	test	SCB_TASK_MANAGEMENT, 0xFF jz idle_loop;
+	SET_SEQINTCODE(TASKMGMT_FUNC_COMPLETE)
+	jmp	idle_loop;
+
+select_out_non_packetized:
+	/* Non packetized request. */
+	and     SCSISEQ0, ~ENSELO;
+	if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+		/*
+		 * Test to ensure that the bus has not
+		 * already gone free prior to clearing
+		 * any stale busfree status.  This avoids
+		 * a window whereby a busfree just after
+		 * a selection could be missed.
+		 */
+		test	SCSISIGI, BSYI jz . + 2;
+		mvi	CLRSINT1,CLRBUSFREE;
+		or	SIMODE1, ENBUSFREE;
+	}
+	mov	SAVED_SCSIID, SCB_SCSIID;
+	mov	SAVED_LUN, SCB_LUN;
+	mvi	SEQ_FLAGS, NO_CDB_SENT;
+END_CRITICAL;
+	or	SXFRCTL0, SPIOEN;
+
+	/*
+	 * As soon as we get a successful selection, the target
+	 * should go into the message out phase since we have ATN
+	 * asserted.
+	 */
+	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
+
+	/*
+	 * Main loop for information transfer phases.  Wait for the
+	 * target to assert REQ before checking MSG, C/D and I/O for
+	 * the bus phase.
+	 */
+mesgin_phasemis:
+ITloop:
+	call	phase_lock;
+
+	mov	A, LASTPHASE;
+
+	test	A, ~P_DATAIN_DT	jz p_data;
+	cmp	A,P_COMMAND	je p_command;
+	cmp	A,P_MESGOUT	je p_mesgout;
+	cmp	A,P_STATUS	je p_status;
+	cmp	A,P_MESGIN	je p_mesgin;
+
+	SET_SEQINTCODE(BAD_PHASE)
+	jmp	ITloop;			/* Try reading the bus again. */
+
+/*
+ * Command phase.  Set up the DMA registers and let 'er rip.
+ */
+p_command:
+	test	SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
+	SET_SEQINTCODE(PROTO_VIOLATION)
+p_command_okay:
+	test	MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+		jnz p_command_allocate_fifo;
+	/*
+	 * Command retry.  Free our current FIFO and
+	 * re-allocate a FIFO so transfer state is
+	 * reset.
+	 */
+SET_SRC_MODE	M_DFF1;
+SET_DST_MODE	M_DFF1;
+	mvi	DFFSXFRCTL, RSTCHN|CLRSHCNT;
+	SET_MODE(M_SCSI, M_SCSI)
+p_command_allocate_fifo:
+	bmov	ALLOCFIFO_SCBPTR, SCBPTR, 2;
+	call	allocate_fifo;
+SET_SRC_MODE	M_DFF1;
+SET_DST_MODE	M_DFF1;
+	add	NONE, -17, SCB_CDB_LEN;
+	jnc	p_command_embedded;
+p_command_from_host:
+	bmov	HADDR[0], SCB_HOST_CDB_PTR, 9;
+	mvi	SG_CACHE_PRE, LAST_SEG;
+	mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+	jmp	p_command_xfer;
+p_command_embedded:
+	bmov	SHCNT[0], SCB_CDB_LEN,  1;
+	bmov	DFDAT, SCB_CDB_STORE, 16; 
+	mvi	DFCNTRL, SCSIEN;
+p_command_xfer:
+	and	SEQ_FLAGS, ~NO_CDB_SENT;
+	if ((ahd->features & AHD_FAST_CDB_DELIVERY) != 0) {
+		/*
+		 * To speed up CDB delivery in Rev B, all CDB acks
+		 * are "released" to the output sync as soon as the
+		 * command phase starts.  There is only one problem
+		 * with this approach.  If the target changes phase
+		 * before all data are sent, we have left over acks
+		 * that can go out on the bus in a data phase.  Due
+		 * to other chip contraints, this only happens if
+		 * the target goes to data-in, but if the acks go
+		 * out before we can test SDONE, we'll think that
+		 * the transfer has completed successfully.  Work
+		 * around this by taking advantage of the 400ns or
+		 * 800ns dead time between command phase and the REQ
+		 * of the new phase.  If the transfer has completed
+		 * successfully, SCSIEN should fall *long* before we
+		 * see a phase change.  We thus treat any phasemiss
+		 * that occurs before SCSIEN falls as an incomplete
+		 * transfer.
+		 */
+		test	SSTAT1, PHASEMIS jnz p_command_xfer_failed;
+		test	DFCNTRL, SCSIEN jnz . - 1;
+	} else {
+		test	DFCNTRL, SCSIEN jnz .;
+	}
+	/*
+	 * DMA Channel automatically disabled.
+	 * Don't allow a data phase if the command
+	 * was not fully transferred.
+	 */
+	test	SSTAT2, SDONE jnz ITloop;
+p_command_xfer_failed:
+	or	SEQ_FLAGS, NO_CDB_SENT;
+	jmp	ITloop;
+
+
+/*
+ * Status phase.  Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+SET_SRC_MODE	M_SCSI;
+SET_DST_MODE	M_SCSI;
+p_status:
+	test	SEQ_FLAGS,NOT_IDENTIFIED jnz mesgin_proto_violation;
+p_status_okay:
+	mov	SCB_SCSI_STATUS, SCSIDAT;
+	or	SCB_CONTROL, STATUS_RCVD;
+	jmp	ITloop;
+
+/*
+ * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target.  The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte.  This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own.  If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ * 
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in response to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+	/* Turn on ATN for the retry */
+	mvi	SCSISIGO, ATNO;
+p_mesgout:
+	mov	SINDEX, MSG_OUT;
+	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+	test	SCB_CONTROL,MK_MESSAGE	jnz host_message_loop;
+p_mesgout_identify:
+	or	SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
+	test	SCB_CONTROL, DISCENB jnz . + 2;
+	and	SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_NONPACKET_TAG as the tag value.
+ */
+p_mesgout_tag:
+	test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
+	mov	SCSIDAT, SINDEX;	/* Send the identify message */
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
+	and	SCSIDAT,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
+	mov	SCBPTR jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
+	jmp	host_message_loop;
+
+p_mesgout_onebyte:
+	mvi	CLRSINT1, CLRATNO;
+	mov	SCSIDAT, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGOUT	je p_mesgout_retry;
+
+p_mesgout_done:
+	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
+	mov	LAST_MSG, MSG_OUT;
+	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
+	jmp	ITloop;
+
+/*
+ * Message in phase.  Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+	/* read the 1st message byte */
+	mvi	ACCUM		call inb_first;
+
+	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
+	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
+	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
+	cmp	ALLZEROS,A		je mesgin_complete;
+	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
+	cmp	A,MSG_IGN_WIDE_RESIDUE	je mesgin_ign_wide_residue;
+	cmp	A,MSG_NOOP		je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine.  To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop.  Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+	call	phase_lock;	/* Benign the first time through. */
+	SET_SEQINTCODE(HOST_MSG_LOOP)
+	cmp	RETURN_1, EXIT_MSG_LOOP	je ITloop;
+	cmp	RETURN_1, CONT_MSG_LOOP_WRITE	jne . + 3;
+	mov	SCSIDAT, RETURN_2;
+	jmp	host_message_loop;
+	/* Must be CONT_MSG_LOOP_READ */
+	mov	NONE, SCSIDAT;	/* ACK Byte */
+	jmp	host_message_loop;
+
+mesgin_ign_wide_residue:
+	mov	SAVED_MODE, MODE_PTR;
+	SET_MODE(M_SCSI, M_SCSI)
+	shr	NEGOADDR, 4, SAVED_SCSIID;
+	mov	A, NEGCONOPTS;
+	RESTORE_MODE(SAVED_MODE)
+	test	A, WIDEXFER jz mesgin_reject;
+	/* Pull the residue byte */
+	mvi	REG0	call inb_next;
+	cmp	REG0, 0x01 jne mesgin_reject;
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+	test	SCB_TASK_ATTRIBUTE, SCB_XFERLEN_ODD jnz mesgin_done;
+	SET_SEQINTCODE(IGN_WIDE_RES)
+	jmp	mesgin_done;
+
+mesgin_proto_violation:
+	SET_SEQINTCODE(PROTO_VIOLATION)
+	jmp	mesgin_done;
+mesgin_reject:
+	mvi	MSG_MESSAGE_REJECT	call mk_mesg;
+mesgin_done:
+	mov	NONE,SCSIDAT;		/*dummy read from latch to ACK*/
+	jmp	ITloop;
+
+#define INDEX_DISC_LIST(scsiid, lun)					\
+	and	A, 0xC0, scsiid;					\
+	or	SCBPTR, A, lun;						\
+	clr	SCBPTR[1];						\
+	and	SINDEX, 0x30, scsiid;					\
+	shr	SINDEX, 3;	/* Multiply by 2 */			\
+	add	SINDEX, (SCB_DISCONNECTED_LISTS & 0xFF);		\
+	mvi	SINDEX[1], ((SCB_DISCONNECTED_LISTS >> 8) & 0xFF)
+
+mesgin_identify:
+	/*
+	 * Determine whether a target is using tagged or non-tagged
+	 * transactions by first looking at the transaction stored in
+	 * the per-device, disconnected array.  If there is no untagged
+	 * transaction for this target, this must be a tagged transaction.
+	 */
+	and	SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+	INDEX_DISC_LIST(SAVED_SCSIID, SAVED_LUN);
+	bmov	DINDEX, SINDEX, 2;
+	bmov	REG0, SINDIR, 2;
+	cmp	REG0[1], SCB_LIST_NULL je snoop_tag;
+	/* Untagged.  Clear the busy table entry and setup the SCB. */
+	bmov	DINDIR, ALLONES, 2;
+	bmov	SCBPTR, REG0, 2;
+	jmp	setup_SCB;
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB.  After receiving the tag, look for the SCB at SCB locations tag and
+ * tag + 256.
+ */
+snoop_tag:
+	if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x80;
+	}
+	mov	NONE, SCSIDAT;		/* ACK Identify MSG */
+	call	phase_lock;
+	if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x1;
+	}
+	cmp	LASTPHASE, P_MESGIN	jne not_found_ITloop;
+	if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x2;
+	}
+	cmp	SCSIBUS, MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+	clr	SCBPTR[1];
+	mvi	SCBPTR	call inb_next;	/* tag value */
+verify_scb:
+	test	SCB_CONTROL,DISCONNECTED jz verify_other_scb;
+	mov	A, SAVED_SCSIID;
+	cmp	SCB_SCSIID, A jne verify_other_scb;
+	mov	A, SAVED_LUN;
+	cmp	SCB_LUN, A je setup_SCB_disconnected;
+verify_other_scb:
+	xor	SCBPTR[1], 1;
+	test	SCBPTR[1], 0xFF jnz verify_scb;
+	jmp	not_found;
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+	if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x10;
+	}
+	test	SCB_CONTROL,DISCONNECTED jz not_found;
+setup_SCB_disconnected:
+	and	SCB_CONTROL,~DISCONNECTED;
+	clr	SEQ_FLAGS;	/* make note of IDENTIFY */
+	test	SCB_SGPTR, SG_LIST_NULL jnz . + 3;
+	bmov	ALLOCFIFO_SCBPTR, SCBPTR, 2;
+	call	allocate_fifo;
+	/* See if the host wants to send a message upon reconnection */
+	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+	mvi	HOST_MSG	call mk_mesg;
+	jmp	mesgin_done;
+
+not_found:
+	SET_SEQINTCODE(NO_MATCH)
+	jmp	mesgin_done;
+
+not_found_ITloop:
+	SET_SEQINTCODE(NO_MATCH)
+	jmp	ITloop;
+
+/*
+ * We received a "command complete" message.  Put the SCB on the complete
+ * queue and trigger a completion interrupt via the idle loop.  Before doing
+ * so, check to see if there is a residual or the status byte is something
+ * other than STATUS_GOOD (0).  In either of these conditions, we upload the
+ * SCB back to the host so it can process this information.
+ */
+mesgin_complete:
+
+	/*
+	 * If ATN is raised, we still want to give the target a message.
+	 * Perhaps there was a parity error on this last message byte.
+	 * Either way, the target should take us to message out phase
+	 * and then attempt to complete the command again.  We should use a
+	 * critical section here to guard against a timeout triggering
+	 * for this command and setting ATN while we are still processing
+	 * the completion.
+	test	SCSISIGI, ATNI jnz mesgin_done;
+	 */
+
+	/*
+	 * If we are identified and have successfully sent the CDB,
+	 * any status will do.  Optimize this fast path.
+	 */
+	test	SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
+	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted;
+
+	/*
+	 * If the target never sent an identify message but instead went
+	 * to mesgin to give an invalid message, let the host abort us.
+	 */
+	test	SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
+
+	/*
+	 * If we recevied good status but never successfully sent the
+	 * cdb, abort the command.
+	 */
+	test	SCB_SCSI_STATUS,0xff	jnz complete_accepted;
+	test	SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;
+complete_accepted:
+
+	/*
+	 * See if we attempted to deliver a message but the target ingnored us.
+	 */
+	test	SCB_CONTROL, MK_MESSAGE jz complete_nomsg;
+	SET_SEQINTCODE(MKMSG_FAILED)
+complete_nomsg:
+	call	queue_scb_completion;
+	jmp	await_busfree;
+
+BEGIN_CRITICAL;
+freeze_queue:
+	/* Cancel any pending select-out. */
+	test	SSTAT0, SELDO|SELINGO jnz . + 2;
+	and	SCSISEQ0, ~ENSELO;
+	mov	ACCUM_SAVE, A;
+	clr	A;
+	add	QFREEZE_COUNT, 1;
+	adc	QFREEZE_COUNT[1], A;
+	or	SEQ_FLAGS2, SELECTOUT_QFROZEN;
+	mov	A, ACCUM_SAVE ret;
+END_CRITICAL;
+
+/*
+ * Complete the current FIFO's SCB if data for this same
+ * SCB is not transferring in the other FIFO.
+ */
+SET_SRC_MODE	M_DFF1;
+SET_DST_MODE	M_DFF1;
+pkt_complete_scb_if_fifos_idle:
+	bmov	ARG_1, SCBPTR, 2;
+	mvi	DFFSXFRCTL, CLRCHN;
+	SET_MODE(M_SCSI, M_SCSI)
+	bmov	SCBPTR, ARG_1, 2;
+	test	SCB_FIFO_USE_COUNT, 0xFF jnz return;
+queue_scb_completion:
+	test	SCB_SCSI_STATUS,0xff	jnz bad_status;
+	/*
+	 * Check for residuals
+	 */
+	test	SCB_SGPTR, SG_LIST_NULL jnz complete;	/* No xfer */
+	test	SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+	test	SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+complete:
+BEGIN_CRITICAL;
+	bmov	SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+	bmov	COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+END_CRITICAL;
+bad_status:
+	cmp	SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb;
+	call	freeze_queue;
+upload_scb:
+	/*
+	 * Restore SCB TAG since we reuse this field
+	 * in the sequencer.  We don't want to corrupt
+	 * it on the host.
+	 */
+	bmov	SCB_TAG, SCBPTR, 2;
+BEGIN_CRITICAL;
+	or	SCB_SGPTR, SG_STATUS_VALID;
+	mvi	SCB_NEXT_COMPLETE[1], SCB_LIST_NULL;
+	cmp	COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne add_dma_scb_tail;
+	bmov	COMPLETE_DMA_SCB_HEAD, SCBPTR, 2;
+	bmov	COMPLETE_DMA_SCB_TAIL, SCBPTR, 2 ret;
+add_dma_scb_tail:
+	bmov	REG0, SCBPTR, 2;
+	bmov	SCBPTR, COMPLETE_DMA_SCB_TAIL, 2;
+	bmov	SCB_NEXT_COMPLETE, REG0, 2;
+	bmov	COMPLETE_DMA_SCB_TAIL, REG0, 2 ret;
+END_CRITICAL;
+
+/*
+ * Is it a disconnect message?  Set a flag in the SCB to remind us
+ * and await the bus going free.  If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselection.
+ */
+mesgin_disconnect:
+	/*
+	 * If ATN is raised, we still want to give the target a message.
+	 * Perhaps there was a parity error on this last message byte
+	 * or we want to abort this command.  Either way, the target
+	 * should take us to message out phase and then attempt to
+	 * disconnect again.
+	 * XXX - Wait for more testing.
+	test	SCSISIGI, ATNI jnz mesgin_done;
+	 */
+	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT
+		jnz mesgin_proto_violation;
+	or	SCB_CONTROL,DISCONNECTED;
+	test	SCB_CONTROL, TAG_ENB jnz await_busfree;
+queue_disc_scb:
+	bmov	REG0, SCBPTR, 2;
+	INDEX_DISC_LIST(SAVED_SCSIID, SAVED_LUN);
+	bmov	DINDEX, SINDEX, 2;
+	bmov	DINDIR, REG0, 2;
+	bmov	SCBPTR, REG0, 2;
+	/* FALLTHROUGH */
+await_busfree:
+	and	SIMODE1, ~ENBUSFREE;
+	if ((ahd->bugs & AHD_BUSFREEREV_BUG) == 0) {
+		/*
+		 * In the BUSFREEREV_BUG case, the
+		 * busfree status was cleared at the
+		 * beginning of the connection.
+		 */
+		mvi	CLRSINT1,CLRBUSFREE;
+	}
+	mov	NONE, SCSIDAT;		/* Ack the last byte */
+	test	MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+		jnz await_busfree_not_m_dff;
+SET_SRC_MODE	M_DFF1;
+SET_DST_MODE	M_DFF1;
+await_busfree_clrchn:
+	mvi	DFFSXFRCTL, CLRCHN;
+await_busfree_not_m_dff:
+	/* clear target specific flags */
+	mvi	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT;
+	test	SSTAT1,REQINIT|BUSFREE	jz .;
+	/*
+	 * We only set BUSFREE status once either a new
+	 * phase has been detected or we are really
+	 * BUSFREE.  This allows the driver to know
+	 * that we are active on the bus even though
+	 * no identified transaction exists should a
+	 * timeout occur while awaiting busfree.
+	 */
+	mvi	LASTPHASE, P_BUSFREE;
+	test	SSTAT1, BUSFREE jnz idle_loop;
+	SET_SEQINTCODE(MISSED_BUSFREE)
+
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them.  This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ * Ack the message as soon as possible.
+ */
+SET_SRC_MODE	M_DFF1;
+SET_DST_MODE	M_DFF1;
+mesgin_sdptrs:
+	mov	NONE,SCSIDAT;		/*dummy read from latch to ACK*/
+	test	SEQ_FLAGS, DPHASE	jz ITloop;
+	call	save_pointers;
+	jmp	ITloop;
+
+save_pointers:
+	/*
+	 * If we are asked to save our position at the end of the
+	 * transfer, just mark us at the end rather than perform a
+	 * full save.
+	 */
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz save_pointers_full;
+	or	SCB_SGPTR, SG_LIST_NULL ret;
+
+save_pointers_full:
+	/*
+	 * The SCB_DATAPTR becomes the current SHADDR.
+	 * All other information comes directly from our residual
+	 * state.
+	 */
+	bmov	SCB_DATAPTR, SHADDR, 8;
+	bmov	SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8 ret;
+
+/*
+ * Restore pointers message?  Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.  We also reset/reallocate the FIFO to make
+ * sure we have a clean start for the next data or command phase.
+ */
+mesgin_rdptrs:
+	and	SEQ_FLAGS, ~DPHASE;
+	test	MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz msgin_rdptrs_get_fifo;
+	mvi	DFFSXFRCTL, RSTCHN|CLRSHCNT;
+	SET_MODE(M_SCSI, M_SCSI)
+msgin_rdptrs_get_fifo:
+	call	allocate_fifo;
+	jmp	mesgin_done;
+
+phase_lock:     
+	if ((ahd->bugs & AHD_EARLY_REQ_BUG) != 0) {
+		/*
+		 * Don't ignore persistent REQ assertions just because
+		 * they were asserted within the bus settle delay window.
+		 * This allows us to tolerate devices like the GEM318
+		 * that violate the SCSI spec.  We are careful not to
+		 * count REQ while we are waiting for it to fall during
+		 * an async phase due to our asserted ACK.  Each
+		 * sequencer instruction takes ~25ns, so the REQ must
+		 * last at least 100ns in order to be counted as a true
+		 * REQ.
+		 */
+		test	SCSIPHASE, 0xFF jnz phase_locked;
+		test	SCSISIGI, ACKI jnz phase_lock;
+		test	SCSISIGI, REQI jz phase_lock;
+		test	SCSIPHASE, 0xFF jnz phase_locked;
+		test	SCSISIGI, ACKI jnz phase_lock;
+		test	SCSISIGI, REQI jz phase_lock;
+phase_locked:
+	} else {
+		test	SCSIPHASE, 0xFF jz .;
+	}
+	test	SSTAT1, SCSIPERR jnz phase_lock;
+phase_lock_latch_phase:
+	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * An ACK is not sent on input from the target until SCSIDATL is read from.
+ * So we wait until SCSIDATL is latched (the usual way), then read the data
+ * byte directly off the bus using SCSIBUSL.  When we have pulled the ATN
+ * line, or we just want to acknowledge the byte, then we do a dummy read
+ * from SCISDATL.  The SCSI spec guarantees that the target will hold the
+ * data byte on the bus until we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called.  inb_{first,next}
+ * use the same calling convention as inb.
+ */
+inb_next:
+	mov	NONE,SCSIDAT;		/*dummy read from latch to ACK*/
+inb_next_wait:
+	/*
+	 * If there is a parity error, wait for the kernel to
+	 * see the interrupt and prepare our message response
+	 * before continuing.
+	 */
+	test	SCSIPHASE, 0xFF jz .;
+	test	SSTAT1, SCSIPERR jnz inb_next_wait;
+inb_next_check_phase:
+	and	LASTPHASE, PHASE_MASK, SCSISIGI;
+	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+	clr	DINDEX[1];
+	mov	DINDEX,SINDEX;
+	mov	DINDIR,SCSIBUS	ret;		/*read byte directly from bus*/
+inb_last:
+	mov	NONE,SCSIDAT ret;		/*dummy read from latch to ACK*/
+
+mk_mesg:
+	mvi	SCSISIGO, ATNO;
+	mov	MSG_OUT,SINDEX ret;
+
+SET_SRC_MODE	M_DFF1;
+SET_DST_MODE	M_DFF1;
+disable_ccsgen:
+	test	SG_STATE, FETCH_INPROG jz disable_ccsgen_fetch_done;
+	clr	CCSGCTL;
+disable_ccsgen_fetch_done:
+	clr	SG_STATE ret;
+
+service_fifo:
+	/*
+	 * Do we have any prefetch left???
+	 */
+	test	SG_STATE, SEGS_AVAIL jnz idle_sg_avail;
+
+	/*
+	 * Can this FIFO have access to the S/G cache yet?
+	 */
+	test	CCSGCTL, SG_CACHE_AVAIL jz return;
+
+	/* Did we just finish fetching segs? */
+	test	CCSGCTL, CCSGDONE jnz idle_sgfetch_complete;
+
+	/* Are we actively fetching segments? */
+	test	CCSGCTL, CCSGENACK jnz return;
+
+	/*
+	 * Should the other FIFO get the S/G cache first?  If
+	 * both FIFOs have been allocated since we last checked
+	 * any FIFO, it is important that we service a FIFO
+	 * that is not actively on the bus first.  This guarantees
+	 * that a FIFO will be freed to handle snapshot requests for
+	 * any FIFO that is still on the bus.  Chips with RTI do not
+	 * perform snapshots, so don't bother with this test there.
+	 */
+	if ((ahd->features & AHD_RTI) == 0) {
+		/*
+		 * If we're not still receiving SCSI data,
+		 * it is safe to allocate the S/G cache to
+		 * this FIFO.
+		 */
+		test	DFCNTRL, SCSIEN jz idle_sgfetch_start;
+
+		/*
+		 * Switch to the other FIFO.  Non-RTI chips
+		 * also have the "set mode" bug, so we must
+		 * disable interrupts during the switch.
+		 */
+		mvi	SEQINTCTL, INTVEC1DSL;
+		xor	MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+
+		/*
+		 * If the other FIFO needs loading, then it
+		 * must not have claimed the S/G cache yet
+		 * (SG_CACHE_AVAIL would have been cleared in
+		 * the original FIFO mode and we test this above).
+		 * Return to the idle loop so we can process the
+		 * FIFO not currently on the bus first.
+		 */
+		test	SG_STATE, LOADING_NEEDED jz idle_sgfetch_okay;
+		clr	SEQINTCTL ret;
+idle_sgfetch_okay:
+		xor	MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+		clr	SEQINTCTL;
+	}
+
+idle_sgfetch_start:
+	/*
+	 * We fetch a "cacheline aligned" and sized amount of data
+	 * so we don't end up referencing a non-existent page.
+	 * Cacheline aligned is in quotes because the kernel will
+	 * set the prefetch amount to a reasonable level if the
+	 * cacheline size is unknown.
+	 */
+	bmov	SGHADDR, SCB_RESIDUAL_SGPTR, 4;
+	mvi	SGHCNT, SG_PREFETCH_CNT;
+	if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) {
+		/*
+		 * Need two instructions between "touches" of SGHADDR.
+		 */
+		nop;
+	}
+	and	SGHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+	mvi	CCSGCTL, CCSGEN|CCSGRESET;
+	or	SG_STATE, FETCH_INPROG ret;
+idle_sgfetch_complete:
+	/*
+	 * Guard against SG_CACHE_AVAIL activating during sg fetch
+	 * request in the other FIFO.
+	 */
+	test	SG_STATE, FETCH_INPROG jz return;
+	clr	CCSGCTL;
+	and	CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+	mvi	SG_STATE, SEGS_AVAIL|LOADING_NEEDED;
+idle_sg_avail:
+	/* Does the hardware have space for another SG entry? */
+	test	DFSTATUS, PRELOAD_AVAIL jz return;
+	/*
+	 * On the A, preloading a segment before HDMAENACK
+	 * comes true can clobber the shadow address of the
+	 * first segment in the S/G FIFO.  Wait until it is
+	 * safe to proceed.
+	 */
+	if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) == 0) {
+		test	DFCNTRL, HDMAENACK jz return;
+	}
+	if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+		bmov	HADDR, CCSGRAM, 8;
+	} else {
+		bmov 	HADDR, CCSGRAM, 4;
+	}
+	bmov	HCNT, CCSGRAM, 3;
+	bmov	SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+	if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+		and	HADDR[4], SG_HIGH_ADDR_BITS, SCB_RESIDUAL_DATACNT[3];
+	}
+	if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+		/* Skip 4 bytes of pad. */
+		add	CCSGADDR, 4;
+	}
+sg_advance:
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+	adc	SCB_RESIDUAL_SGPTR[1],A;
+	adc	SCB_RESIDUAL_SGPTR[2],A;
+	adc	SCB_RESIDUAL_SGPTR[3],A;
+	mov	SINDEX, SCB_RESIDUAL_SGPTR[0];
+	test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 3;
+	or	SINDEX, LAST_SEG;
+	clr	SG_STATE;
+	mov	SG_CACHE_PRE, SINDEX;
+	if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0) {
+		/*
+		 * Use SCSIENWRDIS so that SCSIEN is never
+		 * modified by this operation.
+		 */
+		or	DFCNTRL, PRELOADEN|HDMAEN|SCSIENWRDIS;
+	} else {
+		or	DFCNTRL, PRELOADEN|HDMAEN;
+	}
+	/*
+	 * Do we have another segment in the cache?
+	 */
+	add	NONE, SG_PREFETCH_CNT_LIMIT, CCSGADDR;
+	jnc	return;
+	and	SG_STATE, ~SEGS_AVAIL ret;
+
+/*
+ * Initialize the DMA address and counter from the SCB.
+ */
+load_first_seg:
+	bmov	HADDR, SCB_DATAPTR, 11;
+	and	REG_ISR, ~SG_FULL_RESID, SCB_SGPTR[0];
+	test	SCB_DATACNT[3], SG_LAST_SEG jz . + 2;
+	or	REG_ISR, LAST_SEG;
+	mov	SG_CACHE_PRE, REG_ISR;
+	mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+	/*
+	 * Since we've are entering a data phase, we will
+	 * rely on the SCB_RESID* fields.  Initialize the
+	 * residual and clear the full residual flag.
+	 */
+	and	SCB_SGPTR[0], ~SG_FULL_RESID;
+	bmov	SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
+	/* If we need more S/G elements, tell the idle loop */
+	test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz . + 2;
+	mvi	SG_STATE, LOADING_NEEDED ret;
+	clr	SG_STATE ret;
+
+p_data_handle_xfer:
+	call	setjmp;
+	test	SG_STATE, LOADING_NEEDED jnz service_fifo;
+p_data_clear_handler:
+	or	LONGJMP_ADDR[1], INVALID_ADDR ret;
+
+p_data:
+	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT	jz p_data_allowed;
+	SET_SEQINTCODE(PROTO_VIOLATION)
+p_data_allowed:
+ 
+	test	SEQ_FLAGS, DPHASE	jz data_phase_initialize;
+
+	/*
+	 * If we re-enter the data phase after going through another
+	 * phase, our transfer location has almost certainly been
+	 * corrupted by the interveining, non-data, transfers.  Ask
+	 * the host driver to fix us up based on the transfer residual
+	 * unless we already know that we should be bitbucketing.
+	 */
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket;
+	SET_SEQINTCODE(PDATA_REINIT)
+	jmp	data_phase_inbounds;
+
+p_data_bitbucket:
+	/*
+	 * Turn on `Bit Bucket' mode, wait until the target takes
+	 * us to another phase, and then notify the host.
+	 */
+	mov	SAVED_MODE, MODE_PTR;
+	test	MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+		jnz bitbucket_not_m_dff;
+	/*
+	 * Ensure that any FIFO contents are cleared out and the
+	 * FIFO free'd prior to starting the BITBUCKET.  BITBUCKET
+	 * doesn't discard data already in the FIFO.
+	 */
+	mvi	DFFSXFRCTL, RSTCHN|CLRSHCNT;
+	SET_MODE(M_SCSI, M_SCSI)
+bitbucket_not_m_dff:
+	or	SXFRCTL1,BITBUCKET;
+	/* Wait for non-data phase. */
+	test	SCSIPHASE, ~DATA_PHASE_MASK jz .;
+	and	SXFRCTL1, ~BITBUCKET;
+	RESTORE_MODE(SAVED_MODE)
+SET_SRC_MODE	M_DFF1;
+SET_DST_MODE	M_DFF1;
+	SET_SEQINTCODE(DATA_OVERRUN)
+	jmp	ITloop;
+
+data_phase_initialize:
+	test	SCB_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket;
+	call	load_first_seg;
+data_phase_inbounds:
+	/* We have seen a data phase at least once. */
+	or	SEQ_FLAGS, DPHASE;
+	mov	SAVED_MODE, MODE_PTR;
+	test	SG_STATE, LOADING_NEEDED jz data_group_dma_loop;
+	call	p_data_handle_xfer;
+data_group_dma_loop:
+	/*
+	 * The transfer is complete if either the last segment
+	 * completes or the target changes phase.  Both conditions
+	 * will clear SCSIEN.
+	 */
+	call	idle_loop_service_fifos;
+	call	idle_loop_cchan;
+	call	idle_loop_gsfifo;
+	RESTORE_MODE(SAVED_MODE)
+	test	DFCNTRL, SCSIEN jnz data_group_dma_loop;
+
+data_group_dmafinish:
+	/*
+	 * The transfer has terminated either due to a phase
+	 * change, and/or the completion of the last segment.
+	 * We have two goals here.  Do as much other work
+	 * as possible while the data fifo drains on a read
+	 * and respond as quickly as possible to the standard
+	 * messages (save data pointers/disconnect and command
+	 * complete) that usually follow a data phase.
+	 */
+	call	calc_residual;
+
+	/*
+	 * Go ahead and shut down the DMA engine now.
+	 */
+	test	DFCNTRL, DIRECTION jnz data_phase_finish;
+data_group_fifoflush:
+	if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+		or	DFCNTRL, FIFOFLUSH;
+	}
+	/*
+	 * We have enabled the auto-ack feature.  This means
+	 * that the controller may have already transferred
+	 * some overrun bytes into the data FIFO and acked them
+	 * on the bus.  The only way to detect this situation is
+	 * to wait for LAST_SEG_DONE to come true on a completed
+	 * transfer and then test to see if the data FIFO is
+	 * non-empty.  We know there is more data yet to transfer
+	 * if SG_LIST_NULL is not yet set, thus there cannot be
+	 * an overrun.
+	 */
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_finish;
+	test	SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
+	test	DFSTATUS, FIFOEMP jnz data_phase_finish;
+	/* Overrun */
+	jmp	p_data;
+data_phase_finish:
+	/*
+	 * If the target has left us in data phase, loop through
+	 * the dma code again.  We will only loop if there is a
+	 * data overrun.  
+	 */
+	if ((ahd->flags & AHD_TARGETROLE) != 0) {
+		test	SSTAT0, TARGET jnz data_phase_done;
+	}
+	if ((ahd->flags & AHD_INITIATORROLE) != 0) {
+		test	SSTAT1, REQINIT jz .;
+		test	SCSIPHASE, DATA_PHASE_MASK jnz p_data;
+	}
+
+data_phase_done:
+	/* Kill off any pending prefetch */
+	call	disable_ccsgen;
+	or 	LONGJMP_ADDR[1], INVALID_ADDR;
+
+	if ((ahd->flags & AHD_TARGETROLE) != 0) {
+		test	SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+		/*
+		and	SEQ_FLAGS, ~DPHASE_PENDING;
+		 * For data-in phases, wait for any pending acks from the
+		 * initiator before changing phase.  We only need to
+		 * send Ignore Wide Residue messages for data-in phases.
+		test	DFCNTRL, DIRECTION jz target_ITloop;
+		test	SSTAT1, REQINIT	jnz .;
+		test	SCB_TASK_ATTRIBUTE, SCB_XFERLEN_ODD jz target_ITloop;
+		SET_MODE(M_SCSI, M_SCSI)
+		test	NEGCONOPTS, WIDEXFER jz target_ITloop;
+		 */
+		/*
+		 * Issue an Ignore Wide Residue Message.
+		mvi	P_MESGIN|BSYO call change_phase;
+		mvi	MSG_IGN_WIDE_RESIDUE call target_outb;
+		mvi	1 call target_outb;
+		jmp	target_ITloop;
+		 */
+	} else {
+		jmp	ITloop;
+	}
+
+/*
+ * We assume that, even though data may still be
+ * transferring to the host, that the SCSI side of
+ * the DMA engine is now in a static state.  This
+ * allows us to update our notion of where we are
+ * in this transfer.
+ *
+ * If, by chance, we stopped before being able
+ * to fetch additional segments for this transfer,
+ * yet the last S/G was completely exhausted,
+ * call our idle loop until it is able to load
+ * another segment.  This will allow us to immediately
+ * pickup on the next segment on the next data phase.
+ *
+ * If we happened to stop on the last segment, then
+ * our residual information is still correct from
+ * the idle loop and there is no need to perform
+ * any fixups.
+ */
+residual_before_last_seg:
+	test    MDFFSTAT, SHVALID	jnz sgptr_fixup;
+	/*
+	 * Can never happen from an interrupt as the packetized
+	 * hardware will only interrupt us once SHVALID or
+	 * LAST_SEG_DONE.
+	 */
+	call	idle_loop_service_fifos;
+	RESTORE_MODE(SAVED_MODE)
+	/* FALLTHROUGH */
+calc_residual:
+	test	SG_CACHE_SHADOW, LAST_SEG jz residual_before_last_seg;
+	/* Record if we've consumed all S/G entries */
+	test	MDFFSTAT, SHVALID	jz . + 2;
+	bmov	SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;
+	or	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL ret;
+
+sgptr_fixup:
+	/*
+	 * Fixup the residual next S/G pointer.  The S/G preload
+	 * feature of the chip allows us to load two elements
+	 * in addition to the currently active element.  We
+	 * store the bottom byte of the next S/G pointer in
+	 * the SG_CACHE_PTR register so we can restore the
+	 * correct value when the DMA completes.  If the next
+	 * sg ptr value has advanced to the point where higher
+	 * bytes in the address have been affected, fix them
+	 * too.
+	 */
+	test	SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+	test	SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+	add	SCB_RESIDUAL_SGPTR[1], -1;
+	adc	SCB_RESIDUAL_SGPTR[2], -1; 
+	adc	SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+	and	SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+	clr	SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
+	bmov	SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;
+
+export timer_isr:
+	call	issue_cmdcmplt;
+	mvi	CLRSEQINTSTAT, CLRSEQ_SWTMRTO;
+	if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) {
+		/*
+		 * In H2A4, the mode pointer is not saved
+		 * for intvec2, but is restored on iret.
+		 * This can lead to the restoration of a
+		 * bogus mode ptr.  Manually clear the
+		 * intmask bits and do a normal return
+		 * to compensate.
+		 */
+		and	SEQINTCTL, ~(INTMASK2|INTMASK1) ret;
+	} else {
+		or	SEQINTCTL, IRET ret;
+	}
+
+export seq_isr:
+	if ((ahd->features & AHD_RTI) == 0) {
+		/*
+		 * On RevA Silicon, if the target returns us to data-out
+		 * after we have already trained for data-out, it is
+		 * possible for us to transition the free running clock to
+		 * data-valid before the required 100ns P1 setup time (8 P1
+		 * assertions in fast-160 mode).  This will only happen if
+		 * this L-Q is a continuation of a data transfer for which
+		 * we have already prefetched data into our FIFO (LQ/Data
+		 * followed by LQ/Data for the same write transaction).
+		 * This can cause some target implementations to miss the
+		 * first few data transfers on the bus.  We detect this
+		 * situation by noticing that this is the first data transfer
+		 * after an LQ (LQIWORKONLQ true), that the data transfer is
+		 * a continuation of a transfer already setup in our FIFO
+		 * (SAVEPTRS interrupt), and that the transaction is a write
+		 * (DIRECTION set in DFCNTRL). The delay is performed by
+		 * disabling SCSIEN until we see the first REQ from the
+		 * target.
+		 * 
+		 * First instruction in an ISR cannot be a branch on
+		 * Rev A.  Snapshot LQISTAT2 so the status is not missed
+		 * and deffer the test by one instruction.
+		 */
+		mov	REG_ISR, LQISTAT2;
+		test	REG_ISR, LQIWORKONLQ jz main_isr;
+		test	SEQINTSRC, SAVEPTRS  jz main_isr;
+		test	LONGJMP_ADDR[1], INVALID_ADDR jz saveptr_active_fifo;
+		/*
+		 * Switch to the active FIFO after clearing the snapshot
+		 * savepointer in the current FIFO.  We do this so that
+		 * a pending CTXTDONE or SAVEPTR is visible in the active
+		 * FIFO.  This status is the only way we can detect if we
+		 * have lost the race (e.g. host paused us) and our attempts
+		 * to disable the channel occurred after all REQs were
+		 * already seen and acked (REQINIT never comes true).
+		 */
+		mvi	DFFSXFRCTL, CLRCHN;
+		xor	MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+		test	DFCNTRL, DIRECTION jz interrupt_return;
+		and	DFCNTRL, ~SCSIEN;
+snapshot_wait_data_valid:
+		test	SEQINTSRC, (CTXTDONE|SAVEPTRS) jnz interrupt_return;
+		test	SSTAT1, REQINIT	jz snapshot_wait_data_valid;
+snapshot_data_valid:
+		or	DFCNTRL, SCSIEN;
+		or	SEQINTCTL, IRET ret;
+snapshot_saveptr:
+		mvi	DFFSXFRCTL, CLRCHN;
+		or	SEQINTCTL, IRET ret;
+main_isr:
+	}
+	test	SEQINTSRC, CFG4DATA	jnz cfg4data_intr;
+	test	SEQINTSRC, CFG4ISTAT	jnz cfg4istat_intr;
+	test	SEQINTSRC, SAVEPTRS	jnz saveptr_intr;
+	test	SEQINTSRC, CFG4ICMD	jnz cfg4icmd_intr;
+	SET_SEQINTCODE(INVALID_SEQINT)
+
+/*
+ * There are two types of save pointers interrupts:
+ * The first is a snapshot save pointers where the current FIFO is not
+ * active and contains a snapshot of the current poniter information.
+ * This happens between packets in a stream for a single L_Q.  Since we
+ * are not performing a pointer save, we can safely clear the channel
+ * so it can be used for other transactions.  On RTI capable controllers,
+ * where snapshots can, and are, disabled, the code to handle this type
+ * of snapshot is not active.
+ *
+ * The second case is a save pointers on an active FIFO which occurs
+ * if the target changes to a new L_Q or busfrees/QASes and the transfer
+ * has a residual.  This should occur coincident with a ctxtdone.  We
+ * disable the interrupt and allow our active routine to handle the
+ * save.
+ */
+saveptr_intr:
+	if ((ahd->features & AHD_RTI) == 0) {
+		test	LONGJMP_ADDR[1], INVALID_ADDR jnz snapshot_saveptr;
+	}
+saveptr_active_fifo:
+	and	SEQIMODE, ~ENSAVEPTRS;
+	or	SEQINTCTL, IRET ret;
+
+cfg4data_intr:
+	test	SCB_SGPTR[0], SG_LIST_NULL jnz pkt_handle_overrun_inc_use_count;
+	call	load_first_seg;
+	call	pkt_handle_xfer;
+	inc	SCB_FIFO_USE_COUNT;
+interrupt_return:
+	or	SEQINTCTL, IRET ret;
+
+cfg4istat_intr:
+	call	freeze_queue;
+	add	NONE, -13, SCB_CDB_LEN;
+	jnc	cfg4istat_have_sense_addr;
+	test	SCB_CDB_LEN, SCB_CDB_LEN_PTR jnz cfg4istat_have_sense_addr;
+	/*
+	 * Host sets up address/count and enables transfer.
+	 */
+	SET_SEQINTCODE(CFG4ISTAT_INTR)
+	jmp	cfg4istat_setup_handler;
+cfg4istat_have_sense_addr:
+	bmov	HADDR, SCB_SENSE_BUSADDR, 4;
+	mvi	HCNT[1], (AHD_SENSE_BUFSIZE >> 8);
+	mvi	SG_CACHE_PRE, LAST_SEG;
+	mvi	DFCNTRL, PRELOADEN|SCSIEN|HDMAEN;
+cfg4istat_setup_handler:
+	/*
+	 * Status pkt is transferring to host.
+	 * Wait in idle loop for transfer to complete.
+	 * If a command completed before an attempted
+	 * task management function completed, notify the host.
+	 */
+	test	SCB_TASK_MANAGEMENT, 0xFF jz cfg4istat_no_taskmgmt_func;
+	SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY)
+cfg4istat_no_taskmgmt_func:
+	call	pkt_handle_status;
+	or	SEQINTCTL, IRET ret;
+
+cfg4icmd_intr:
+	/*
+	 * In the case of DMAing a CDB from the host, the normal
+	 * CDB buffer is formatted with an 8 byte address followed
+	 * by a 1 byte count.
+	 */
+	bmov	HADDR[0], SCB_HOST_CDB_PTR, 9;
+	mvi	SG_CACHE_PRE, LAST_SEG;
+	mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+	call	pkt_handle_cdb;
+	or	SEQINTCTL, IRET ret;
+
+/*
+ * See if the target has gone on in this context creating an
+ * overrun condition.  For the write case, the hardware cannot
+ * ack bytes until data are provided.  So, if the target begins
+ * another  packet without changing contexts, implying we are
+ * not sitting on a packet boundary, we are in an overrun
+ * situation.  For the read case, the hardware will continue to
+ * ack bytes into the FIFO, and may even ack the last overrun packet
+ * into the FIFO.   If the FIFO should become non-empty, we are in
+ * a read overrun case.
+ */
+#define check_overrun							\
+	/* Not on a packet boundary. */					\
+	test 	MDFFSTAT, DLZERO jz pkt_handle_overrun;			\
+	test	DFSTATUS, FIFOEMP jz pkt_handle_overrun
+
+pkt_handle_xfer:
+	test	SG_STATE, LOADING_NEEDED jz pkt_last_seg;
+	call	setjmp;
+	test	SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
+	test	SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
+	test	SCSISIGO, ATNO jnz . + 2;
+	test	SSTAT2, NONPACKREQ jz pkt_service_fifo;
+	/*
+	 * Defer handling of this NONPACKREQ until we
+	 * can be sure it pertains to this FIFO.  SAVEPTRS
+	 * will not be asserted if the NONPACKREQ is for us,
+	 * so we must simulate it if shadow is valid.  If
+	 * shadow is not valid, keep running this FIFO until we
+	 * have satisfied the transfer by loading segments and
+	 * waiting for either shadow valid or last_seg_done.
+	 */
+	test	MDFFSTAT, SHVALID jnz pkt_saveptrs;
+pkt_service_fifo:
+	test	SG_STATE, LOADING_NEEDED jnz service_fifo;
+pkt_last_seg:
+	call	setjmp;
+	test	SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
+	test	SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_last_seg_done;
+	test	SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
+	test	SCSISIGO, ATNO jnz . + 2;
+	test	SSTAT2, NONPACKREQ jz return;
+	test	MDFFSTAT, SHVALID jz return;
+	/* FALLTHROUGH */
+
+/*
+ * Either a SAVEPTRS interrupt condition is pending for this FIFO
+ * or we have a pending NONPACKREQ for this FIFO.  We differentiate
+ * between the two by capturing the state of the SAVEPTRS interrupt
+ * prior to clearing this status and executing the common code for
+ * these two cases.
+ */
+pkt_saveptrs:
+BEGIN_CRITICAL;
+	if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+		or	DFCNTRL, FIFOFLUSH;
+	}
+	mov	REG0, SEQINTSRC;
+	call	calc_residual;
+	call	save_pointers;
+	mvi	CLRSEQINTSRC, CLRSAVEPTRS;
+	call	disable_ccsgen;
+	or	SEQIMODE, ENSAVEPTRS;
+	test	DFCNTRL, DIRECTION jnz pkt_saveptrs_check_status;
+	test	DFSTATUS, FIFOEMP jnz pkt_saveptrs_check_status;
+	/*
+	 * Keep a handler around for this FIFO until it drains
+	 * to the host to guarantee that we don't complete the
+	 * command to the host before the data arrives.
+	 */
+pkt_saveptrs_wait_fifoemp:
+	call	setjmp;
+	test	DFSTATUS, FIFOEMP jz return;
+pkt_saveptrs_check_status:
+	or	LONGJMP_ADDR[1], INVALID_ADDR;
+	test	REG0, SAVEPTRS jz unexpected_nonpkt_phase;
+	dec	SCB_FIFO_USE_COUNT;
+	test	SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
+	mvi	DFFSXFRCTL, CLRCHN ret;
+
+/*
+ * LAST_SEG_DONE status has been seen in the current FIFO.
+ * This indicates that all of the allowed data for this
+ * command has transferred across the SCSI and host buses.
+ * Check for overrun and see if we can complete this command.
+ */
+pkt_last_seg_done:
+	/*
+	 * Mark transfer as completed.
+	 */
+	or	SCB_SGPTR, SG_LIST_NULL;
+
+	/*
+	 * Wait for the current context to finish to verify that
+	 * no overrun condition has occurred.
+	 */
+	test	SEQINTSRC, CTXTDONE jnz pkt_ctxt_done;
+	call	setjmp;
+pkt_wait_ctxt_done_loop:
+	test	SEQINTSRC, CTXTDONE jnz pkt_ctxt_done;
+	/*
+	 * A sufficiently large overrun or a NONPACKREQ may
+	 * prevent CTXTDONE from ever asserting, so we must
+	 * poll for these statuses too.
+	 */
+	check_overrun;
+	test	SSTAT2, NONPACKREQ jz return;
+	test	SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
+	/* FALLTHROUGH */
+
+pkt_ctxt_done:
+	check_overrun;
+	or	LONGJMP_ADDR[1], INVALID_ADDR;
+	/*
+	 * If status has been received, it is safe to skip
+	 * the check to see if another FIFO is active because
+	 * LAST_SEG_DONE has been observed.  However, we check
+	 * the FIFO anyway since it costs us only one extra
+	 * instruction to leverage common code to perform the
+	 * SCB completion.
+	 */
+	dec	SCB_FIFO_USE_COUNT;
+	test	SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
+	mvi	DFFSXFRCTL, CLRCHN ret;
+END_CRITICAL;
+
+/*
+ * Must wait until CDB xfer is over before issuing the
+ * clear channel.
+ */
+pkt_handle_cdb:
+	call	setjmp;
+	test	SG_CACHE_SHADOW, LAST_SEG_DONE jz return;
+	or	LONGJMP_ADDR[1], INVALID_ADDR;
+	mvi	DFFSXFRCTL, CLRCHN ret;
+
+/*
+ * Watch over the status transfer.  Our host sense buffer is
+ * large enough to take the maximum allowed status packet.
+ * None-the-less, we must still catch and report overruns to
+ * the host.  Additionally, properly catch unexpected non-packet
+ * phases that are typically caused by CRC errors in status packet
+ * transmission.
+ */
+pkt_handle_status:
+	call	setjmp;
+	test	SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_status_check_overrun;
+	test	SEQINTSRC, CTXTDONE jz pkt_status_check_nonpackreq;
+	test	SG_CACHE_SHADOW, LAST_SEG_DONE jnz pkt_status_check_overrun;
+pkt_status_IU_done:
+	if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+		or	DFCNTRL, FIFOFLUSH;
+	}
+	test	DFSTATUS, FIFOEMP jz return;
+BEGIN_CRITICAL;
+	or	LONGJMP_ADDR[1], INVALID_ADDR;
+	mvi	SCB_SCSI_STATUS, STATUS_PKT_SENSE;
+	or	SCB_CONTROL, STATUS_RCVD;
+	jmp	pkt_complete_scb_if_fifos_idle;
+END_CRITICAL;
+pkt_status_check_overrun:
+	/*
+	 * Status PKT overruns are uncerimoniously recovered with a
+	 * bus reset.  If we've overrun, let the host know so that
+	 * recovery can be performed.
+	 *
+	 * LAST_SEG_DONE has been observed.  If either CTXTDONE or
+	 * a NONPACKREQ phase change have occurred and the FIFO is
+	 * empty, there is no overrun.
+	 */
+	test	DFSTATUS, FIFOEMP jz pkt_status_report_overrun;
+	test	SEQINTSRC, CTXTDONE jz . + 2;
+	test	DFSTATUS, FIFOEMP jnz pkt_status_IU_done;
+	test	SCSIPHASE, ~DATA_PHASE_MASK jz return;
+	test	DFSTATUS, FIFOEMP jnz pkt_status_check_nonpackreq;
+pkt_status_report_overrun:
+	SET_SEQINTCODE(STATUS_OVERRUN)
+	/* SEQUENCER RESTARTED */
+pkt_status_check_nonpackreq:
+	/*
+	 * CTXTDONE may be held off if a NONPACKREQ is associated with
+	 * the current context.  If a NONPACKREQ is observed, decide
+	 * if it is for the current context.  If it is for the current
+	 * context, we must defer NONPACKREQ processing until all data
+	 * has transferred to the host.
+	 */
+	test	SCSIPHASE, ~DATA_PHASE_MASK jz return;
+	test	SCSISIGO, ATNO jnz . + 2;
+	test	SSTAT2, NONPACKREQ jz return;
+	test	SEQINTSRC, CTXTDONE jnz pkt_status_IU_done;
+	test	DFSTATUS, FIFOEMP jz return;
+	/*
+	 * The unexpected nonpkt phase handler assumes that any
+	 * data channel use will have a FIFO reference count.  It
+	 * turns out that the status handler doesn't need a references
+	 * count since the status received flag, and thus completion
+	 * processing, cannot be set until the handler is finished.
+	 * We increment the count here to make the nonpkt handler
+	 * happy.
+	 */
+	inc	SCB_FIFO_USE_COUNT;
+	/* FALLTHROUGH */
+
+/*
+ * Nonpackreq is a polled status.  It can come true in three situations:
+ * we have received an L_Q, we have sent one or more L_Qs, or there is no
+ * L_Q context associated with this REQ (REQ occurs immediately after a
+ * (re)selection).  Routines that know that the context responsible for this
+ * nonpackreq call directly into unexpected_nonpkt_phase.  In the case of the
+ * top level idle loop, we exhaust all active contexts prior to determining that
+ * we simply do not have the full I_T_L_Q for this phase.
+ */
+unexpected_nonpkt_phase_find_ctxt:
+	/*
+	 * This nonpackreq is most likely associated with one of the tags
+	 * in a FIFO or an outgoing LQ.  Only treat it as an I_T only
+	 * nonpackreq if we've cleared out the FIFOs and handled any
+	 * pending SELDO.
+	 */
+SET_SRC_MODE	M_SCSI;
+SET_DST_MODE	M_SCSI;
+	and	A, FIFO1FREE|FIFO0FREE, DFFSTAT;
+	cmp	A, FIFO1FREE|FIFO0FREE jne return;
+	test	SSTAT0, SELDO jnz return;
+	mvi	SCBPTR[1], SCB_LIST_NULL;
+unexpected_nonpkt_phase:
+	test	MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+		jnz unexpected_nonpkt_mode_cleared;
+SET_SRC_MODE	M_DFF0;
+SET_DST_MODE	M_DFF0;
+	or	LONGJMP_ADDR[1], INVALID_ADDR;
+	dec	SCB_FIFO_USE_COUNT;
+	mvi	DFFSXFRCTL, CLRCHN;
+unexpected_nonpkt_mode_cleared:
+	mvi	CLRSINT2, CLRNONPACKREQ;
+	if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+		/*
+		 * Test to ensure that the bus has not
+		 * already gone free prior to clearing
+		 * any stale busfree status.  This avoids
+		 * a window whereby a busfree just after
+		 * a selection could be missed.
+		 */
+		test	SCSISIGI, BSYI jz . + 2;
+		mvi	CLRSINT1,CLRBUSFREE;
+		or	SIMODE1, ENBUSFREE;
+	}
+	test	SCSIPHASE, ~(MSG_IN_PHASE|MSG_OUT_PHASE) jnz illegal_phase;
+	SET_SEQINTCODE(ENTERING_NONPACK)
+	jmp	ITloop;
+
+illegal_phase:
+	SET_SEQINTCODE(ILLEGAL_PHASE)
+	jmp	ITloop;
+
+/*
+ * We have entered an overrun situation.  If we have working
+ * BITBUCKET, flip that on and let the hardware eat any overrun
+ * data.  Otherwise use an overrun buffer in the host to simulate
+ * BITBUCKET.
+ */
+pkt_handle_overrun_inc_use_count:
+	inc	SCB_FIFO_USE_COUNT;
+pkt_handle_overrun:
+	SET_SEQINTCODE(CFG4OVERRUN)
+	call	freeze_queue;
+	if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0) {
+		or	DFFSXFRCTL, DFFBITBUCKET;
+SET_SRC_MODE	M_DFF1;
+SET_DST_MODE	M_DFF1;
+	} else {
+		call	load_overrun_buf;
+		mvi	DFCNTRL, (HDMAEN|SCSIEN|PRELOADEN);
+	}
+	call	setjmp;
+	if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+		test	DFSTATUS, PRELOAD_AVAIL jz overrun_load_done;
+		call	load_overrun_buf;
+		or	DFCNTRL, PRELOADEN;
+overrun_load_done:
+		test	SEQINTSRC, CTXTDONE jnz pkt_overrun_end;
+	} else {
+		test	DFFSXFRCTL, DFFBITBUCKET jz pkt_overrun_end;
+	}
+	test	SSTAT2, NONPACKREQ jz return;
+pkt_overrun_end:
+	or	SCB_RESIDUAL_SGPTR, SG_OVERRUN_RESID;
+	test	SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
+	dec	SCB_FIFO_USE_COUNT;
+	or	LONGJMP_ADDR[1], INVALID_ADDR;
+	test	SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
+	mvi	DFFSXFRCTL, CLRCHN ret;
+
+if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+load_overrun_buf:
+	/*
+	 * Load a dummy segment if preload space is available.
+	 */
+	mov 	HADDR[0], SHARED_DATA_ADDR;
+	add	HADDR[1], PKT_OVERRUN_BUFOFFSET, SHARED_DATA_ADDR[1];
+	mov	ACCUM_SAVE, A;
+	clr	A;
+	adc	HADDR[2], A, SHARED_DATA_ADDR[2];
+	adc	HADDR[3], A, SHARED_DATA_ADDR[3];
+	mov	A, ACCUM_SAVE;
+	bmov	HADDR[4], ALLZEROS, 4;
+	/* PKT_OVERRUN_BUFSIZE is a multiple of 256 */
+	clr	HCNT[0];
+	mvi	HCNT[1], ((PKT_OVERRUN_BUFSIZE >> 8) & 0xFF);
+	clr	HCNT[2] ret;
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
new file mode 100644
index 0000000..109e2c9
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -0,0 +1,10822 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2002 Justin T. Gibbs.
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#250 $
+ */
+
+#ifdef __linux__
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+#else
+#include <dev/aic7xxx/aic79xx_osm.h>
+#include <dev/aic7xxx/aic79xx_inline.h>
+#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
+#endif
+
+
+/***************************** Lookup Tables **********************************/
+static const char *const ahd_chip_names[] =
+{
+	"NONE",
+	"aic7901",
+	"aic7902",
+	"aic7901A"
+};
+static const u_int num_chip_names = ARRAY_SIZE(ahd_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahd_hard_error_entry {
+        uint8_t errno;
+	const char *errmesg;
+};
+
+static const struct ahd_hard_error_entry ahd_hard_errors[] = {
+	{ DSCTMOUT,	"Discard Timer has timed out" },
+	{ ILLOPCODE,	"Illegal Opcode in sequencer program" },
+	{ SQPARERR,	"Sequencer Parity Error" },
+	{ DPARERR,	"Data-path Parity Error" },
+	{ MPARERR,	"Scratch or SCB Memory Parity Error" },
+	{ CIOPARERR,	"CIOBUS Parity Error" },
+};
+static const u_int num_errors = ARRAY_SIZE(ahd_hard_errors);
+
+static const struct ahd_phase_table_entry ahd_phase_table[] =
+{
+	{ P_DATAOUT,	MSG_NOOP,		"in Data-out phase"	},
+	{ P_DATAIN,	MSG_INITIATOR_DET_ERR,	"in Data-in phase"	},
+	{ P_DATAOUT_DT,	MSG_NOOP,		"in DT Data-out phase"	},
+	{ P_DATAIN_DT,	MSG_INITIATOR_DET_ERR,	"in DT Data-in phase"	},
+	{ P_COMMAND,	MSG_NOOP,		"in Command phase"	},
+	{ P_MESGOUT,	MSG_NOOP,		"in Message-out phase"	},
+	{ P_STATUS,	MSG_INITIATOR_DET_ERR,	"in Status phase"	},
+	{ P_MESGIN,	MSG_PARITY_ERROR,	"in Message-in phase"	},
+	{ P_BUSFREE,	MSG_NOOP,		"while idle"		},
+	{ 0,		MSG_NOOP,		"in unknown phase"	}
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = ARRAY_SIZE(ahd_phase_table) - 1;
+
+/* Our Sequencer Program */
+#include "aic79xx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void		ahd_handle_transmission_error(struct ahd_softc *ahd);
+static void		ahd_handle_lqiphase_error(struct ahd_softc *ahd,
+						  u_int lqistat1);
+static int		ahd_handle_pkt_busfree(struct ahd_softc *ahd,
+					       u_int busfreetime);
+static int		ahd_handle_nonpkt_busfree(struct ahd_softc *ahd);
+static void		ahd_handle_proto_violation(struct ahd_softc *ahd);
+static void		ahd_force_renegotiation(struct ahd_softc *ahd,
+						struct ahd_devinfo *devinfo);
+
+static struct ahd_tmode_tstate*
+			ahd_alloc_tstate(struct ahd_softc *ahd,
+					 u_int scsi_id, char channel);
+#ifdef AHD_TARGET_MODE
+static void		ahd_free_tstate(struct ahd_softc *ahd,
+					u_int scsi_id, char channel, int force);
+#endif
+static void		ahd_devlimited_syncrate(struct ahd_softc *ahd,
+					        struct ahd_initiator_tinfo *,
+						u_int *period,
+						u_int *ppr_options,
+						role_t role);
+static void		ahd_update_neg_table(struct ahd_softc *ahd,
+					     struct ahd_devinfo *devinfo,
+					     struct ahd_transinfo *tinfo);
+static void		ahd_update_pending_scbs(struct ahd_softc *ahd);
+static void		ahd_fetch_devinfo(struct ahd_softc *ahd,
+					  struct ahd_devinfo *devinfo);
+static void		ahd_scb_devinfo(struct ahd_softc *ahd,
+					struct ahd_devinfo *devinfo,
+					struct scb *scb);
+static void		ahd_setup_initiator_msgout(struct ahd_softc *ahd,
+						   struct ahd_devinfo *devinfo,
+						   struct scb *scb);
+static void		ahd_build_transfer_msg(struct ahd_softc *ahd,
+					       struct ahd_devinfo *devinfo);
+static void		ahd_construct_sdtr(struct ahd_softc *ahd,
+					   struct ahd_devinfo *devinfo,
+					   u_int period, u_int offset);
+static void		ahd_construct_wdtr(struct ahd_softc *ahd,
+					   struct ahd_devinfo *devinfo,
+					   u_int bus_width);
+static void		ahd_construct_ppr(struct ahd_softc *ahd,
+					  struct ahd_devinfo *devinfo,
+					  u_int period, u_int offset,
+					  u_int bus_width, u_int ppr_options);
+static void		ahd_clear_msg_state(struct ahd_softc *ahd);
+static void		ahd_handle_message_phase(struct ahd_softc *ahd);
+typedef enum {
+	AHDMSG_1B,
+	AHDMSG_2B,
+	AHDMSG_EXT
+} ahd_msgtype;
+static int		ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type,
+				     u_int msgval, int full);
+static int		ahd_parse_msg(struct ahd_softc *ahd,
+				      struct ahd_devinfo *devinfo);
+static int		ahd_handle_msg_reject(struct ahd_softc *ahd,
+					      struct ahd_devinfo *devinfo);
+static void		ahd_handle_ign_wide_residue(struct ahd_softc *ahd,
+						struct ahd_devinfo *devinfo);
+static void		ahd_reinitialize_dataptrs(struct ahd_softc *ahd);
+static void		ahd_handle_devreset(struct ahd_softc *ahd,
+					    struct ahd_devinfo *devinfo,
+					    u_int lun, cam_status status,
+					    char *message, int verbose_level);
+#ifdef AHD_TARGET_MODE
+static void		ahd_setup_target_msgin(struct ahd_softc *ahd,
+					       struct ahd_devinfo *devinfo,
+					       struct scb *scb);
+#endif
+
+static u_int		ahd_sglist_size(struct ahd_softc *ahd);
+static u_int		ahd_sglist_allocsize(struct ahd_softc *ahd);
+static bus_dmamap_callback_t
+			ahd_dmamap_cb; 
+static void		ahd_initialize_hscbs(struct ahd_softc *ahd);
+static int		ahd_init_scbdata(struct ahd_softc *ahd);
+static void		ahd_fini_scbdata(struct ahd_softc *ahd);
+static void		ahd_setup_iocell_workaround(struct ahd_softc *ahd);
+static void		ahd_iocell_first_selection(struct ahd_softc *ahd);
+static void		ahd_add_col_list(struct ahd_softc *ahd,
+					 struct scb *scb, u_int col_idx);
+static void		ahd_rem_col_list(struct ahd_softc *ahd,
+					 struct scb *scb);
+static void		ahd_chip_init(struct ahd_softc *ahd);
+static void		ahd_qinfifo_requeue(struct ahd_softc *ahd,
+					    struct scb *prev_scb,
+					    struct scb *scb);
+static int		ahd_qinfifo_count(struct ahd_softc *ahd);
+static int		ahd_search_scb_list(struct ahd_softc *ahd, int target,
+					    char channel, int lun, u_int tag,
+					    role_t role, uint32_t status,
+					    ahd_search_action action,
+					    u_int *list_head, u_int *list_tail,
+					    u_int tid);
+static void		ahd_stitch_tid_list(struct ahd_softc *ahd,
+					    u_int tid_prev, u_int tid_cur,
+					    u_int tid_next);
+static void		ahd_add_scb_to_free_list(struct ahd_softc *ahd,
+						 u_int scbid);
+static u_int		ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
+				     u_int prev, u_int next, u_int tid);
+static void		ahd_reset_current_bus(struct ahd_softc *ahd);
+static ahd_callback_t	ahd_stat_timer;
+#ifdef AHD_DUMP_SEQ
+static void		ahd_dumpseq(struct ahd_softc *ahd);
+#endif
+static void		ahd_loadseq(struct ahd_softc *ahd);
+static int		ahd_check_patch(struct ahd_softc *ahd,
+					const struct patch **start_patch,
+					u_int start_instr, u_int *skip_addr);
+static u_int		ahd_resolve_seqaddr(struct ahd_softc *ahd,
+					    u_int address);
+static void		ahd_download_instr(struct ahd_softc *ahd,
+					   u_int instrptr, uint8_t *dconsts);
+static int		ahd_probe_stack_size(struct ahd_softc *ahd);
+static int		ahd_scb_active_in_fifo(struct ahd_softc *ahd,
+					       struct scb *scb);
+static void		ahd_run_data_fifo(struct ahd_softc *ahd,
+					  struct scb *scb);
+
+#ifdef AHD_TARGET_MODE
+static void		ahd_queue_lstate_event(struct ahd_softc *ahd,
+					       struct ahd_tmode_lstate *lstate,
+					       u_int initiator_id,
+					       u_int event_type,
+					       u_int event_arg);
+static void		ahd_update_scsiid(struct ahd_softc *ahd,
+					  u_int targid_mask);
+static int		ahd_handle_target_cmd(struct ahd_softc *ahd,
+					      struct target_cmd *cmd);
+#endif
+
+static int		ahd_abort_scbs(struct ahd_softc *ahd, int target,
+				       char channel, int lun, u_int tag,
+				       role_t role, uint32_t status);
+static void		ahd_alloc_scbs(struct ahd_softc *ahd);
+static void		ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl,
+				     u_int scbid);
+static void		ahd_calc_residual(struct ahd_softc *ahd,
+					  struct scb *scb);
+static void		ahd_clear_critical_section(struct ahd_softc *ahd);
+static void		ahd_clear_intstat(struct ahd_softc *ahd);
+static void		ahd_enable_coalescing(struct ahd_softc *ahd,
+					      int enable);
+static u_int		ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl);
+static void		ahd_freeze_devq(struct ahd_softc *ahd,
+					struct scb *scb);
+static void		ahd_handle_scb_status(struct ahd_softc *ahd,
+					      struct scb *scb);
+static const struct ahd_phase_table_entry* ahd_lookup_phase_entry(int phase);
+static void		ahd_shutdown(void *arg);
+static void		ahd_update_coalescing_values(struct ahd_softc *ahd,
+						     u_int timer,
+						     u_int maxcmds,
+						     u_int mincmds);
+static int		ahd_verify_vpd_cksum(struct vpd_config *vpd);
+static int		ahd_wait_seeprom(struct ahd_softc *ahd);
+static int		ahd_match_scb(struct ahd_softc *ahd, struct scb *scb,
+				      int target, char channel, int lun,
+				      u_int tag, role_t role);
+
+static void		ahd_reset_cmds_pending(struct ahd_softc *ahd);
+
+/*************************** Interrupt Services *******************************/
+static void		ahd_run_qoutfifo(struct ahd_softc *ahd);
+#ifdef AHD_TARGET_MODE
+static void		ahd_run_tqinfifo(struct ahd_softc *ahd, int paused);
+#endif
+static void		ahd_handle_hwerrint(struct ahd_softc *ahd);
+static void		ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat);
+static void		ahd_handle_scsiint(struct ahd_softc *ahd,
+				           u_int intstat);
+
+/************************ Sequencer Execution Control *************************/
+void
+ahd_set_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+	if (ahd->src_mode == src && ahd->dst_mode == dst)
+		return;
+#ifdef AHD_DEBUG
+	if (ahd->src_mode == AHD_MODE_UNKNOWN
+	 || ahd->dst_mode == AHD_MODE_UNKNOWN)
+		panic("Setting mode prior to saving it.\n");
+	if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
+		printk("%s: Setting mode 0x%x\n", ahd_name(ahd),
+		       ahd_build_mode_state(ahd, src, dst));
+#endif
+	ahd_outb(ahd, MODE_PTR, ahd_build_mode_state(ahd, src, dst));
+	ahd->src_mode = src;
+	ahd->dst_mode = dst;
+}
+
+static void
+ahd_update_modes(struct ahd_softc *ahd)
+{
+	ahd_mode_state mode_ptr;
+	ahd_mode src;
+	ahd_mode dst;
+
+	mode_ptr = ahd_inb(ahd, MODE_PTR);
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
+		printk("Reading mode 0x%x\n", mode_ptr);
+#endif
+	ahd_extract_mode_state(ahd, mode_ptr, &src, &dst);
+	ahd_known_modes(ahd, src, dst);
+}
+
+static void
+ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode,
+		 ahd_mode dstmode, const char *file, int line)
+{
+#ifdef AHD_DEBUG
+	if ((srcmode & AHD_MK_MSK(ahd->src_mode)) == 0
+	 || (dstmode & AHD_MK_MSK(ahd->dst_mode)) == 0) {
+		panic("%s:%s:%d: Mode assertion failed.\n",
+		       ahd_name(ahd), file, line);
+	}
+#endif
+}
+
+#define AHD_ASSERT_MODES(ahd, source, dest) \
+	ahd_assert_modes(ahd, source, dest, __FILE__, __LINE__);
+
+ahd_mode_state
+ahd_save_modes(struct ahd_softc *ahd)
+{
+	if (ahd->src_mode == AHD_MODE_UNKNOWN
+	 || ahd->dst_mode == AHD_MODE_UNKNOWN)
+		ahd_update_modes(ahd);
+
+	return (ahd_build_mode_state(ahd, ahd->src_mode, ahd->dst_mode));
+}
+
+void
+ahd_restore_modes(struct ahd_softc *ahd, ahd_mode_state state)
+{
+	ahd_mode src;
+	ahd_mode dst;
+
+	ahd_extract_mode_state(ahd, state, &src, &dst);
+	ahd_set_modes(ahd, src, dst);
+}
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+int
+ahd_is_paused(struct ahd_softc *ahd)
+{
+	return ((ahd_inb(ahd, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop.  The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register.  The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+void
+ahd_pause(struct ahd_softc *ahd)
+{
+	ahd_outb(ahd, HCNTRL, ahd->pause);
+
+	/*
+	 * Since the sequencer can disable pausing in a critical section, we
+	 * must loop until it actually stops.
+	 */
+	while (ahd_is_paused(ahd) == 0)
+		;
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted.  If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+void
+ahd_unpause(struct ahd_softc *ahd)
+{
+	/*
+	 * Automatically restore our modes to those saved
+	 * prior to the first change of the mode.
+	 */
+	if (ahd->saved_src_mode != AHD_MODE_UNKNOWN
+	 && ahd->saved_dst_mode != AHD_MODE_UNKNOWN) {
+		if ((ahd->flags & AHD_UPDATE_PEND_CMDS) != 0)
+			ahd_reset_cmds_pending(ahd);
+		ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+	}
+
+	if ((ahd_inb(ahd, INTSTAT) & ~CMDCMPLT) == 0)
+		ahd_outb(ahd, HCNTRL, ahd->unpause);
+
+	ahd_known_modes(ahd, AHD_MODE_UNKNOWN, AHD_MODE_UNKNOWN);
+}
+
+/*********************** Scatter Gather List Handling *************************/
+void *
+ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
+	     void *sgptr, dma_addr_t addr, bus_size_t len, int last)
+{
+	scb->sg_count++;
+	if (sizeof(dma_addr_t) > 4
+	 && (ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+		struct ahd_dma64_seg *sg;
+
+		sg = (struct ahd_dma64_seg *)sgptr;
+		sg->addr = ahd_htole64(addr);
+		sg->len = ahd_htole32(len | (last ? AHD_DMA_LAST_SEG : 0));
+		return (sg + 1);
+	} else {
+		struct ahd_dma_seg *sg;
+
+		sg = (struct ahd_dma_seg *)sgptr;
+		sg->addr = ahd_htole32(addr & 0xFFFFFFFF);
+		sg->len = ahd_htole32(len | ((addr >> 8) & 0x7F000000)
+				    | (last ? AHD_DMA_LAST_SEG : 0));
+		return (sg + 1);
+	}
+}
+
+static void
+ahd_setup_scb_common(struct ahd_softc *ahd, struct scb *scb)
+{
+	/* XXX Handle target mode SCBs. */
+	scb->crc_retry_count = 0;
+	if ((scb->flags & SCB_PACKETIZED) != 0) {
+		/* XXX what about ACA??  It is type 4, but TAG_TYPE == 0x3. */
+		scb->hscb->task_attribute = scb->hscb->control & SCB_TAG_TYPE;
+	} else {
+		if (ahd_get_transfer_length(scb) & 0x01)
+			scb->hscb->task_attribute = SCB_XFERLEN_ODD;
+		else
+			scb->hscb->task_attribute = 0;
+	}
+
+	if (scb->hscb->cdb_len <= MAX_CDB_LEN_WITH_SENSE_ADDR
+	 || (scb->hscb->cdb_len & SCB_CDB_LEN_PTR) != 0)
+		scb->hscb->shared_data.idata.cdb_plus_saddr.sense_addr =
+		    ahd_htole32(scb->sense_busaddr);
+}
+
+static void
+ahd_setup_data_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+	/*
+	 * Copy the first SG into the "current" data ponter area.
+	 */
+	if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+		struct ahd_dma64_seg *sg;
+
+		sg = (struct ahd_dma64_seg *)scb->sg_list;
+		scb->hscb->dataptr = sg->addr;
+		scb->hscb->datacnt = sg->len;
+	} else {
+		struct ahd_dma_seg *sg;
+		uint32_t *dataptr_words;
+
+		sg = (struct ahd_dma_seg *)scb->sg_list;
+		dataptr_words = (uint32_t*)&scb->hscb->dataptr;
+		dataptr_words[0] = sg->addr;
+		dataptr_words[1] = 0;
+		if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+			uint64_t high_addr;
+
+			high_addr = ahd_le32toh(sg->len) & 0x7F000000;
+			scb->hscb->dataptr |= ahd_htole64(high_addr << 8);
+		}
+		scb->hscb->datacnt = sg->len;
+	}
+	/*
+	 * Note where to find the SG entries in bus space.
+	 * We also set the full residual flag which the
+	 * sequencer will clear as soon as a data transfer
+	 * occurs.
+	 */
+	scb->hscb->sgptr = ahd_htole32(scb->sg_list_busaddr|SG_FULL_RESID);
+}
+
+static void
+ahd_setup_noxfer_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+	scb->hscb->sgptr = ahd_htole32(SG_LIST_NULL);
+	scb->hscb->dataptr = 0;
+	scb->hscb->datacnt = 0;
+}
+
+/************************** Memory mapping routines ***************************/
+static void *
+ahd_sg_bus_to_virt(struct ahd_softc *ahd, struct scb *scb, uint32_t sg_busaddr)
+{
+	dma_addr_t sg_offset;
+
+	/* sg_list_phys points to entry 1, not 0 */
+	sg_offset = sg_busaddr - (scb->sg_list_busaddr - ahd_sg_size(ahd));
+	return ((uint8_t *)scb->sg_list + sg_offset);
+}
+
+static uint32_t
+ahd_sg_virt_to_bus(struct ahd_softc *ahd, struct scb *scb, void *sg)
+{
+	dma_addr_t sg_offset;
+
+	/* sg_list_phys points to entry 1, not 0 */
+	sg_offset = ((uint8_t *)sg - (uint8_t *)scb->sg_list)
+		  - ahd_sg_size(ahd);
+
+	return (scb->sg_list_busaddr + sg_offset);
+}
+
+static void
+ahd_sync_scb(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+	ahd_dmamap_sync(ahd, ahd->scb_data.hscb_dmat,
+			scb->hscb_map->dmamap,
+			/*offset*/(uint8_t*)scb->hscb - scb->hscb_map->vaddr,
+			/*len*/sizeof(*scb->hscb), op);
+}
+
+void
+ahd_sync_sglist(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+	if (scb->sg_count == 0)
+		return;
+
+	ahd_dmamap_sync(ahd, ahd->scb_data.sg_dmat,
+			scb->sg_map->dmamap,
+			/*offset*/scb->sg_list_busaddr - ahd_sg_size(ahd),
+			/*len*/ahd_sg_size(ahd) * scb->sg_count, op);
+}
+
+static void
+ahd_sync_sense(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+	ahd_dmamap_sync(ahd, ahd->scb_data.sense_dmat,
+			scb->sense_map->dmamap,
+			/*offset*/scb->sense_busaddr,
+			/*len*/AHD_SENSE_BUFSIZE, op);
+}
+
+#ifdef AHD_TARGET_MODE
+static uint32_t
+ahd_targetcmd_offset(struct ahd_softc *ahd, u_int index)
+{
+	return (((uint8_t *)&ahd->targetcmds[index])
+	       - (uint8_t *)ahd->qoutfifo);
+}
+#endif
+
+/*********************** Miscellaneous Support Functions ***********************/
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+struct ahd_initiator_tinfo *
+ahd_fetch_transinfo(struct ahd_softc *ahd, char channel, u_int our_id,
+		    u_int remote_id, struct ahd_tmode_tstate **tstate)
+{
+	/*
+	 * Transfer data structures are stored from the perspective
+	 * of the target role.  Since the parameters for a connection
+	 * in the initiator role to a given target are the same as
+	 * when the roles are reversed, we pretend we are the target.
+	 */
+	if (channel == 'B')
+		our_id += 8;
+	*tstate = ahd->enabled_targets[our_id];
+	return (&(*tstate)->transinfo[remote_id]);
+}
+
+uint16_t
+ahd_inw(struct ahd_softc *ahd, u_int port)
+{
+	/*
+	 * Read high byte first as some registers increment
+	 * or have other side effects when the low byte is
+	 * read.
+	 */
+	uint16_t r = ahd_inb(ahd, port+1) << 8;
+	return r | ahd_inb(ahd, port);
+}
+
+void
+ahd_outw(struct ahd_softc *ahd, u_int port, u_int value)
+{
+	/*
+	 * Write low byte first to accommodate registers
+	 * such as PRGMCNT where the order maters.
+	 */
+	ahd_outb(ahd, port, value & 0xFF);
+	ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
+}
+
+uint32_t
+ahd_inl(struct ahd_softc *ahd, u_int port)
+{
+	return ((ahd_inb(ahd, port))
+	      | (ahd_inb(ahd, port+1) << 8)
+	      | (ahd_inb(ahd, port+2) << 16)
+	      | (ahd_inb(ahd, port+3) << 24));
+}
+
+void
+ahd_outl(struct ahd_softc *ahd, u_int port, uint32_t value)
+{
+	ahd_outb(ahd, port, (value) & 0xFF);
+	ahd_outb(ahd, port+1, ((value) >> 8) & 0xFF);
+	ahd_outb(ahd, port+2, ((value) >> 16) & 0xFF);
+	ahd_outb(ahd, port+3, ((value) >> 24) & 0xFF);
+}
+
+uint64_t
+ahd_inq(struct ahd_softc *ahd, u_int port)
+{
+	return ((ahd_inb(ahd, port))
+	      | (ahd_inb(ahd, port+1) << 8)
+	      | (ahd_inb(ahd, port+2) << 16)
+	      | (ahd_inb(ahd, port+3) << 24)
+	      | (((uint64_t)ahd_inb(ahd, port+4)) << 32)
+	      | (((uint64_t)ahd_inb(ahd, port+5)) << 40)
+	      | (((uint64_t)ahd_inb(ahd, port+6)) << 48)
+	      | (((uint64_t)ahd_inb(ahd, port+7)) << 56));
+}
+
+void
+ahd_outq(struct ahd_softc *ahd, u_int port, uint64_t value)
+{
+	ahd_outb(ahd, port, value & 0xFF);
+	ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
+	ahd_outb(ahd, port+2, (value >> 16) & 0xFF);
+	ahd_outb(ahd, port+3, (value >> 24) & 0xFF);
+	ahd_outb(ahd, port+4, (value >> 32) & 0xFF);
+	ahd_outb(ahd, port+5, (value >> 40) & 0xFF);
+	ahd_outb(ahd, port+6, (value >> 48) & 0xFF);
+	ahd_outb(ahd, port+7, (value >> 56) & 0xFF);
+}
+
+u_int
+ahd_get_scbptr(struct ahd_softc *ahd)
+{
+	AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+			 ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+	return (ahd_inb(ahd, SCBPTR) | (ahd_inb(ahd, SCBPTR + 1) << 8));
+}
+
+void
+ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr)
+{
+	AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+			 ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+	ahd_outb(ahd, SCBPTR, scbptr & 0xFF);
+	ahd_outb(ahd, SCBPTR+1, (scbptr >> 8) & 0xFF);
+}
+
+#if 0 /* unused */
+static u_int
+ahd_get_hnscb_qoff(struct ahd_softc *ahd)
+{
+	return (ahd_inw_atomic(ahd, HNSCB_QOFF));
+}
+#endif
+
+static void
+ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+	ahd_outw_atomic(ahd, HNSCB_QOFF, value);
+}
+
+#if 0 /* unused */
+static u_int
+ahd_get_hescb_qoff(struct ahd_softc *ahd)
+{
+	return (ahd_inb(ahd, HESCB_QOFF));
+}
+#endif
+
+static void
+ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value)
+{
+	ahd_outb(ahd, HESCB_QOFF, value);
+}
+
+static u_int
+ahd_get_snscb_qoff(struct ahd_softc *ahd)
+{
+	u_int oldvalue;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+	oldvalue = ahd_inw(ahd, SNSCB_QOFF);
+	ahd_outw(ahd, SNSCB_QOFF, oldvalue);
+	return (oldvalue);
+}
+
+static void
+ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+	AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+	ahd_outw(ahd, SNSCB_QOFF, value);
+}
+
+#if 0 /* unused */
+static u_int
+ahd_get_sescb_qoff(struct ahd_softc *ahd)
+{
+	AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+	return (ahd_inb(ahd, SESCB_QOFF));
+}
+#endif
+
+static void
+ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value)
+{
+	AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+	ahd_outb(ahd, SESCB_QOFF, value);
+}
+
+#if 0 /* unused */
+static u_int
+ahd_get_sdscb_qoff(struct ahd_softc *ahd)
+{
+	AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+	return (ahd_inb(ahd, SDSCB_QOFF) | (ahd_inb(ahd, SDSCB_QOFF + 1) << 8));
+}
+#endif
+
+static void
+ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+	AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+	ahd_outb(ahd, SDSCB_QOFF, value & 0xFF);
+	ahd_outb(ahd, SDSCB_QOFF+1, (value >> 8) & 0xFF);
+}
+
+u_int
+ahd_inb_scbram(struct ahd_softc *ahd, u_int offset)
+{
+	u_int value;
+
+	/*
+	 * Workaround PCI-X Rev A. hardware bug.
+	 * After a host read of SCB memory, the chip
+	 * may become confused into thinking prefetch
+	 * was required.  This starts the discard timer
+	 * running and can cause an unexpected discard
+	 * timer interrupt.  The work around is to read
+	 * a normal register prior to the exhaustion of
+	 * the discard timer.  The mode pointer register
+	 * has no side effects and so serves well for
+	 * this purpose.
+	 *
+	 * Razor #528
+	 */
+	value = ahd_inb(ahd, offset);
+	if ((ahd->bugs & AHD_PCIX_SCBRAM_RD_BUG) != 0)
+		ahd_inb(ahd, MODE_PTR);
+	return (value);
+}
+
+u_int
+ahd_inw_scbram(struct ahd_softc *ahd, u_int offset)
+{
+	return (ahd_inb_scbram(ahd, offset)
+	      | (ahd_inb_scbram(ahd, offset+1) << 8));
+}
+
+static uint32_t
+ahd_inl_scbram(struct ahd_softc *ahd, u_int offset)
+{
+	return (ahd_inw_scbram(ahd, offset)
+	      | (ahd_inw_scbram(ahd, offset+2) << 16));
+}
+
+static uint64_t
+ahd_inq_scbram(struct ahd_softc *ahd, u_int offset)
+{
+	return (ahd_inl_scbram(ahd, offset)
+	      | ((uint64_t)ahd_inl_scbram(ahd, offset+4)) << 32);
+}
+
+struct scb *
+ahd_lookup_scb(struct ahd_softc *ahd, u_int tag)
+{
+	struct scb* scb;
+
+	if (tag >= AHD_SCB_MAX)
+		return (NULL);
+	scb = ahd->scb_data.scbindex[tag];
+	if (scb != NULL)
+		ahd_sync_scb(ahd, scb,
+			     BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+	return (scb);
+}
+
+static void
+ahd_swap_with_next_hscb(struct ahd_softc *ahd, struct scb *scb)
+{
+	struct	 hardware_scb *q_hscb;
+	struct	 map_node *q_hscb_map;
+	uint32_t saved_hscb_busaddr;
+
+	/*
+	 * Our queuing method is a bit tricky.  The card
+	 * knows in advance which HSCB (by address) to download,
+	 * and we can't disappoint it.  To achieve this, the next
+	 * HSCB to download is saved off in ahd->next_queued_hscb.
+	 * When we are called to queue "an arbitrary scb",
+	 * we copy the contents of the incoming HSCB to the one
+	 * the sequencer knows about, swap HSCB pointers and
+	 * finally assign the SCB to the tag indexed location
+	 * in the scb_array.  This makes sure that we can still
+	 * locate the correct SCB by SCB_TAG.
+	 */
+	q_hscb = ahd->next_queued_hscb;
+	q_hscb_map = ahd->next_queued_hscb_map;
+	saved_hscb_busaddr = q_hscb->hscb_busaddr;
+	memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+	q_hscb->hscb_busaddr = saved_hscb_busaddr;
+	q_hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
+
+	/* Now swap HSCB pointers. */
+	ahd->next_queued_hscb = scb->hscb;
+	ahd->next_queued_hscb_map = scb->hscb_map;
+	scb->hscb = q_hscb;
+	scb->hscb_map = q_hscb_map;
+
+	/* Now define the mapping from tag to SCB in the scbindex */
+	ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+void
+ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+	ahd_swap_with_next_hscb(ahd, scb);
+
+	if (SCBID_IS_NULL(SCB_GET_TAG(scb)))
+		panic("Attempt to queue invalid SCB tag %x\n",
+		      SCB_GET_TAG(scb));
+
+	/*
+	 * Keep a history of SCBs we've downloaded in the qinfifo.
+	 */
+	ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
+	ahd->qinfifonext++;
+
+	if (scb->sg_count != 0)
+		ahd_setup_data_scb(ahd, scb);
+	else
+		ahd_setup_noxfer_scb(ahd, scb);
+	ahd_setup_scb_common(ahd, scb);
+
+	/*
+	 * Make sure our data is consistent from the
+	 * perspective of the adapter.
+	 */
+	ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_QUEUE) != 0) {
+		uint64_t host_dataptr;
+
+		host_dataptr = ahd_le64toh(scb->hscb->dataptr);
+		printk("%s: Queueing SCB %d:0x%x bus addr 0x%x - 0x%x%x/0x%x\n",
+		       ahd_name(ahd),
+		       SCB_GET_TAG(scb), scb->hscb->scsiid,
+		       ahd_le32toh(scb->hscb->hscb_busaddr),
+		       (u_int)((host_dataptr >> 32) & 0xFFFFFFFF),
+		       (u_int)(host_dataptr & 0xFFFFFFFF),
+		       ahd_le32toh(scb->hscb->datacnt));
+	}
+#endif
+	/* Tell the adapter about the newly queued SCB */
+	ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+}
+
+/************************** Interrupt Processing ******************************/
+static void
+ahd_sync_qoutfifo(struct ahd_softc *ahd, int op)
+{
+	ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap,
+			/*offset*/0,
+			/*len*/AHD_SCB_MAX * sizeof(struct ahd_completion), op);
+}
+
+static void
+ahd_sync_tqinfifo(struct ahd_softc *ahd, int op)
+{
+#ifdef AHD_TARGET_MODE
+	if ((ahd->flags & AHD_TARGETROLE) != 0) {
+		ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+				ahd->shared_data_map.dmamap,
+				ahd_targetcmd_offset(ahd, 0),
+				sizeof(struct target_cmd) * AHD_TMODE_CMDS,
+				op);
+	}
+#endif
+}
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHD_RUN_QOUTFIFO 0x1
+#define AHD_RUN_TQINFIFO 0x2
+static u_int
+ahd_check_cmdcmpltqueues(struct ahd_softc *ahd)
+{
+	u_int retval;
+
+	retval = 0;
+	ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap,
+			/*offset*/ahd->qoutfifonext * sizeof(*ahd->qoutfifo),
+			/*len*/sizeof(*ahd->qoutfifo), BUS_DMASYNC_POSTREAD);
+	if (ahd->qoutfifo[ahd->qoutfifonext].valid_tag
+	  == ahd->qoutfifonext_valid_tag)
+		retval |= AHD_RUN_QOUTFIFO;
+#ifdef AHD_TARGET_MODE
+	if ((ahd->flags & AHD_TARGETROLE) != 0
+	 && (ahd->flags & AHD_TQINFIFO_BLOCKED) == 0) {
+		ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+				ahd->shared_data_map.dmamap,
+				ahd_targetcmd_offset(ahd, ahd->tqinfifofnext),
+				/*len*/sizeof(struct target_cmd),
+				BUS_DMASYNC_POSTREAD);
+		if (ahd->targetcmds[ahd->tqinfifonext].cmd_valid != 0)
+			retval |= AHD_RUN_TQINFIFO;
+	}
+#endif
+	return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+int
+ahd_intr(struct ahd_softc *ahd)
+{
+	u_int	intstat;
+
+	if ((ahd->pause & INTEN) == 0) {
+		/*
+		 * Our interrupt is not enabled on the chip
+		 * and may be disabled for re-entrancy reasons,
+		 * so just return.  This is likely just a shared
+		 * interrupt.
+		 */
+		return (0);
+	}
+
+	/*
+	 * Instead of directly reading the interrupt status register,
+	 * infer the cause of the interrupt by checking our in-core
+	 * completion queues.  This avoids a costly PCI bus read in
+	 * most cases.
+	 */
+	if ((ahd->flags & AHD_ALL_INTERRUPTS) == 0
+	 && (ahd_check_cmdcmpltqueues(ahd) != 0))
+		intstat = CMDCMPLT;
+	else
+		intstat = ahd_inb(ahd, INTSTAT);
+
+	if ((intstat & INT_PEND) == 0)
+		return (0);
+
+	if (intstat & CMDCMPLT) {
+		ahd_outb(ahd, CLRINT, CLRCMDINT);
+
+		/*
+		 * Ensure that the chip sees that we've cleared
+		 * this interrupt before we walk the output fifo.
+		 * Otherwise, we may, due to posted bus writes,
+		 * clear the interrupt after we finish the scan,
+		 * and after the sequencer has added new entries
+		 * and asserted the interrupt again.
+		 */
+		if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+			if (ahd_is_paused(ahd)) {
+				/*
+				 * Potentially lost SEQINT.
+				 * If SEQINTCODE is non-zero,
+				 * simulate the SEQINT.
+				 */
+				if (ahd_inb(ahd, SEQINTCODE) != NO_SEQINT)
+					intstat |= SEQINT;
+			}
+		} else {
+			ahd_flush_device_writes(ahd);
+		}
+		ahd_run_qoutfifo(ahd);
+		ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket]++;
+		ahd->cmdcmplt_total++;
+#ifdef AHD_TARGET_MODE
+		if ((ahd->flags & AHD_TARGETROLE) != 0)
+			ahd_run_tqinfifo(ahd, /*paused*/FALSE);
+#endif
+	}
+
+	/*
+	 * Handle statuses that may invalidate our cached
+	 * copy of INTSTAT separately.
+	 */
+	if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0) {
+		/* Hot eject.  Do nothing */
+	} else if (intstat & HWERRINT) {
+		ahd_handle_hwerrint(ahd);
+	} else if ((intstat & (PCIINT|SPLTINT)) != 0) {
+		ahd->bus_intr(ahd);
+	} else {
+
+		if ((intstat & SEQINT) != 0)
+			ahd_handle_seqint(ahd, intstat);
+
+		if ((intstat & SCSIINT) != 0)
+			ahd_handle_scsiint(ahd, intstat);
+	}
+	return (1);
+}
+
+/******************************** Private Inlines *****************************/
+static inline void
+ahd_assert_atn(struct ahd_softc *ahd)
+{
+	ahd_outb(ahd, SCSISIGO, ATNO);
+}
+
+/*
+ * Determine if the current connection has a packetized
+ * agreement.  This does not necessarily mean that we
+ * are currently in a packetized transfer.  We could
+ * just as easily be sending or receiving a message.
+ */
+static int
+ahd_currently_packetized(struct ahd_softc *ahd)
+{
+	ahd_mode_state	 saved_modes;
+	int		 packetized;
+
+	saved_modes = ahd_save_modes(ahd);
+	if ((ahd->bugs & AHD_PKTIZED_STATUS_BUG) != 0) {
+		/*
+		 * The packetized bit refers to the last
+		 * connection, not the current one.  Check
+		 * for non-zero LQISTATE instead.
+		 */
+		ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+		packetized = ahd_inb(ahd, LQISTATE) != 0;
+	} else {
+		ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+		packetized = ahd_inb(ahd, LQISTAT2) & PACKETIZED;
+	}
+	ahd_restore_modes(ahd, saved_modes);
+	return (packetized);
+}
+
+static inline int
+ahd_set_active_fifo(struct ahd_softc *ahd)
+{
+	u_int active_fifo;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	active_fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;
+	switch (active_fifo) {
+	case 0:
+	case 1:
+		ahd_set_modes(ahd, active_fifo, active_fifo);
+		return (1);
+	default:
+		return (0);
+	}
+}
+
+static inline void
+ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl)
+{
+	ahd_busy_tcl(ahd, tcl, SCB_LIST_NULL);
+}
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static inline void
+ahd_update_residual(struct ahd_softc *ahd, struct scb *scb)
+{
+	uint32_t sgptr;
+
+	sgptr = ahd_le32toh(scb->hscb->sgptr);
+	if ((sgptr & SG_STATUS_VALID) != 0)
+		ahd_calc_residual(ahd, scb);
+}
+
+static inline void
+ahd_complete_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+	uint32_t sgptr;
+
+	sgptr = ahd_le32toh(scb->hscb->sgptr);
+	if ((sgptr & SG_STATUS_VALID) != 0)
+		ahd_handle_scb_status(ahd, scb);
+	else
+		ahd_done(ahd, scb);
+}
+
+
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+static void
+ahd_restart(struct ahd_softc *ahd)
+{
+
+	ahd_pause(ahd);
+
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+	/* No more pending messages */
+	ahd_clear_msg_state(ahd);
+	ahd_outb(ahd, SCSISIGO, 0);		/* De-assert BSY */
+	ahd_outb(ahd, MSG_OUT, MSG_NOOP);	/* No message to send */
+	ahd_outb(ahd, SXFRCTL1, ahd_inb(ahd, SXFRCTL1) & ~BITBUCKET);
+	ahd_outb(ahd, SEQINTCTL, 0);
+	ahd_outb(ahd, LASTPHASE, P_BUSFREE);
+	ahd_outb(ahd, SEQ_FLAGS, 0);
+	ahd_outb(ahd, SAVED_SCSIID, 0xFF);
+	ahd_outb(ahd, SAVED_LUN, 0xFF);
+
+	/*
+	 * Ensure that the sequencer's idea of TQINPOS
+	 * matches our own.  The sequencer increments TQINPOS
+	 * only after it sees a DMA complete and a reset could
+	 * occur before the increment leaving the kernel to believe
+	 * the command arrived but the sequencer to not.
+	 */
+	ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);
+
+	/* Always allow reselection */
+	ahd_outb(ahd, SCSISEQ1,
+		 ahd_inb(ahd, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+	ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+	/*
+	 * Clear any pending sequencer interrupt.  It is no
+	 * longer relevant since we're resetting the Program
+	 * Counter.
+	 */
+	ahd_outb(ahd, CLRINT, CLRSEQINT);
+
+	ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
+	ahd_unpause(ahd);
+}
+
+static void
+ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo)
+{
+	ahd_mode_state	 saved_modes;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_FIFOS) != 0)
+		printk("%s: Clearing FIFO %d\n", ahd_name(ahd), fifo);
+#endif
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, fifo, fifo);
+	ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
+	if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
+		ahd_outb(ahd, CCSGCTL, CCSGRESET);
+	ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+	ahd_outb(ahd, SG_STATE, 0);
+	ahd_restore_modes(ahd, saved_modes);
+}
+
+/************************* Input/Output Queues ********************************/
+/*
+ * Flush and completed commands that are sitting in the command
+ * complete queues down on the chip but have yet to be dma'ed back up.
+ */
+static void
+ahd_flush_qoutfifo(struct ahd_softc *ahd)
+{
+	struct		scb *scb;
+	ahd_mode_state	saved_modes;
+	u_int		saved_scbptr;
+	u_int		ccscbctl;
+	u_int		scbid;
+	u_int		next_scbid;
+
+	saved_modes = ahd_save_modes(ahd);
+
+	/*
+	 * Flush the good status FIFO for completed packetized commands.
+	 */
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	saved_scbptr = ahd_get_scbptr(ahd);
+	while ((ahd_inb(ahd, LQISTAT2) & LQIGSAVAIL) != 0) {
+		u_int fifo_mode;
+		u_int i;
+		
+		scbid = ahd_inw(ahd, GSFIFO);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			printk("%s: Warning - GSFIFO SCB %d invalid\n",
+			       ahd_name(ahd), scbid);
+			continue;
+		}
+		/*
+		 * Determine if this transaction is still active in
+		 * any FIFO.  If it is, we must flush that FIFO to
+		 * the host before completing the  command.
+		 */
+		fifo_mode = 0;
+rescan_fifos:
+		for (i = 0; i < 2; i++) {
+			/* Toggle to the other mode. */
+			fifo_mode ^= 1;
+			ahd_set_modes(ahd, fifo_mode, fifo_mode);
+
+			if (ahd_scb_active_in_fifo(ahd, scb) == 0)
+				continue;
+
+			ahd_run_data_fifo(ahd, scb);
+
+			/*
+			 * Running this FIFO may cause a CFG4DATA for
+			 * this same transaction to assert in the other
+			 * FIFO or a new snapshot SAVEPTRS interrupt
+			 * in this FIFO.  Even running a FIFO may not
+			 * clear the transaction if we are still waiting
+			 * for data to drain to the host. We must loop
+			 * until the transaction is not active in either
+			 * FIFO just to be sure.  Reset our loop counter
+			 * so we will visit both FIFOs again before
+			 * declaring this transaction finished.  We
+			 * also delay a bit so that status has a chance
+			 * to change before we look at this FIFO again.
+			 */
+			ahd_delay(200);
+			goto rescan_fifos;
+		}
+		ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+		ahd_set_scbptr(ahd, scbid);
+		if ((ahd_inb_scbram(ahd, SCB_SGPTR) & SG_LIST_NULL) == 0
+		 && ((ahd_inb_scbram(ahd, SCB_SGPTR) & SG_FULL_RESID) != 0
+		  || (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR)
+		      & SG_LIST_NULL) != 0)) {
+			u_int comp_head;
+
+			/*
+			 * The transfer completed with a residual.
+			 * Place this SCB on the complete DMA list
+			 * so that we update our in-core copy of the
+			 * SCB before completing the command.
+			 */
+			ahd_outb(ahd, SCB_SCSI_STATUS, 0);
+			ahd_outb(ahd, SCB_SGPTR,
+				 ahd_inb_scbram(ahd, SCB_SGPTR)
+				 | SG_STATUS_VALID);
+			ahd_outw(ahd, SCB_TAG, scbid);
+			ahd_outw(ahd, SCB_NEXT_COMPLETE, SCB_LIST_NULL);
+			comp_head = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+			if (SCBID_IS_NULL(comp_head)) {
+				ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, scbid);
+				ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, scbid);
+			} else {
+				u_int tail;
+
+				tail = ahd_inw(ahd, COMPLETE_DMA_SCB_TAIL);
+				ahd_set_scbptr(ahd, tail);
+				ahd_outw(ahd, SCB_NEXT_COMPLETE, scbid);
+				ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, scbid);
+				ahd_set_scbptr(ahd, scbid);
+			}
+		} else
+			ahd_complete_scb(ahd, scb);
+	}
+	ahd_set_scbptr(ahd, saved_scbptr);
+
+	/*
+	 * Setup for command channel portion of flush.
+	 */
+	ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+	/*
+	 * Wait for any inprogress DMA to complete and clear DMA state
+	 * if this is for an SCB in the qinfifo.
+	 */
+	while (((ccscbctl = ahd_inb(ahd, CCSCBCTL)) & (CCARREN|CCSCBEN)) != 0) {
+
+		if ((ccscbctl & (CCSCBDIR|CCARREN)) == (CCSCBDIR|CCARREN)) {
+			if ((ccscbctl & ARRDONE) != 0)
+				break;
+		} else if ((ccscbctl & CCSCBDONE) != 0)
+			break;
+		ahd_delay(200);
+	}
+	/*
+	 * We leave the sequencer to cleanup in the case of DMA's to
+	 * update the qoutfifo.  In all other cases (DMA's to the
+	 * chip or a push of an SCB from the COMPLETE_DMA_SCB list),
+	 * we disable the DMA engine so that the sequencer will not
+	 * attempt to handle the DMA completion.
+	 */
+	if ((ccscbctl & CCSCBDIR) != 0 || (ccscbctl & ARRDONE) != 0)
+		ahd_outb(ahd, CCSCBCTL, ccscbctl & ~(CCARREN|CCSCBEN));
+
+	/*
+	 * Complete any SCBs that just finished
+	 * being DMA'ed into the qoutfifo.
+	 */
+	ahd_run_qoutfifo(ahd);
+
+	saved_scbptr = ahd_get_scbptr(ahd);
+	/*
+	 * Manually update/complete any completed SCBs that are waiting to be
+	 * DMA'ed back up to the host.
+	 */
+	scbid = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+	while (!SCBID_IS_NULL(scbid)) {
+		uint8_t *hscb_ptr;
+		u_int	 i;
+		
+		ahd_set_scbptr(ahd, scbid);
+		next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			printk("%s: Warning - DMA-up and complete "
+			       "SCB %d invalid\n", ahd_name(ahd), scbid);
+			continue;
+		}
+		hscb_ptr = (uint8_t *)scb->hscb;
+		for (i = 0; i < sizeof(struct hardware_scb); i++)
+			*hscb_ptr++ = ahd_inb_scbram(ahd, SCB_BASE + i);
+
+		ahd_complete_scb(ahd, scb);
+		scbid = next_scbid;
+	}
+	ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
+	ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, SCB_LIST_NULL);
+
+	scbid = ahd_inw(ahd, COMPLETE_ON_QFREEZE_HEAD);
+	while (!SCBID_IS_NULL(scbid)) {
+
+		ahd_set_scbptr(ahd, scbid);
+		next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			printk("%s: Warning - Complete Qfrz SCB %d invalid\n",
+			       ahd_name(ahd), scbid);
+			continue;
+		}
+
+		ahd_complete_scb(ahd, scb);
+		scbid = next_scbid;
+	}
+	ahd_outw(ahd, COMPLETE_ON_QFREEZE_HEAD, SCB_LIST_NULL);
+
+	scbid = ahd_inw(ahd, COMPLETE_SCB_HEAD);
+	while (!SCBID_IS_NULL(scbid)) {
+
+		ahd_set_scbptr(ahd, scbid);
+		next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			printk("%s: Warning - Complete SCB %d invalid\n",
+			       ahd_name(ahd), scbid);
+			continue;
+		}
+
+		ahd_complete_scb(ahd, scb);
+		scbid = next_scbid;
+	}
+	ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);
+
+	/*
+	 * Restore state.
+	 */
+	ahd_set_scbptr(ahd, saved_scbptr);
+	ahd_restore_modes(ahd, saved_modes);
+	ahd->flags |= AHD_UPDATE_PEND_CMDS;
+}
+
+/*
+ * Determine if an SCB for a packetized transaction
+ * is active in a FIFO.
+ */
+static int
+ahd_scb_active_in_fifo(struct ahd_softc *ahd, struct scb *scb)
+{
+
+	/*
+	 * The FIFO is only active for our transaction if
+	 * the SCBPTR matches the SCB's ID and the firmware
+	 * has installed a handler for the FIFO or we have
+	 * a pending SAVEPTRS or CFG4DATA interrupt.
+	 */
+	if (ahd_get_scbptr(ahd) != SCB_GET_TAG(scb)
+	 || ((ahd_inb(ahd, LONGJMP_ADDR+1) & INVALID_ADDR) != 0
+	  && (ahd_inb(ahd, SEQINTSRC) & (CFG4DATA|SAVEPTRS)) == 0))
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Run a data fifo to completion for a transaction we know
+ * has completed across the SCSI bus (good status has been
+ * received).  We are already set to the correct FIFO mode
+ * on entry to this routine.
+ *
+ * This function attempts to operate exactly as the firmware
+ * would when running this FIFO.  Care must be taken to update
+ * this routine any time the firmware's FIFO algorithm is
+ * changed.
+ */
+static void
+ahd_run_data_fifo(struct ahd_softc *ahd, struct scb *scb)
+{
+	u_int seqintsrc;
+
+	seqintsrc = ahd_inb(ahd, SEQINTSRC);
+	if ((seqintsrc & CFG4DATA) != 0) {
+		uint32_t datacnt;
+		uint32_t sgptr;
+
+		/*
+		 * Clear full residual flag.
+		 */
+		sgptr = ahd_inl_scbram(ahd, SCB_SGPTR) & ~SG_FULL_RESID;
+		ahd_outb(ahd, SCB_SGPTR, sgptr);
+
+		/*
+		 * Load datacnt and address.
+		 */
+		datacnt = ahd_inl_scbram(ahd, SCB_DATACNT);
+		if ((datacnt & AHD_DMA_LAST_SEG) != 0) {
+			sgptr |= LAST_SEG;
+			ahd_outb(ahd, SG_STATE, 0);
+		} else
+			ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
+		ahd_outq(ahd, HADDR, ahd_inq_scbram(ahd, SCB_DATAPTR));
+		ahd_outl(ahd, HCNT, datacnt & AHD_SG_LEN_MASK);
+		ahd_outb(ahd, SG_CACHE_PRE, sgptr);
+		ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
+
+		/*
+		 * Initialize Residual Fields.
+		 */
+		ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, datacnt >> 24);
+		ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr & SG_PTR_MASK);
+
+		/*
+		 * Mark the SCB as having a FIFO in use.
+		 */
+		ahd_outb(ahd, SCB_FIFO_USE_COUNT,
+			 ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) + 1);
+
+		/*
+		 * Install a "fake" handler for this FIFO.
+		 */
+		ahd_outw(ahd, LONGJMP_ADDR, 0);
+
+		/*
+		 * Notify the hardware that we have satisfied
+		 * this sequencer interrupt.
+		 */
+		ahd_outb(ahd, CLRSEQINTSRC, CLRCFG4DATA);
+	} else if ((seqintsrc & SAVEPTRS) != 0) {
+		uint32_t sgptr;
+		uint32_t resid;
+
+		if ((ahd_inb(ahd, LONGJMP_ADDR+1)&INVALID_ADDR) != 0) {
+			/*
+			 * Snapshot Save Pointers.  All that
+			 * is necessary to clear the snapshot
+			 * is a CLRCHN.
+			 */
+			goto clrchn;
+		}
+
+		/*
+		 * Disable S/G fetch so the DMA engine
+		 * is available to future users.
+		 */
+		if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
+			ahd_outb(ahd, CCSGCTL, 0);
+		ahd_outb(ahd, SG_STATE, 0);
+
+		/*
+		 * Flush the data FIFO.  Strickly only
+		 * necessary for Rev A parts.
+		 */
+		ahd_outb(ahd, DFCNTRL, ahd_inb(ahd, DFCNTRL) | FIFOFLUSH);
+
+		/*
+		 * Calculate residual.
+		 */
+		sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
+		resid = ahd_inl(ahd, SHCNT);
+		resid |= ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+3) << 24;
+		ahd_outl(ahd, SCB_RESIDUAL_DATACNT, resid);
+		if ((ahd_inb(ahd, SG_CACHE_SHADOW) & LAST_SEG) == 0) {
+			/*
+			 * Must back up to the correct S/G element.
+			 * Typically this just means resetting our
+			 * low byte to the offset in the SG_CACHE,
+			 * but if we wrapped, we have to correct
+			 * the other bytes of the sgptr too.
+			 */
+			if ((ahd_inb(ahd, SG_CACHE_SHADOW) & 0x80) != 0
+			 && (sgptr & 0x80) == 0)
+				sgptr -= 0x100;
+			sgptr &= ~0xFF;
+			sgptr |= ahd_inb(ahd, SG_CACHE_SHADOW)
+			       & SG_ADDR_MASK;
+			ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+			ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 3, 0);
+		} else if ((resid & AHD_SG_LEN_MASK) == 0) {
+			ahd_outb(ahd, SCB_RESIDUAL_SGPTR,
+				 sgptr | SG_LIST_NULL);
+		}
+		/*
+		 * Save Pointers.
+		 */
+		ahd_outq(ahd, SCB_DATAPTR, ahd_inq(ahd, SHADDR));
+		ahd_outl(ahd, SCB_DATACNT, resid);
+		ahd_outl(ahd, SCB_SGPTR, sgptr);
+		ahd_outb(ahd, CLRSEQINTSRC, CLRSAVEPTRS);
+		ahd_outb(ahd, SEQIMODE,
+			 ahd_inb(ahd, SEQIMODE) | ENSAVEPTRS);
+		/*
+		 * If the data is to the SCSI bus, we are
+		 * done, otherwise wait for FIFOEMP.
+		 */
+		if ((ahd_inb(ahd, DFCNTRL) & DIRECTION) != 0)
+			goto clrchn;
+	} else if ((ahd_inb(ahd, SG_STATE) & LOADING_NEEDED) != 0) {
+		uint32_t sgptr;
+		uint64_t data_addr;
+		uint32_t data_len;
+		u_int	 dfcntrl;
+
+		/*
+		 * Disable S/G fetch so the DMA engine
+		 * is available to future users.  We won't
+		 * be using the DMA engine to load segments.
+		 */
+		if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0) {
+			ahd_outb(ahd, CCSGCTL, 0);
+			ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
+		}
+
+		/*
+		 * Wait for the DMA engine to notice that the
+		 * host transfer is enabled and that there is
+		 * space in the S/G FIFO for new segments before
+		 * loading more segments.
+		 */
+		if ((ahd_inb(ahd, DFSTATUS) & PRELOAD_AVAIL) != 0
+		 && (ahd_inb(ahd, DFCNTRL) & HDMAENACK) != 0) {
+
+			/*
+			 * Determine the offset of the next S/G
+			 * element to load.
+			 */
+			sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
+			sgptr &= SG_PTR_MASK;
+			if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+				struct ahd_dma64_seg *sg;
+
+				sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+				data_addr = sg->addr;
+				data_len = sg->len;
+				sgptr += sizeof(*sg);
+			} else {
+				struct	ahd_dma_seg *sg;
+
+				sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+				data_addr = sg->len & AHD_SG_HIGH_ADDR_MASK;
+				data_addr <<= 8;
+				data_addr |= sg->addr;
+				data_len = sg->len;
+				sgptr += sizeof(*sg);
+			}
+
+			/*
+			 * Update residual information.
+			 */
+			ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, data_len >> 24);
+			ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+
+			/*
+			 * Load the S/G.
+			 */
+			if (data_len & AHD_DMA_LAST_SEG) {
+				sgptr |= LAST_SEG;
+				ahd_outb(ahd, SG_STATE, 0);
+			}
+			ahd_outq(ahd, HADDR, data_addr);
+			ahd_outl(ahd, HCNT, data_len & AHD_SG_LEN_MASK);
+			ahd_outb(ahd, SG_CACHE_PRE, sgptr & 0xFF);
+
+			/*
+			 * Advertise the segment to the hardware.
+			 */
+			dfcntrl = ahd_inb(ahd, DFCNTRL)|PRELOADEN|HDMAEN;
+			if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0) {
+				/*
+				 * Use SCSIENWRDIS so that SCSIEN
+				 * is never modified by this
+				 * operation.
+				 */
+				dfcntrl |= SCSIENWRDIS;
+			}
+			ahd_outb(ahd, DFCNTRL, dfcntrl);
+		}
+	} else if ((ahd_inb(ahd, SG_CACHE_SHADOW) & LAST_SEG_DONE) != 0) {
+
+		/*
+		 * Transfer completed to the end of SG list
+		 * and has flushed to the host.
+		 */
+		ahd_outb(ahd, SCB_SGPTR,
+			 ahd_inb_scbram(ahd, SCB_SGPTR) | SG_LIST_NULL);
+		goto clrchn;
+	} else if ((ahd_inb(ahd, DFSTATUS) & FIFOEMP) != 0) {
+clrchn:
+		/*
+		 * Clear any handler for this FIFO, decrement
+		 * the FIFO use count for the SCB, and release
+		 * the FIFO.
+		 */
+		ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+		ahd_outb(ahd, SCB_FIFO_USE_COUNT,
+			 ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) - 1);
+		ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
+	}
+}
+
+/*
+ * Look for entries in the QoutFIFO that have completed.
+ * The valid_tag completion field indicates the validity
+ * of the entry - the valid value toggles each time through
+ * the queue. We use the sg_status field in the completion
+ * entry to avoid referencing the hscb if the completion
+ * occurred with no errors and no residual.  sg_status is
+ * a copy of the first byte (little endian) of the sgptr
+ * hscb field.
+ */
+static void
+ahd_run_qoutfifo(struct ahd_softc *ahd)
+{
+	struct ahd_completion *completion;
+	struct scb *scb;
+	u_int  scb_index;
+
+	if ((ahd->flags & AHD_RUNNING_QOUTFIFO) != 0)
+		panic("ahd_run_qoutfifo recursion");
+	ahd->flags |= AHD_RUNNING_QOUTFIFO;
+	ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD);
+	for (;;) {
+		completion = &ahd->qoutfifo[ahd->qoutfifonext];
+
+		if (completion->valid_tag != ahd->qoutfifonext_valid_tag)
+			break;
+
+		scb_index = ahd_le16toh(completion->tag);
+		scb = ahd_lookup_scb(ahd, scb_index);
+		if (scb == NULL) {
+			printk("%s: WARNING no command for scb %d "
+			       "(cmdcmplt)\nQOUTPOS = %d\n",
+			       ahd_name(ahd), scb_index,
+			       ahd->qoutfifonext);
+			ahd_dump_card_state(ahd);
+		} else if ((completion->sg_status & SG_STATUS_VALID) != 0) {
+			ahd_handle_scb_status(ahd, scb);
+		} else {
+			ahd_done(ahd, scb);
+		}
+
+		ahd->qoutfifonext = (ahd->qoutfifonext+1) & (AHD_QOUT_SIZE-1);
+		if (ahd->qoutfifonext == 0)
+			ahd->qoutfifonext_valid_tag ^= QOUTFIFO_ENTRY_VALID;
+	}
+	ahd->flags &= ~AHD_RUNNING_QOUTFIFO;
+}
+
+/************************* Interrupt Handling *********************************/
+static void
+ahd_handle_hwerrint(struct ahd_softc *ahd)
+{
+	/*
+	 * Some catastrophic hardware error has occurred.
+	 * Print it for the user and disable the controller.
+	 */
+	int i;
+	int error;
+
+	error = ahd_inb(ahd, ERROR);
+	for (i = 0; i < num_errors; i++) {
+		if ((error & ahd_hard_errors[i].errno) != 0)
+			printk("%s: hwerrint, %s\n",
+			       ahd_name(ahd), ahd_hard_errors[i].errmesg);
+	}
+
+	ahd_dump_card_state(ahd);
+	panic("BRKADRINT");
+
+	/* Tell everyone that this HBA is no longer available */
+	ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+		       CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+		       CAM_NO_HBA);
+
+	/* Tell the system that this controller has gone away. */
+	ahd_free(ahd);
+}
+
+#ifdef AHD_DEBUG
+static void
+ahd_dump_sglist(struct scb *scb)
+{
+	int i;
+
+	if (scb->sg_count > 0) {
+		if ((scb->ahd_softc->flags & AHD_64BIT_ADDRESSING) != 0) {
+			struct ahd_dma64_seg *sg_list;
+
+			sg_list = (struct ahd_dma64_seg*)scb->sg_list;
+			for (i = 0; i < scb->sg_count; i++) {
+				uint64_t addr;
+				uint32_t len;
+
+				addr = ahd_le64toh(sg_list[i].addr);
+				len = ahd_le32toh(sg_list[i].len);
+				printk("sg[%d] - Addr 0x%x%x : Length %d%s\n",
+				       i,
+				       (uint32_t)((addr >> 32) & 0xFFFFFFFF),
+				       (uint32_t)(addr & 0xFFFFFFFF),
+				       sg_list[i].len & AHD_SG_LEN_MASK,
+				       (sg_list[i].len & AHD_DMA_LAST_SEG)
+				     ? " Last" : "");
+			}
+		} else {
+			struct ahd_dma_seg *sg_list;
+
+			sg_list = (struct ahd_dma_seg*)scb->sg_list;
+			for (i = 0; i < scb->sg_count; i++) {
+				uint32_t len;
+
+				len = ahd_le32toh(sg_list[i].len);
+				printk("sg[%d] - Addr 0x%x%x : Length %d%s\n",
+				       i,
+				       (len & AHD_SG_HIGH_ADDR_MASK) >> 24,
+				       ahd_le32toh(sg_list[i].addr),
+				       len & AHD_SG_LEN_MASK,
+				       len & AHD_DMA_LAST_SEG ? " Last" : "");
+			}
+		}
+	}
+}
+#endif  /*  AHD_DEBUG  */
+
+static void
+ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
+{
+	u_int seqintcode;
+
+	/*
+	 * Save the sequencer interrupt code and clear the SEQINT
+	 * bit. We will unpause the sequencer, if appropriate,
+	 * after servicing the request.
+	 */
+	seqintcode = ahd_inb(ahd, SEQINTCODE);
+	ahd_outb(ahd, CLRINT, CLRSEQINT);
+	if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
+		/*
+		 * Unpause the sequencer and let it clear
+		 * SEQINT by writing NO_SEQINT to it.  This
+		 * will cause the sequencer to be paused again,
+		 * which is the expected state of this routine.
+		 */
+		ahd_unpause(ahd);
+		while (!ahd_is_paused(ahd))
+			;
+		ahd_outb(ahd, CLRINT, CLRSEQINT);
+	}
+	ahd_update_modes(ahd);
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_MISC) != 0)
+		printk("%s: Handle Seqint Called for code %d\n",
+		       ahd_name(ahd), seqintcode);
+#endif
+	switch (seqintcode) {
+	case ENTERING_NONPACK:
+	{
+		struct	scb *scb;
+		u_int	scbid;
+
+		AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+				 ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+		scbid = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			/*
+			 * Somehow need to know if this
+			 * is from a selection or reselection.
+			 * From that, we can determine target
+			 * ID so we at least have an I_T nexus.
+			 */
+		} else {
+			ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
+			ahd_outb(ahd, SAVED_LUN, scb->hscb->lun);
+			ahd_outb(ahd, SEQ_FLAGS, 0x0);
+		}
+		if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0
+		 && (ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
+			/*
+			 * Phase change after read stream with
+			 * CRC error with P0 asserted on last
+			 * packet.
+			 */
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+				printk("%s: Assuming LQIPHASE_NLQ with "
+				       "P0 assertion\n", ahd_name(ahd));
+#endif
+		}
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+			printk("%s: Entering NONPACK\n", ahd_name(ahd));
+#endif
+		break;
+	}
+	case INVALID_SEQINT:
+		printk("%s: Invalid Sequencer interrupt occurred, "
+		       "resetting channel.\n",
+		       ahd_name(ahd));
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+			ahd_dump_card_state(ahd);
+#endif
+		ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+		break;
+	case STATUS_OVERRUN:
+	{
+		struct	scb *scb;
+		u_int	scbid;
+
+		scbid = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb != NULL)
+			ahd_print_path(ahd, scb);
+		else
+			printk("%s: ", ahd_name(ahd));
+		printk("SCB %d Packetized Status Overrun", scbid);
+		ahd_dump_card_state(ahd);
+		ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+		break;
+	}
+	case CFG4ISTAT_INTR:
+	{
+		struct	scb *scb;
+		u_int	scbid;
+
+		scbid = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			ahd_dump_card_state(ahd);
+			printk("CFG4ISTAT: Free SCB %d referenced", scbid);
+			panic("For safety");
+		}
+		ahd_outq(ahd, HADDR, scb->sense_busaddr);
+		ahd_outw(ahd, HCNT, AHD_SENSE_BUFSIZE);
+		ahd_outb(ahd, HCNT + 2, 0);
+		ahd_outb(ahd, SG_CACHE_PRE, SG_LAST_SEG);
+		ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
+		break;
+	}
+	case ILLEGAL_PHASE:
+	{
+		u_int bus_phase;
+
+		bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+		printk("%s: ILLEGAL_PHASE 0x%x\n",
+		       ahd_name(ahd), bus_phase);
+
+		switch (bus_phase) {
+		case P_DATAOUT:
+		case P_DATAIN:
+		case P_DATAOUT_DT:
+		case P_DATAIN_DT:
+		case P_MESGOUT:
+		case P_STATUS:
+		case P_MESGIN:
+			ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+			printk("%s: Issued Bus Reset.\n", ahd_name(ahd));
+			break;
+		case P_COMMAND:
+		{
+			struct	ahd_devinfo devinfo;
+			struct	scb *scb;
+			struct	ahd_initiator_tinfo *targ_info;
+			struct	ahd_tmode_tstate *tstate;
+			struct	ahd_transinfo *tinfo;
+			u_int	scbid;
+
+			/*
+			 * If a target takes us into the command phase
+			 * assume that it has been externally reset and
+			 * has thus lost our previous packetized negotiation
+			 * agreement.  Since we have not sent an identify
+			 * message and may not have fully qualified the
+			 * connection, we change our command to TUR, assert
+			 * ATN and ABORT the task when we go to message in
+			 * phase.  The OSM will see the REQUEUE_REQUEST
+			 * status and retry the command.
+			 */
+			scbid = ahd_get_scbptr(ahd);
+			scb = ahd_lookup_scb(ahd, scbid);
+			if (scb == NULL) {
+				printk("Invalid phase with no valid SCB.  "
+				       "Resetting bus.\n");
+				ahd_reset_channel(ahd, 'A',
+						  /*Initiate Reset*/TRUE);
+				break;
+			}
+			ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
+					    SCB_GET_TARGET(ahd, scb),
+					    SCB_GET_LUN(scb),
+					    SCB_GET_CHANNEL(ahd, scb),
+					    ROLE_INITIATOR);
+			targ_info = ahd_fetch_transinfo(ahd,
+							devinfo.channel,
+							devinfo.our_scsiid,
+							devinfo.target,
+							&tstate);
+			tinfo = &targ_info->curr;
+			ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+				      AHD_TRANS_ACTIVE, /*paused*/TRUE);
+			ahd_set_syncrate(ahd, &devinfo, /*period*/0,
+					 /*offset*/0, /*ppr_options*/0,
+					 AHD_TRANS_ACTIVE, /*paused*/TRUE);
+			/* Hand-craft TUR command */
+			ahd_outb(ahd, SCB_CDB_STORE, 0);
+			ahd_outb(ahd, SCB_CDB_STORE+1, 0);
+			ahd_outb(ahd, SCB_CDB_STORE+2, 0);
+			ahd_outb(ahd, SCB_CDB_STORE+3, 0);
+			ahd_outb(ahd, SCB_CDB_STORE+4, 0);
+			ahd_outb(ahd, SCB_CDB_STORE+5, 0);
+			ahd_outb(ahd, SCB_CDB_LEN, 6);
+			scb->hscb->control &= ~(TAG_ENB|SCB_TAG_TYPE);
+			scb->hscb->control |= MK_MESSAGE;
+			ahd_outb(ahd, SCB_CONTROL, scb->hscb->control);
+			ahd_outb(ahd, MSG_OUT, HOST_MSG);
+			ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
+			/*
+			 * The lun is 0, regardless of the SCB's lun
+			 * as we have not sent an identify message.
+			 */
+			ahd_outb(ahd, SAVED_LUN, 0);
+			ahd_outb(ahd, SEQ_FLAGS, 0);
+			ahd_assert_atn(ahd);
+			scb->flags &= ~SCB_PACKETIZED;
+			scb->flags |= SCB_ABORT|SCB_EXTERNAL_RESET;
+			ahd_freeze_devq(ahd, scb);
+			ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+			ahd_freeze_scb(scb);
+
+			/* Notify XPT */
+			ahd_send_async(ahd, devinfo.channel, devinfo.target,
+				       CAM_LUN_WILDCARD, AC_SENT_BDR);
+
+			/*
+			 * Allow the sequencer to continue with
+			 * non-pack processing.
+			 */
+			ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+			ahd_outb(ahd, CLRLQOINT1, CLRLQOPHACHGINPKT);
+			if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+				ahd_outb(ahd, CLRLQOINT1, 0);
+			}
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+				ahd_print_path(ahd, scb);
+				printk("Unexpected command phase from "
+				       "packetized target\n");
+			}
+#endif
+			break;
+		}
+		}
+		break;
+	}
+	case CFG4OVERRUN:
+	{
+		struct	scb *scb;
+		u_int	scb_index;
+		
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+			printk("%s: CFG4OVERRUN mode = %x\n", ahd_name(ahd),
+			       ahd_inb(ahd, MODE_PTR));
+		}
+#endif
+		scb_index = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scb_index);
+		if (scb == NULL) {
+			/*
+			 * Attempt to transfer to an SCB that is
+			 * not outstanding.
+			 */
+			ahd_assert_atn(ahd);
+			ahd_outb(ahd, MSG_OUT, HOST_MSG);
+			ahd->msgout_buf[0] = MSG_ABORT_TASK;
+			ahd->msgout_len = 1;
+			ahd->msgout_index = 0;
+			ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+			/*
+			 * Clear status received flag to prevent any
+			 * attempt to complete this bogus SCB.
+			 */
+			ahd_outb(ahd, SCB_CONTROL,
+				 ahd_inb_scbram(ahd, SCB_CONTROL)
+				 & ~STATUS_RCVD);
+		}
+		break;
+	}
+	case DUMP_CARD_STATE:
+	{
+		ahd_dump_card_state(ahd);
+		break;
+	}
+	case PDATA_REINIT:
+	{
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+			printk("%s: PDATA_REINIT - DFCNTRL = 0x%x "
+			       "SG_CACHE_SHADOW = 0x%x\n",
+			       ahd_name(ahd), ahd_inb(ahd, DFCNTRL),
+			       ahd_inb(ahd, SG_CACHE_SHADOW));
+		}
+#endif
+		ahd_reinitialize_dataptrs(ahd);
+		break;
+	}
+	case HOST_MSG_LOOP:
+	{
+		struct ahd_devinfo devinfo;
+
+		/*
+		 * The sequencer has encountered a message phase
+		 * that requires host assistance for completion.
+		 * While handling the message phase(s), we will be
+		 * notified by the sequencer after each byte is
+		 * transferred so we can track bus phase changes.
+		 *
+		 * If this is the first time we've seen a HOST_MSG_LOOP
+		 * interrupt, initialize the state of the host message
+		 * loop.
+		 */
+		ahd_fetch_devinfo(ahd, &devinfo);
+		if (ahd->msg_type == MSG_TYPE_NONE) {
+			struct scb *scb;
+			u_int scb_index;
+			u_int bus_phase;
+
+			bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+			if (bus_phase != P_MESGIN
+			 && bus_phase != P_MESGOUT) {
+				printk("ahd_intr: HOST_MSG_LOOP bad "
+				       "phase 0x%x\n", bus_phase);
+				/*
+				 * Probably transitioned to bus free before
+				 * we got here.  Just punt the message.
+				 */
+				ahd_dump_card_state(ahd);
+				ahd_clear_intstat(ahd);
+				ahd_restart(ahd);
+				return;
+			}
+
+			scb_index = ahd_get_scbptr(ahd);
+			scb = ahd_lookup_scb(ahd, scb_index);
+			if (devinfo.role == ROLE_INITIATOR) {
+				if (bus_phase == P_MESGOUT)
+					ahd_setup_initiator_msgout(ahd,
+								   &devinfo,
+								   scb);
+				else {
+					ahd->msg_type =
+					    MSG_TYPE_INITIATOR_MSGIN;
+					ahd->msgin_index = 0;
+				}
+			}
+#ifdef AHD_TARGET_MODE
+			else {
+				if (bus_phase == P_MESGOUT) {
+					ahd->msg_type =
+					    MSG_TYPE_TARGET_MSGOUT;
+					ahd->msgin_index = 0;
+				}
+				else 
+					ahd_setup_target_msgin(ahd,
+							       &devinfo,
+							       scb);
+			}
+#endif
+		}
+
+		ahd_handle_message_phase(ahd);
+		break;
+	}
+	case NO_MATCH:
+	{
+		/* Ensure we don't leave the selection hardware on */
+		AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+		ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+
+		printk("%s:%c:%d: no active SCB for reconnecting "
+		       "target - issuing BUS DEVICE RESET\n",
+		       ahd_name(ahd), 'A', ahd_inb(ahd, SELID) >> 4);
+		printk("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+		       "REG0 == 0x%x ACCUM = 0x%x\n",
+		       ahd_inb(ahd, SAVED_SCSIID), ahd_inb(ahd, SAVED_LUN),
+		       ahd_inw(ahd, REG0), ahd_inb(ahd, ACCUM));
+		printk("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+		       "SINDEX == 0x%x\n",
+		       ahd_inb(ahd, SEQ_FLAGS), ahd_get_scbptr(ahd),
+		       ahd_find_busy_tcl(ahd,
+					 BUILD_TCL(ahd_inb(ahd, SAVED_SCSIID),
+						   ahd_inb(ahd, SAVED_LUN))),
+		       ahd_inw(ahd, SINDEX));
+		printk("SELID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+		       "SCB_CONTROL == 0x%x\n",
+		       ahd_inb(ahd, SELID), ahd_inb_scbram(ahd, SCB_SCSIID),
+		       ahd_inb_scbram(ahd, SCB_LUN),
+		       ahd_inb_scbram(ahd, SCB_CONTROL));
+		printk("SCSIBUS[0] == 0x%x, SCSISIGI == 0x%x\n",
+		       ahd_inb(ahd, SCSIBUS), ahd_inb(ahd, SCSISIGI));
+		printk("SXFRCTL0 == 0x%x\n", ahd_inb(ahd, SXFRCTL0));
+		printk("SEQCTL0 == 0x%x\n", ahd_inb(ahd, SEQCTL0));
+		ahd_dump_card_state(ahd);
+		ahd->msgout_buf[0] = MSG_BUS_DEV_RESET;
+		ahd->msgout_len = 1;
+		ahd->msgout_index = 0;
+		ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+		ahd_outb(ahd, MSG_OUT, HOST_MSG);
+		ahd_assert_atn(ahd);
+		break;
+	}
+	case PROTO_VIOLATION:
+	{
+		ahd_handle_proto_violation(ahd);
+		break;
+	}
+	case IGN_WIDE_RES:
+	{
+		struct ahd_devinfo devinfo;
+
+		ahd_fetch_devinfo(ahd, &devinfo);
+		ahd_handle_ign_wide_residue(ahd, &devinfo);
+		break;
+	}
+	case BAD_PHASE:
+	{
+		u_int lastphase;
+
+		lastphase = ahd_inb(ahd, LASTPHASE);
+		printk("%s:%c:%d: unknown scsi bus phase %x, "
+		       "lastphase = 0x%x.  Attempting to continue\n",
+		       ahd_name(ahd), 'A',
+		       SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
+		       lastphase, ahd_inb(ahd, SCSISIGI));
+		break;
+	}
+	case MISSED_BUSFREE:
+	{
+		u_int lastphase;
+
+		lastphase = ahd_inb(ahd, LASTPHASE);
+		printk("%s:%c:%d: Missed busfree. "
+		       "Lastphase = 0x%x, Curphase = 0x%x\n",
+		       ahd_name(ahd), 'A',
+		       SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
+		       lastphase, ahd_inb(ahd, SCSISIGI));
+		ahd_restart(ahd);
+		return;
+	}
+	case DATA_OVERRUN:
+	{
+		/*
+		 * When the sequencer detects an overrun, it
+		 * places the controller in "BITBUCKET" mode
+		 * and allows the target to complete its transfer.
+		 * Unfortunately, none of the counters get updated
+		 * when the controller is in this mode, so we have
+		 * no way of knowing how large the overrun was.
+		 */
+		struct	scb *scb;
+		u_int	scbindex;
+#ifdef AHD_DEBUG
+		u_int	lastphase;
+#endif
+
+		scbindex = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbindex);
+#ifdef AHD_DEBUG
+		lastphase = ahd_inb(ahd, LASTPHASE);
+		if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+			ahd_print_path(ahd, scb);
+			printk("data overrun detected %s.  Tag == 0x%x.\n",
+			       ahd_lookup_phase_entry(lastphase)->phasemsg,
+			       SCB_GET_TAG(scb));
+			ahd_print_path(ahd, scb);
+			printk("%s seen Data Phase.  Length = %ld.  "
+			       "NumSGs = %d.\n",
+			       ahd_inb(ahd, SEQ_FLAGS) & DPHASE
+			       ? "Have" : "Haven't",
+			       ahd_get_transfer_length(scb), scb->sg_count);
+			ahd_dump_sglist(scb);
+		}
+#endif
+
+		/*
+		 * Set this and it will take effect when the
+		 * target does a command complete.
+		 */
+		ahd_freeze_devq(ahd, scb);
+		ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+		ahd_freeze_scb(scb);
+		break;
+	}
+	case MKMSG_FAILED:
+	{
+		struct ahd_devinfo devinfo;
+		struct scb *scb;
+		u_int scbid;
+
+		ahd_fetch_devinfo(ahd, &devinfo);
+		printk("%s:%c:%d:%d: Attempt to issue message failed\n",
+		       ahd_name(ahd), devinfo.channel, devinfo.target,
+		       devinfo.lun);
+		scbid = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb != NULL
+		 && (scb->flags & SCB_RECOVERY_SCB) != 0)
+			/*
+			 * Ensure that we didn't put a second instance of this
+			 * SCB into the QINFIFO.
+			 */
+			ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+					   SCB_GET_CHANNEL(ahd, scb),
+					   SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+					   ROLE_INITIATOR, /*status*/0,
+					   SEARCH_REMOVE);
+		ahd_outb(ahd, SCB_CONTROL,
+			 ahd_inb_scbram(ahd, SCB_CONTROL) & ~MK_MESSAGE);
+		break;
+	}
+	case TASKMGMT_FUNC_COMPLETE:
+	{
+		u_int	scbid;
+		struct	scb *scb;
+
+		scbid = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb != NULL) {
+			u_int	   lun;
+			u_int	   tag;
+			cam_status error;
+
+			ahd_print_path(ahd, scb);
+			printk("Task Management Func 0x%x Complete\n",
+			       scb->hscb->task_management);
+			lun = CAM_LUN_WILDCARD;
+			tag = SCB_LIST_NULL;
+
+			switch (scb->hscb->task_management) {
+			case SIU_TASKMGMT_ABORT_TASK:
+				tag = SCB_GET_TAG(scb);
+			case SIU_TASKMGMT_ABORT_TASK_SET:
+			case SIU_TASKMGMT_CLEAR_TASK_SET:
+				lun = scb->hscb->lun;
+				error = CAM_REQ_ABORTED;
+				ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+					       'A', lun, tag, ROLE_INITIATOR,
+					       error);
+				break;
+			case SIU_TASKMGMT_LUN_RESET:
+				lun = scb->hscb->lun;
+			case SIU_TASKMGMT_TARGET_RESET:
+			{
+				struct ahd_devinfo devinfo;
+
+				ahd_scb_devinfo(ahd, &devinfo, scb);
+				error = CAM_BDR_SENT;
+				ahd_handle_devreset(ahd, &devinfo, lun,
+						    CAM_BDR_SENT,
+						    lun != CAM_LUN_WILDCARD
+						    ? "Lun Reset"
+						    : "Target Reset",
+						    /*verbose_level*/0);
+				break;
+			}
+			default:
+				panic("Unexpected TaskMgmt Func\n");
+				break;
+			}
+		}
+		break;
+	}
+	case TASKMGMT_CMD_CMPLT_OKAY:
+	{
+		u_int	scbid;
+		struct	scb *scb;
+
+		/*
+		 * An ABORT TASK TMF failed to be delivered before
+		 * the targeted command completed normally.
+		 */
+		scbid = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb != NULL) {
+			/*
+			 * Remove the second instance of this SCB from
+			 * the QINFIFO if it is still there.
+                         */
+			ahd_print_path(ahd, scb);
+			printk("SCB completes before TMF\n");
+			/*
+			 * Handle losing the race.  Wait until any
+			 * current selection completes.  We will then
+			 * set the TMF back to zero in this SCB so that
+			 * the sequencer doesn't bother to issue another
+			 * sequencer interrupt for its completion.
+			 */
+			while ((ahd_inb(ahd, SCSISEQ0) & ENSELO) != 0
+			    && (ahd_inb(ahd, SSTAT0) & SELDO) == 0
+			    && (ahd_inb(ahd, SSTAT1) & SELTO) == 0)
+				;
+			ahd_outb(ahd, SCB_TASK_MANAGEMENT, 0);
+			ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+					   SCB_GET_CHANNEL(ahd, scb),  
+					   SCB_GET_LUN(scb), SCB_GET_TAG(scb), 
+					   ROLE_INITIATOR, /*status*/0,   
+					   SEARCH_REMOVE);
+		}
+		break;
+	}
+	case TRACEPOINT0:
+	case TRACEPOINT1:
+	case TRACEPOINT2:
+	case TRACEPOINT3:
+		printk("%s: Tracepoint %d\n", ahd_name(ahd),
+		       seqintcode - TRACEPOINT0);
+		break;
+	case NO_SEQINT:
+		break;
+	case SAW_HWERR:
+		ahd_handle_hwerrint(ahd);
+		break;
+	default:
+		printk("%s: Unexpected SEQINTCODE %d\n", ahd_name(ahd),
+		       seqintcode);
+		break;
+	}
+	/*
+	 *  The sequencer is paused immediately on
+	 *  a SEQINT, so we should restart it when
+	 *  we're done.
+	 */
+	ahd_unpause(ahd);
+}
+
+static void
+ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
+{
+	struct scb	*scb;
+	u_int		 status0;
+	u_int		 status3;
+	u_int		 status;
+	u_int		 lqistat1;
+	u_int		 lqostat0;
+	u_int		 scbid;
+	u_int		 busfreetime;
+
+	ahd_update_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+	status3 = ahd_inb(ahd, SSTAT3) & (NTRAMPERR|OSRAMPERR);
+	status0 = ahd_inb(ahd, SSTAT0) & (IOERR|OVERRUN|SELDI|SELDO);
+	status = ahd_inb(ahd, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+	lqistat1 = ahd_inb(ahd, LQISTAT1);
+	lqostat0 = ahd_inb(ahd, LQOSTAT0);
+	busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
+
+	/*
+	 * Ignore external resets after a bus reset.
+	 */
+	if (((status & SCSIRSTI) != 0) && (ahd->flags & AHD_BUS_RESET_ACTIVE)) {
+		ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
+		return;
+	}
+
+	/*
+	 * Clear bus reset flag
+	 */
+	ahd->flags &= ~AHD_BUS_RESET_ACTIVE;
+
+	if ((status0 & (SELDI|SELDO)) != 0) {
+		u_int simode0;
+
+		ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+		simode0 = ahd_inb(ahd, SIMODE0);
+		status0 &= simode0 & (IOERR|OVERRUN|SELDI|SELDO);
+		ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	}
+	scbid = ahd_get_scbptr(ahd);
+	scb = ahd_lookup_scb(ahd, scbid);
+	if (scb != NULL
+	 && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+		scb = NULL;
+
+	if ((status0 & IOERR) != 0) {
+		u_int now_lvd;
+
+		now_lvd = ahd_inb(ahd, SBLKCTL) & ENAB40;
+		printk("%s: Transceiver State Has Changed to %s mode\n",
+		       ahd_name(ahd), now_lvd ? "LVD" : "SE");
+		ahd_outb(ahd, CLRSINT0, CLRIOERR);
+		/*
+		 * A change in I/O mode is equivalent to a bus reset.
+		 */
+		ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+		ahd_pause(ahd);
+		ahd_setup_iocell_workaround(ahd);
+		ahd_unpause(ahd);
+	} else if ((status0 & OVERRUN) != 0) {
+
+		printk("%s: SCSI offset overrun detected.  Resetting bus.\n",
+		       ahd_name(ahd));
+		ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+	} else if ((status & SCSIRSTI) != 0) {
+
+		printk("%s: Someone reset channel A\n", ahd_name(ahd));
+		ahd_reset_channel(ahd, 'A', /*Initiate Reset*/FALSE);
+	} else if ((status & SCSIPERR) != 0) {
+
+		/* Make sure the sequencer is in a safe location. */
+		ahd_clear_critical_section(ahd);
+
+		ahd_handle_transmission_error(ahd);
+	} else if (lqostat0 != 0) {
+
+		printk("%s: lqostat0 == 0x%x!\n", ahd_name(ahd), lqostat0);
+		ahd_outb(ahd, CLRLQOINT0, lqostat0);
+		if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0)
+			ahd_outb(ahd, CLRLQOINT1, 0);
+	} else if ((status & SELTO) != 0) {
+		/* Stop the selection */
+		ahd_outb(ahd, SCSISEQ0, 0);
+
+		/* Make sure the sequencer is in a safe location. */
+		ahd_clear_critical_section(ahd);
+
+		/* No more pending messages */
+		ahd_clear_msg_state(ahd);
+
+		/* Clear interrupt state */
+		ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+		/*
+		 * Although the driver does not care about the
+		 * 'Selection in Progress' status bit, the busy
+		 * LED does.  SELINGO is only cleared by a successful
+		 * selection, so we must manually clear it to insure
+		 * the LED turns off just incase no future successful
+		 * selections occur (e.g. no devices on the bus).
+		 */
+		ahd_outb(ahd, CLRSINT0, CLRSELINGO);
+
+		scbid = ahd_inw(ahd, WAITING_TID_HEAD);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			printk("%s: ahd_intr - referenced scb not "
+			       "valid during SELTO scb(0x%x)\n",
+			       ahd_name(ahd), scbid);
+			ahd_dump_card_state(ahd);
+		} else {
+			struct ahd_devinfo devinfo;
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_SELTO) != 0) {
+				ahd_print_path(ahd, scb);
+				printk("Saw Selection Timeout for SCB 0x%x\n",
+				       scbid);
+			}
+#endif
+			ahd_scb_devinfo(ahd, &devinfo, scb);
+			ahd_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+			ahd_freeze_devq(ahd, scb);
+
+			/*
+			 * Cancel any pending transactions on the device
+			 * now that it seems to be missing.  This will
+			 * also revert us to async/narrow transfers until
+			 * we can renegotiate with the device.
+			 */
+			ahd_handle_devreset(ahd, &devinfo,
+					    CAM_LUN_WILDCARD,
+					    CAM_SEL_TIMEOUT,
+					    "Selection Timeout",
+					    /*verbose_level*/1);
+		}
+		ahd_outb(ahd, CLRINT, CLRSCSIINT);
+		ahd_iocell_first_selection(ahd);
+		ahd_unpause(ahd);
+	} else if ((status0 & (SELDI|SELDO)) != 0) {
+
+		ahd_iocell_first_selection(ahd);
+		ahd_unpause(ahd);
+	} else if (status3 != 0) {
+		printk("%s: SCSI Cell parity error SSTAT3 == 0x%x\n",
+		       ahd_name(ahd), status3);
+		ahd_outb(ahd, CLRSINT3, status3);
+	} else if ((lqistat1 & (LQIPHASE_LQ|LQIPHASE_NLQ)) != 0) {
+
+		/* Make sure the sequencer is in a safe location. */
+		ahd_clear_critical_section(ahd);
+
+		ahd_handle_lqiphase_error(ahd, lqistat1);
+	} else if ((lqistat1 & LQICRCI_NLQ) != 0) {
+		/*
+		 * This status can be delayed during some
+		 * streaming operations.  The SCSIPHASE
+		 * handler has already dealt with this case
+		 * so just clear the error.
+		 */
+		ahd_outb(ahd, CLRLQIINT1, CLRLQICRCI_NLQ);
+	} else if ((status & BUSFREE) != 0
+		|| (lqistat1 & LQOBUSFREE) != 0) {
+		u_int lqostat1;
+		int   restart;
+		int   clear_fifo;
+		int   packetized;
+		u_int mode;
+
+		/*
+		 * Clear our selection hardware as soon as possible.
+		 * We may have an entry in the waiting Q for this target,
+		 * that is affected by this busfree and we don't want to
+		 * go about selecting the target while we handle the event.
+		 */
+		ahd_outb(ahd, SCSISEQ0, 0);
+
+		/* Make sure the sequencer is in a safe location. */
+		ahd_clear_critical_section(ahd);
+
+		/*
+		 * Determine what we were up to at the time of
+		 * the busfree.
+		 */
+		mode = AHD_MODE_SCSI;
+		busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
+		lqostat1 = ahd_inb(ahd, LQOSTAT1);
+		switch (busfreetime) {
+		case BUSFREE_DFF0:
+		case BUSFREE_DFF1:
+		{
+			mode = busfreetime == BUSFREE_DFF0
+			     ? AHD_MODE_DFF0 : AHD_MODE_DFF1;
+			ahd_set_modes(ahd, mode, mode);
+			scbid = ahd_get_scbptr(ahd);
+			scb = ahd_lookup_scb(ahd, scbid);
+			if (scb == NULL) {
+				printk("%s: Invalid SCB %d in DFF%d "
+				       "during unexpected busfree\n",
+				       ahd_name(ahd), scbid, mode);
+				packetized = 0;
+			} else
+				packetized = (scb->flags & SCB_PACKETIZED) != 0;
+			clear_fifo = 1;
+			break;
+		}
+		case BUSFREE_LQO:
+			clear_fifo = 0;
+			packetized = 1;
+			break;
+		default:
+			clear_fifo = 0;
+			packetized =  (lqostat1 & LQOBUSFREE) != 0;
+			if (!packetized
+			 && ahd_inb(ahd, LASTPHASE) == P_BUSFREE
+			 && (ahd_inb(ahd, SSTAT0) & SELDI) == 0
+			 && ((ahd_inb(ahd, SSTAT0) & SELDO) == 0
+			  || (ahd_inb(ahd, SCSISEQ0) & ENSELO) == 0))
+				/*
+				 * Assume packetized if we are not
+				 * on the bus in a non-packetized
+				 * capacity and any pending selection
+				 * was a packetized selection.
+				 */
+				packetized = 1;
+			break;
+		}
+
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MISC) != 0)
+			printk("Saw Busfree.  Busfreetime = 0x%x.\n",
+			       busfreetime);
+#endif
+		/*
+		 * Busfrees that occur in non-packetized phases are
+		 * handled by the nonpkt_busfree handler.
+		 */
+		if (packetized && ahd_inb(ahd, LASTPHASE) == P_BUSFREE) {
+			restart = ahd_handle_pkt_busfree(ahd, busfreetime);
+		} else {
+			packetized = 0;
+			restart = ahd_handle_nonpkt_busfree(ahd);
+		}
+		/*
+		 * Clear the busfree interrupt status.  The setting of
+		 * the interrupt is a pulse, so in a perfect world, we
+		 * would not need to muck with the ENBUSFREE logic.  This
+		 * would ensure that if the bus moves on to another
+		 * connection, busfree protection is still in force.  If
+		 * BUSFREEREV is broken, however, we must manually clear
+		 * the ENBUSFREE if the busfree occurred during a non-pack
+		 * connection so that we don't get false positives during
+		 * future, packetized, connections.
+		 */
+		ahd_outb(ahd, CLRSINT1, CLRBUSFREE);
+		if (packetized == 0
+		 && (ahd->bugs & AHD_BUSFREEREV_BUG) != 0)
+			ahd_outb(ahd, SIMODE1,
+				 ahd_inb(ahd, SIMODE1) & ~ENBUSFREE);
+
+		if (clear_fifo)
+			ahd_clear_fifo(ahd, mode);
+
+		ahd_clear_msg_state(ahd);
+		ahd_outb(ahd, CLRINT, CLRSCSIINT);
+		if (restart) {
+			ahd_restart(ahd);
+		} else {
+			ahd_unpause(ahd);
+		}
+	} else {
+		printk("%s: Missing case in ahd_handle_scsiint. status = %x\n",
+		       ahd_name(ahd), status);
+		ahd_dump_card_state(ahd);
+		ahd_clear_intstat(ahd);
+		ahd_unpause(ahd);
+	}
+}
+
+static void
+ahd_handle_transmission_error(struct ahd_softc *ahd)
+{
+	struct	scb *scb;
+	u_int	scbid;
+	u_int	lqistat1;
+	u_int	lqistat2;
+	u_int	msg_out;
+	u_int	curphase;
+	u_int	lastphase;
+	u_int	perrdiag;
+	u_int	cur_col;
+	int	silent;
+
+	scb = NULL;
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	lqistat1 = ahd_inb(ahd, LQISTAT1) & ~(LQIPHASE_LQ|LQIPHASE_NLQ);
+	lqistat2 = ahd_inb(ahd, LQISTAT2);
+	if ((lqistat1 & (LQICRCI_NLQ|LQICRCI_LQ)) == 0
+	 && (ahd->bugs & AHD_NLQICRC_DELAYED_BUG) != 0) {
+		u_int lqistate;
+
+		ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+		lqistate = ahd_inb(ahd, LQISTATE);
+		if ((lqistate >= 0x1E && lqistate <= 0x24)
+		 || (lqistate == 0x29)) {
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
+				printk("%s: NLQCRC found via LQISTATE\n",
+				       ahd_name(ahd));
+			}
+#endif
+			lqistat1 |= LQICRCI_NLQ;
+		}
+		ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	}
+
+	ahd_outb(ahd, CLRLQIINT1, lqistat1);
+	lastphase = ahd_inb(ahd, LASTPHASE);
+	curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+	perrdiag = ahd_inb(ahd, PERRDIAG);
+	msg_out = MSG_INITIATOR_DET_ERR;
+	ahd_outb(ahd, CLRSINT1, CLRSCSIPERR);
+	
+	/*
+	 * Try to find the SCB associated with this error.
+	 */
+	silent = FALSE;
+	if (lqistat1 == 0
+	 || (lqistat1 & LQICRCI_NLQ) != 0) {
+	 	if ((lqistat1 & (LQICRCI_NLQ|LQIOVERI_NLQ)) != 0)
+			ahd_set_active_fifo(ahd);
+		scbid = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb != NULL && SCB_IS_SILENT(scb))
+			silent = TRUE;
+	}
+
+	cur_col = 0;
+	if (silent == FALSE) {
+		printk("%s: Transmission error detected\n", ahd_name(ahd));
+		ahd_lqistat1_print(lqistat1, &cur_col, 50);
+		ahd_lastphase_print(lastphase, &cur_col, 50);
+		ahd_scsisigi_print(curphase, &cur_col, 50);
+		ahd_perrdiag_print(perrdiag, &cur_col, 50);
+		printk("\n");
+		ahd_dump_card_state(ahd);
+	}
+
+	if ((lqistat1 & (LQIOVERI_LQ|LQIOVERI_NLQ)) != 0) {
+		if (silent == FALSE) {
+			printk("%s: Gross protocol error during incoming "
+			       "packet.  lqistat1 == 0x%x.  Resetting bus.\n",
+			       ahd_name(ahd), lqistat1);
+		}
+		ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+		return;
+	} else if ((lqistat1 & LQICRCI_LQ) != 0) {
+		/*
+		 * A CRC error has been detected on an incoming LQ.
+		 * The bus is currently hung on the last ACK.
+		 * Hit LQIRETRY to release the last ack, and
+		 * wait for the sequencer to determine that ATNO
+		 * is asserted while in message out to take us
+		 * to our host message loop.  No NONPACKREQ or
+		 * LQIPHASE type errors will occur in this
+		 * scenario.  After this first LQIRETRY, the LQI
+		 * manager will be in ISELO where it will
+		 * happily sit until another packet phase begins.
+		 * Unexpected bus free detection is enabled
+		 * through any phases that occur after we release
+		 * this last ack until the LQI manager sees a
+		 * packet phase.  This implies we may have to
+		 * ignore a perfectly valid "unexected busfree"
+		 * after our "initiator detected error" message is
+		 * sent.  A busfree is the expected response after
+		 * we tell the target that it's L_Q was corrupted.
+		 * (SPI4R09 10.7.3.3.3)
+		 */
+		ahd_outb(ahd, LQCTL2, LQIRETRY);
+		printk("LQIRetry for LQICRCI_LQ to release ACK\n");
+	} else if ((lqistat1 & LQICRCI_NLQ) != 0) {
+		/*
+		 * We detected a CRC error in a NON-LQ packet.
+		 * The hardware has varying behavior in this situation
+		 * depending on whether this packet was part of a
+		 * stream or not.
+		 *
+		 * PKT by PKT mode:
+		 * The hardware has already acked the complete packet.
+		 * If the target honors our outstanding ATN condition,
+		 * we should be (or soon will be) in MSGOUT phase.
+		 * This will trigger the LQIPHASE_LQ status bit as the
+		 * hardware was expecting another LQ.  Unexpected
+		 * busfree detection is enabled.  Once LQIPHASE_LQ is
+		 * true (first entry into host message loop is much
+		 * the same), we must clear LQIPHASE_LQ and hit
+		 * LQIRETRY so the hardware is ready to handle
+		 * a future LQ.  NONPACKREQ will not be asserted again
+		 * once we hit LQIRETRY until another packet is
+		 * processed.  The target may either go busfree
+		 * or start another packet in response to our message.
+		 *
+		 * Read Streaming P0 asserted:
+		 * If we raise ATN and the target completes the entire
+		 * stream (P0 asserted during the last packet), the
+		 * hardware will ack all data and return to the ISTART
+		 * state.  When the target reponds to our ATN condition,
+		 * LQIPHASE_LQ will be asserted.  We should respond to
+		 * this with an LQIRETRY to prepare for any future
+		 * packets.  NONPACKREQ will not be asserted again
+		 * once we hit LQIRETRY until another packet is
+		 * processed.  The target may either go busfree or
+		 * start another packet in response to our message.
+		 * Busfree detection is enabled.
+		 *
+		 * Read Streaming P0 not asserted:
+		 * If we raise ATN and the target transitions to
+		 * MSGOUT in or after a packet where P0 is not
+		 * asserted, the hardware will assert LQIPHASE_NLQ.
+		 * We should respond to the LQIPHASE_NLQ with an
+		 * LQIRETRY.  Should the target stay in a non-pkt
+		 * phase after we send our message, the hardware
+		 * will assert LQIPHASE_LQ.  Recovery is then just as
+		 * listed above for the read streaming with P0 asserted.
+		 * Busfree detection is enabled.
+		 */
+		if (silent == FALSE)
+			printk("LQICRC_NLQ\n");
+		if (scb == NULL) {
+			printk("%s: No SCB valid for LQICRC_NLQ.  "
+			       "Resetting bus\n", ahd_name(ahd));
+			ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+			return;
+		}
+	} else if ((lqistat1 & LQIBADLQI) != 0) {
+		printk("Need to handle BADLQI!\n");
+		ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+		return;
+	} else if ((perrdiag & (PARITYERR|PREVPHASE)) == PARITYERR) {
+		if ((curphase & ~P_DATAIN_DT) != 0) {
+			/* Ack the byte.  So we can continue. */
+			if (silent == FALSE)
+				printk("Acking %s to clear perror\n",
+				    ahd_lookup_phase_entry(curphase)->phasemsg);
+			ahd_inb(ahd, SCSIDAT);
+		}
+	
+		if (curphase == P_MESGIN)
+			msg_out = MSG_PARITY_ERROR;
+	}
+
+	/*
+	 * We've set the hardware to assert ATN if we 
+	 * get a parity error on "in" phases, so all we
+	 * need to do is stuff the message buffer with
+	 * the appropriate message.  "In" phases have set
+	 * mesg_out to something other than MSG_NOP.
+	 */
+	ahd->send_msg_perror = msg_out;
+	if (scb != NULL && msg_out == MSG_INITIATOR_DET_ERR)
+		scb->flags |= SCB_TRANSMISSION_ERROR;
+	ahd_outb(ahd, MSG_OUT, HOST_MSG);
+	ahd_outb(ahd, CLRINT, CLRSCSIINT);
+	ahd_unpause(ahd);
+}
+
+static void
+ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
+{
+	/*
+	 * Clear the sources of the interrupts.
+	 */
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	ahd_outb(ahd, CLRLQIINT1, lqistat1);
+
+	/*
+	 * If the "illegal" phase changes were in response
+	 * to our ATN to flag a CRC error, AND we ended up
+	 * on packet boundaries, clear the error, restart the
+	 * LQI manager as appropriate, and go on our merry
+	 * way toward sending the message.  Otherwise, reset
+	 * the bus to clear the error.
+	 */
+	ahd_set_active_fifo(ahd);
+	if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0
+	 && (ahd_inb(ahd, MDFFSTAT) & DLZERO) != 0) {
+		if ((lqistat1 & LQIPHASE_LQ) != 0) {
+			printk("LQIRETRY for LQIPHASE_LQ\n");
+			ahd_outb(ahd, LQCTL2, LQIRETRY);
+		} else if ((lqistat1 & LQIPHASE_NLQ) != 0) {
+			printk("LQIRETRY for LQIPHASE_NLQ\n");
+			ahd_outb(ahd, LQCTL2, LQIRETRY);
+		} else
+			panic("ahd_handle_lqiphase_error: No phase errors\n");
+		ahd_dump_card_state(ahd);
+		ahd_outb(ahd, CLRINT, CLRSCSIINT);
+		ahd_unpause(ahd);
+	} else {
+		printk("Resetting Channel for LQI Phase error\n");
+		ahd_dump_card_state(ahd);
+		ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+	}
+}
+
+/*
+ * Packetized unexpected or expected busfree.
+ * Entered in mode based on busfreetime.
+ */
+static int
+ahd_handle_pkt_busfree(struct ahd_softc *ahd, u_int busfreetime)
+{
+	u_int lqostat1;
+
+	AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+			 ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+	lqostat1 = ahd_inb(ahd, LQOSTAT1);
+	if ((lqostat1 & LQOBUSFREE) != 0) {
+		struct scb *scb;
+		u_int scbid;
+		u_int saved_scbptr;
+		u_int waiting_h;
+		u_int waiting_t;
+		u_int next;
+
+		/*
+		 * The LQO manager detected an unexpected busfree
+		 * either:
+		 *
+		 * 1) During an outgoing LQ.
+		 * 2) After an outgoing LQ but before the first
+		 *    REQ of the command packet.
+		 * 3) During an outgoing command packet.
+		 *
+		 * In all cases, CURRSCB is pointing to the
+		 * SCB that encountered the failure.  Clean
+		 * up the queue, clear SELDO and LQOBUSFREE,
+		 * and allow the sequencer to restart the select
+		 * out at its lesure.
+		 */
+		ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+		scbid = ahd_inw(ahd, CURRSCB);
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL)
+		       panic("SCB not valid during LQOBUSFREE");
+		/*
+		 * Clear the status.
+		 */
+		ahd_outb(ahd, CLRLQOINT1, CLRLQOBUSFREE);
+		if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0)
+			ahd_outb(ahd, CLRLQOINT1, 0);
+		ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+		ahd_flush_device_writes(ahd);
+		ahd_outb(ahd, CLRSINT0, CLRSELDO);
+
+		/*
+		 * Return the LQO manager to its idle loop.  It will
+		 * not do this automatically if the busfree occurs
+		 * after the first REQ of either the LQ or command
+		 * packet or between the LQ and command packet.
+		 */
+		ahd_outb(ahd, LQCTL2, ahd_inb(ahd, LQCTL2) | LQOTOIDLE);
+
+		/*
+		 * Update the waiting for selection queue so
+		 * we restart on the correct SCB.
+		 */
+		waiting_h = ahd_inw(ahd, WAITING_TID_HEAD);
+		saved_scbptr = ahd_get_scbptr(ahd);
+		if (waiting_h != scbid) {
+
+			ahd_outw(ahd, WAITING_TID_HEAD, scbid);
+			waiting_t = ahd_inw(ahd, WAITING_TID_TAIL);
+			if (waiting_t == waiting_h) {
+				ahd_outw(ahd, WAITING_TID_TAIL, scbid);
+				next = SCB_LIST_NULL;
+			} else {
+				ahd_set_scbptr(ahd, waiting_h);
+				next = ahd_inw_scbram(ahd, SCB_NEXT2);
+			}
+			ahd_set_scbptr(ahd, scbid);
+			ahd_outw(ahd, SCB_NEXT2, next);
+		}
+		ahd_set_scbptr(ahd, saved_scbptr);
+		if (scb->crc_retry_count < AHD_MAX_LQ_CRC_ERRORS) {
+			if (SCB_IS_SILENT(scb) == FALSE) {
+				ahd_print_path(ahd, scb);
+				printk("Probable outgoing LQ CRC error.  "
+				       "Retrying command\n");
+			}
+			scb->crc_retry_count++;
+		} else {
+			ahd_set_transaction_status(scb, CAM_UNCOR_PARITY);
+			ahd_freeze_scb(scb);
+			ahd_freeze_devq(ahd, scb);
+		}
+		/* Return unpausing the sequencer. */
+		return (0);
+	} else if ((ahd_inb(ahd, PERRDIAG) & PARITYERR) != 0) {
+		/*
+		 * Ignore what are really parity errors that
+		 * occur on the last REQ of a free running
+		 * clock prior to going busfree.  Some drives
+		 * do not properly active negate just before
+		 * going busfree resulting in a parity glitch.
+		 */
+		ahd_outb(ahd, CLRSINT1, CLRSCSIPERR|CLRBUSFREE);
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MASKED_ERRORS) != 0)
+			printk("%s: Parity on last REQ detected "
+			       "during busfree phase.\n",
+			       ahd_name(ahd));
+#endif
+		/* Return unpausing the sequencer. */
+		return (0);
+	}
+	if (ahd->src_mode != AHD_MODE_SCSI) {
+		u_int	scbid;
+		struct	scb *scb;
+
+		scbid = ahd_get_scbptr(ahd);
+		scb = ahd_lookup_scb(ahd, scbid);
+		ahd_print_path(ahd, scb);
+		printk("Unexpected PKT busfree condition\n");
+		ahd_dump_card_state(ahd);
+		ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), 'A',
+			       SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+			       ROLE_INITIATOR, CAM_UNEXP_BUSFREE);
+
+		/* Return restarting the sequencer. */
+		return (1);
+	}
+	printk("%s: Unexpected PKT busfree condition\n", ahd_name(ahd));
+	ahd_dump_card_state(ahd);
+	/* Restart the sequencer. */
+	return (1);
+}
+
+/*
+ * Non-packetized unexpected or expected busfree.
+ */
+static int
+ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
+{
+	struct	ahd_devinfo devinfo;
+	struct	scb *scb;
+	u_int	lastphase;
+	u_int	saved_scsiid;
+	u_int	saved_lun;
+	u_int	target;
+	u_int	initiator_role_id;
+	u_int	scbid;
+	u_int	ppr_busfree;
+	int	printerror;
+
+	/*
+	 * Look at what phase we were last in.  If its message out,
+	 * chances are pretty good that the busfree was in response
+	 * to one of our abort requests.
+	 */
+	lastphase = ahd_inb(ahd, LASTPHASE);
+	saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+	saved_lun = ahd_inb(ahd, SAVED_LUN);
+	target = SCSIID_TARGET(ahd, saved_scsiid);
+	initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+	ahd_compile_devinfo(&devinfo, initiator_role_id,
+			    target, saved_lun, 'A', ROLE_INITIATOR);
+	printerror = 1;
+
+	scbid = ahd_get_scbptr(ahd);
+	scb = ahd_lookup_scb(ahd, scbid);
+	if (scb != NULL
+	 && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+		scb = NULL;
+
+	ppr_busfree = (ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0;
+	if (lastphase == P_MESGOUT) {
+		u_int tag;
+
+		tag = SCB_LIST_NULL;
+		if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT_TAG, TRUE)
+		 || ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT, TRUE)) {
+			int found;
+			int sent_msg;
+
+			if (scb == NULL) {
+				ahd_print_devinfo(ahd, &devinfo);
+				printk("Abort for unidentified "
+				       "connection completed.\n");
+				/* restart the sequencer. */
+				return (1);
+			}
+			sent_msg = ahd->msgout_buf[ahd->msgout_index - 1];
+			ahd_print_path(ahd, scb);
+			printk("SCB %d - Abort%s Completed.\n",
+			       SCB_GET_TAG(scb),
+			       sent_msg == MSG_ABORT_TAG ? "" : " Tag");
+
+			if (sent_msg == MSG_ABORT_TAG)
+				tag = SCB_GET_TAG(scb);
+
+			if ((scb->flags & SCB_EXTERNAL_RESET) != 0) {
+				/*
+				 * This abort is in response to an
+				 * unexpected switch to command phase
+				 * for a packetized connection.  Since
+				 * the identify message was never sent,
+				 * "saved lun" is 0.  We really want to
+				 * abort only the SCB that encountered
+				 * this error, which could have a different
+				 * lun.  The SCB will be retried so the OS
+				 * will see the UA after renegotiating to
+				 * packetized.
+				 */
+				tag = SCB_GET_TAG(scb);
+				saved_lun = scb->hscb->lun;
+			}
+			found = ahd_abort_scbs(ahd, target, 'A', saved_lun,
+					       tag, ROLE_INITIATOR,
+					       CAM_REQ_ABORTED);
+			printk("found == 0x%x\n", found);
+			printerror = 0;
+		} else if (ahd_sent_msg(ahd, AHDMSG_1B,
+					MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+			/*
+			 * Don't mark the user's request for this BDR
+			 * as completing with CAM_BDR_SENT.  CAM3
+			 * specifies CAM_REQ_CMP.
+			 */
+			if (scb != NULL
+			 && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+			 && ahd_match_scb(ahd, scb, target, 'A',
+					  CAM_LUN_WILDCARD, SCB_LIST_NULL,
+					  ROLE_INITIATOR))
+				ahd_set_transaction_status(scb, CAM_REQ_CMP);
+#endif
+			ahd_handle_devreset(ahd, &devinfo, CAM_LUN_WILDCARD,
+					    CAM_BDR_SENT, "Bus Device Reset",
+					    /*verbose_level*/0);
+			printerror = 0;
+		} else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, FALSE)
+			&& ppr_busfree == 0) {
+			struct ahd_initiator_tinfo *tinfo;
+			struct ahd_tmode_tstate *tstate;
+
+			/*
+			 * PPR Rejected.
+			 *
+			 * If the previous negotiation was packetized,
+			 * this could be because the device has been
+			 * reset without our knowledge.  Force our
+			 * current negotiation to async and retry the
+			 * negotiation.  Otherwise retry the command
+			 * with non-ppr negotiation.
+			 */
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+				printk("PPR negotiation rejected busfree.\n");
+#endif
+			tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+						    devinfo.our_scsiid,
+						    devinfo.target, &tstate);
+			if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ)!=0) {
+				ahd_set_width(ahd, &devinfo,
+					      MSG_EXT_WDTR_BUS_8_BIT,
+					      AHD_TRANS_CUR,
+					      /*paused*/TRUE);
+				ahd_set_syncrate(ahd, &devinfo,
+						/*period*/0, /*offset*/0,
+						/*ppr_options*/0,
+						AHD_TRANS_CUR,
+						/*paused*/TRUE);
+				/*
+				 * The expect PPR busfree handler below
+				 * will effect the retry and necessary
+				 * abort.
+				 */
+			} else {
+				tinfo->curr.transport_version = 2;
+				tinfo->goal.transport_version = 2;
+				tinfo->goal.ppr_options = 0;
+				if (scb != NULL) {
+					/*
+					 * Remove any SCBs in the waiting
+					 * for selection queue that may
+					 * also be for this target so that
+					 * command ordering is preserved.
+					 */
+					ahd_freeze_devq(ahd, scb);
+					ahd_qinfifo_requeue_tail(ahd, scb);
+				}
+				printerror = 0;
+			}
+		} else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, FALSE)
+			&& ppr_busfree == 0) {
+			/*
+			 * Negotiation Rejected.  Go-narrow and
+			 * retry command.
+			 */
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+				printk("WDTR negotiation rejected busfree.\n");
+#endif
+			ahd_set_width(ahd, &devinfo,
+				      MSG_EXT_WDTR_BUS_8_BIT,
+				      AHD_TRANS_CUR|AHD_TRANS_GOAL,
+				      /*paused*/TRUE);
+			if (scb != NULL) {
+				/*
+				 * Remove any SCBs in the waiting for
+				 * selection queue that may also be for
+				 * this target so that command ordering
+				 * is preserved.
+				 */
+				ahd_freeze_devq(ahd, scb);
+				ahd_qinfifo_requeue_tail(ahd, scb);
+			}
+			printerror = 0;
+		} else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, FALSE)
+			&& ppr_busfree == 0) {
+			/*
+			 * Negotiation Rejected.  Go-async and
+			 * retry command.
+			 */
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+				printk("SDTR negotiation rejected busfree.\n");
+#endif
+			ahd_set_syncrate(ahd, &devinfo,
+					/*period*/0, /*offset*/0,
+					/*ppr_options*/0,
+					AHD_TRANS_CUR|AHD_TRANS_GOAL,
+					/*paused*/TRUE);
+			if (scb != NULL) {
+				/*
+				 * Remove any SCBs in the waiting for
+				 * selection queue that may also be for
+				 * this target so that command ordering
+				 * is preserved.
+				 */
+				ahd_freeze_devq(ahd, scb);
+				ahd_qinfifo_requeue_tail(ahd, scb);
+			}
+			printerror = 0;
+		} else if ((ahd->msg_flags & MSG_FLAG_EXPECT_IDE_BUSFREE) != 0
+			&& ahd_sent_msg(ahd, AHDMSG_1B,
+					 MSG_INITIATOR_DET_ERR, TRUE)) {
+
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+				printk("Expected IDE Busfree\n");
+#endif
+			printerror = 0;
+		} else if ((ahd->msg_flags & MSG_FLAG_EXPECT_QASREJ_BUSFREE)
+			&& ahd_sent_msg(ahd, AHDMSG_1B,
+					MSG_MESSAGE_REJECT, TRUE)) {
+
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+				printk("Expected QAS Reject Busfree\n");
+#endif
+			printerror = 0;
+		}
+	}
+
+	/*
+	 * The busfree required flag is honored at the end of
+	 * the message phases.  We check it last in case we
+	 * had to send some other message that caused a busfree.
+	 */
+	if (scb != NULL && printerror != 0
+	 && (lastphase == P_MESGIN || lastphase == P_MESGOUT)
+	 && ((ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0)) {
+
+		ahd_freeze_devq(ahd, scb);
+		ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+		ahd_freeze_scb(scb);
+		if ((ahd->msg_flags & MSG_FLAG_IU_REQ_CHANGED) != 0) {
+			ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+				       SCB_GET_CHANNEL(ahd, scb),
+				       SCB_GET_LUN(scb), SCB_LIST_NULL,
+				       ROLE_INITIATOR, CAM_REQ_ABORTED);
+		} else {
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+				printk("PPR Negotiation Busfree.\n");
+#endif
+			ahd_done(ahd, scb);
+		}
+		printerror = 0;
+	}
+	if (printerror != 0) {
+		int aborted;
+
+		aborted = 0;
+		if (scb != NULL) {
+			u_int tag;
+
+			if ((scb->hscb->control & TAG_ENB) != 0)
+				tag = SCB_GET_TAG(scb);
+			else
+				tag = SCB_LIST_NULL;
+			ahd_print_path(ahd, scb);
+			aborted = ahd_abort_scbs(ahd, target, 'A',
+				       SCB_GET_LUN(scb), tag,
+				       ROLE_INITIATOR,
+				       CAM_UNEXP_BUSFREE);
+		} else {
+			/*
+			 * We had not fully identified this connection,
+			 * so we cannot abort anything.
+			 */
+			printk("%s: ", ahd_name(ahd));
+		}
+		printk("Unexpected busfree %s, %d SCBs aborted, "
+		       "PRGMCNT == 0x%x\n",
+		       ahd_lookup_phase_entry(lastphase)->phasemsg,
+		       aborted,
+		       ahd_inw(ahd, PRGMCNT));
+		ahd_dump_card_state(ahd);
+		if (lastphase != P_BUSFREE)
+			ahd_force_renegotiation(ahd, &devinfo);
+	}
+	/* Always restart the sequencer. */
+	return (1);
+}
+
+static void
+ahd_handle_proto_violation(struct ahd_softc *ahd)
+{
+	struct	ahd_devinfo devinfo;
+	struct	scb *scb;
+	u_int	scbid;
+	u_int	seq_flags;
+	u_int	curphase;
+	u_int	lastphase;
+	int	found;
+
+	ahd_fetch_devinfo(ahd, &devinfo);
+	scbid = ahd_get_scbptr(ahd);
+	scb = ahd_lookup_scb(ahd, scbid);
+	seq_flags = ahd_inb(ahd, SEQ_FLAGS);
+	curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+	lastphase = ahd_inb(ahd, LASTPHASE);
+	if ((seq_flags & NOT_IDENTIFIED) != 0) {
+
+		/*
+		 * The reconnecting target either did not send an
+		 * identify message, or did, but we didn't find an SCB
+		 * to match.
+		 */
+		ahd_print_devinfo(ahd, &devinfo);
+		printk("Target did not send an IDENTIFY message. "
+		       "LASTPHASE = 0x%x.\n", lastphase);
+		scb = NULL;
+	} else if (scb == NULL) {
+		/*
+		 * We don't seem to have an SCB active for this
+		 * transaction.  Print an error and reset the bus.
+		 */
+		ahd_print_devinfo(ahd, &devinfo);
+		printk("No SCB found during protocol violation\n");
+		goto proto_violation_reset;
+	} else {
+		ahd_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
+		if ((seq_flags & NO_CDB_SENT) != 0) {
+			ahd_print_path(ahd, scb);
+			printk("No or incomplete CDB sent to device.\n");
+		} else if ((ahd_inb_scbram(ahd, SCB_CONTROL)
+			  & STATUS_RCVD) == 0) {
+			/*
+			 * The target never bothered to provide status to
+			 * us prior to completing the command.  Since we don't
+			 * know the disposition of this command, we must attempt
+			 * to abort it.  Assert ATN and prepare to send an abort
+			 * message.
+			 */
+			ahd_print_path(ahd, scb);
+			printk("Completed command without status.\n");
+		} else {
+			ahd_print_path(ahd, scb);
+			printk("Unknown protocol violation.\n");
+			ahd_dump_card_state(ahd);
+		}
+	}
+	if ((lastphase & ~P_DATAIN_DT) == 0
+	 || lastphase == P_COMMAND) {
+proto_violation_reset:
+		/*
+		 * Target either went directly to data
+		 * phase or didn't respond to our ATN.
+		 * The only safe thing to do is to blow
+		 * it away with a bus reset.
+		 */
+		found = ahd_reset_channel(ahd, 'A', TRUE);
+		printk("%s: Issued Channel %c Bus Reset. "
+		       "%d SCBs aborted\n", ahd_name(ahd), 'A', found);
+	} else {
+		/*
+		 * Leave the selection hardware off in case
+		 * this abort attempt will affect yet to
+		 * be sent commands.
+		 */
+		ahd_outb(ahd, SCSISEQ0,
+			 ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+		ahd_assert_atn(ahd);
+		ahd_outb(ahd, MSG_OUT, HOST_MSG);
+		if (scb == NULL) {
+			ahd_print_devinfo(ahd, &devinfo);
+			ahd->msgout_buf[0] = MSG_ABORT_TASK;
+			ahd->msgout_len = 1;
+			ahd->msgout_index = 0;
+			ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+		} else {
+			ahd_print_path(ahd, scb);
+			scb->flags |= SCB_ABORT;
+		}
+		printk("Protocol violation %s.  Attempting to abort.\n",
+		       ahd_lookup_phase_entry(curphase)->phasemsg);
+	}
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahd_force_renegotiation(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+	struct	ahd_initiator_tinfo *targ_info;
+	struct	ahd_tmode_tstate *tstate;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+		ahd_print_devinfo(ahd, devinfo);
+		printk("Forcing renegotiation\n");
+	}
+#endif
+	targ_info = ahd_fetch_transinfo(ahd,
+					devinfo->channel,
+					devinfo->our_scsiid,
+					devinfo->target,
+					&tstate);
+	ahd_update_neg_request(ahd, devinfo, tstate,
+			       targ_info, AHD_NEG_IF_NON_ASYNC);
+}
+
+#define AHD_MAX_STEPS 2000
+static void
+ahd_clear_critical_section(struct ahd_softc *ahd)
+{
+	ahd_mode_state	saved_modes;
+	int		stepping;
+	int		steps;
+	int		first_instr;
+	u_int		simode0;
+	u_int		simode1;
+	u_int		simode3;
+	u_int		lqimode0;
+	u_int		lqimode1;
+	u_int		lqomode0;
+	u_int		lqomode1;
+
+	if (ahd->num_critical_sections == 0)
+		return;
+
+	stepping = FALSE;
+	steps = 0;
+	first_instr = 0;
+	simode0 = 0;
+	simode1 = 0;
+	simode3 = 0;
+	lqimode0 = 0;
+	lqimode1 = 0;
+	lqomode0 = 0;
+	lqomode1 = 0;
+	saved_modes = ahd_save_modes(ahd);
+	for (;;) {
+		struct	cs *cs;
+		u_int	seqaddr;
+		u_int	i;
+
+		ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+		seqaddr = ahd_inw(ahd, CURADDR);
+
+		cs = ahd->critical_sections;
+		for (i = 0; i < ahd->num_critical_sections; i++, cs++) {
+			
+			if (cs->begin < seqaddr && cs->end >= seqaddr)
+				break;
+		}
+
+		if (i == ahd->num_critical_sections)
+			break;
+
+		if (steps > AHD_MAX_STEPS) {
+			printk("%s: Infinite loop in critical section\n"
+			       "%s: First Instruction 0x%x now 0x%x\n",
+			       ahd_name(ahd), ahd_name(ahd), first_instr,
+			       seqaddr);
+			ahd_dump_card_state(ahd);
+			panic("critical section loop");
+		}
+
+		steps++;
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MISC) != 0)
+			printk("%s: Single stepping at 0x%x\n", ahd_name(ahd),
+			       seqaddr);
+#endif
+		if (stepping == FALSE) {
+
+			first_instr = seqaddr;
+  			ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+  			simode0 = ahd_inb(ahd, SIMODE0);
+			simode3 = ahd_inb(ahd, SIMODE3);
+			lqimode0 = ahd_inb(ahd, LQIMODE0);
+			lqimode1 = ahd_inb(ahd, LQIMODE1);
+			lqomode0 = ahd_inb(ahd, LQOMODE0);
+			lqomode1 = ahd_inb(ahd, LQOMODE1);
+			ahd_outb(ahd, SIMODE0, 0);
+			ahd_outb(ahd, SIMODE3, 0);
+			ahd_outb(ahd, LQIMODE0, 0);
+			ahd_outb(ahd, LQIMODE1, 0);
+			ahd_outb(ahd, LQOMODE0, 0);
+			ahd_outb(ahd, LQOMODE1, 0);
+			ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+			simode1 = ahd_inb(ahd, SIMODE1);
+			/*
+			 * We don't clear ENBUSFREE.  Unfortunately
+			 * we cannot re-enable busfree detection within
+			 * the current connection, so we must leave it
+			 * on while single stepping.
+			 */
+			ahd_outb(ahd, SIMODE1, simode1 & ENBUSFREE);
+			ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) | STEP);
+			stepping = TRUE;
+		}
+		ahd_outb(ahd, CLRSINT1, CLRBUSFREE);
+		ahd_outb(ahd, CLRINT, CLRSCSIINT);
+		ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+		ahd_outb(ahd, HCNTRL, ahd->unpause);
+		while (!ahd_is_paused(ahd))
+			ahd_delay(200);
+		ahd_update_modes(ahd);
+	}
+	if (stepping) {
+		ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+		ahd_outb(ahd, SIMODE0, simode0);
+		ahd_outb(ahd, SIMODE3, simode3);
+		ahd_outb(ahd, LQIMODE0, lqimode0);
+		ahd_outb(ahd, LQIMODE1, lqimode1);
+		ahd_outb(ahd, LQOMODE0, lqomode0);
+		ahd_outb(ahd, LQOMODE1, lqomode1);
+		ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+		ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) & ~STEP);
+  		ahd_outb(ahd, SIMODE1, simode1);
+		/*
+		 * SCSIINT seems to glitch occasionally when
+		 * the interrupt masks are restored.  Clear SCSIINT
+		 * one more time so that only persistent errors
+		 * are seen as a real interrupt.
+		 */
+		ahd_outb(ahd, CLRINT, CLRSCSIINT);
+	}
+	ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+static void
+ahd_clear_intstat(struct ahd_softc *ahd)
+{
+	AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+			 ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+	/* Clear any interrupt conditions this may have caused */
+	ahd_outb(ahd, CLRLQIINT0, CLRLQIATNQAS|CLRLQICRCT1|CLRLQICRCT2
+				 |CLRLQIBADLQT|CLRLQIATNLQ|CLRLQIATNCMD);
+	ahd_outb(ahd, CLRLQIINT1, CLRLQIPHASE_LQ|CLRLQIPHASE_NLQ|CLRLIQABORT
+				 |CLRLQICRCI_LQ|CLRLQICRCI_NLQ|CLRLQIBADLQI
+				 |CLRLQIOVERI_LQ|CLRLQIOVERI_NLQ|CLRNONPACKREQ);
+	ahd_outb(ahd, CLRLQOINT0, CLRLQOTARGSCBPERR|CLRLQOSTOPT2|CLRLQOATNLQ
+				 |CLRLQOATNPKT|CLRLQOTCRC);
+	ahd_outb(ahd, CLRLQOINT1, CLRLQOINITSCBPERR|CLRLQOSTOPI2|CLRLQOBADQAS
+				 |CLRLQOBUSFREE|CLRLQOPHACHGINPKT);
+	if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+		ahd_outb(ahd, CLRLQOINT0, 0);
+		ahd_outb(ahd, CLRLQOINT1, 0);
+	}
+	ahd_outb(ahd, CLRSINT3, CLRNTRAMPERR|CLROSRAMPERR);
+	ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+				|CLRBUSFREE|CLRSCSIPERR|CLRREQINIT);
+	ahd_outb(ahd, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO
+			        |CLRIOERR|CLROVERRUN);
+	ahd_outb(ahd, CLRINT, CLRSCSIINT);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHD_DEBUG
+uint32_t ahd_debug = AHD_DEBUG_OPTS;
+#endif
+
+#if 0
+void
+ahd_print_scb(struct scb *scb)
+{
+	struct hardware_scb *hscb;
+	int i;
+
+	hscb = scb->hscb;
+	printk("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+	       (void *)scb,
+	       hscb->control,
+	       hscb->scsiid,
+	       hscb->lun,
+	       hscb->cdb_len);
+	printk("Shared Data: ");
+	for (i = 0; i < sizeof(hscb->shared_data.idata.cdb); i++)
+		printk("%#02x", hscb->shared_data.idata.cdb[i]);
+	printk("        dataptr:%#x%x datacnt:%#x sgptr:%#x tag:%#x\n",
+	       (uint32_t)((ahd_le64toh(hscb->dataptr) >> 32) & 0xFFFFFFFF),
+	       (uint32_t)(ahd_le64toh(hscb->dataptr) & 0xFFFFFFFF),
+	       ahd_le32toh(hscb->datacnt),
+	       ahd_le32toh(hscb->sgptr),
+	       SCB_GET_TAG(scb));
+	ahd_dump_sglist(scb);
+}
+#endif  /*  0  */
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahd_tmode_tstate *
+ahd_alloc_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel)
+{
+	struct ahd_tmode_tstate *master_tstate;
+	struct ahd_tmode_tstate *tstate;
+	int i;
+
+	master_tstate = ahd->enabled_targets[ahd->our_id];
+	if (ahd->enabled_targets[scsi_id] != NULL
+	 && ahd->enabled_targets[scsi_id] != master_tstate)
+		panic("%s: ahd_alloc_tstate - Target already allocated",
+		      ahd_name(ahd));
+	tstate = kmalloc(sizeof(*tstate), GFP_ATOMIC);
+	if (tstate == NULL)
+		return (NULL);
+
+	/*
+	 * If we have allocated a master tstate, copy user settings from
+	 * the master tstate (taken from SRAM or the EEPROM) for this
+	 * channel, but reset our current and goal settings to async/narrow
+	 * until an initiator talks to us.
+	 */
+	if (master_tstate != NULL) {
+		memcpy(tstate, master_tstate, sizeof(*tstate));
+		memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+		for (i = 0; i < 16; i++) {
+			memset(&tstate->transinfo[i].curr, 0,
+			      sizeof(tstate->transinfo[i].curr));
+			memset(&tstate->transinfo[i].goal, 0,
+			      sizeof(tstate->transinfo[i].goal));
+		}
+	} else
+		memset(tstate, 0, sizeof(*tstate));
+	ahd->enabled_targets[scsi_id] = tstate;
+	return (tstate);
+}
+
+#ifdef AHD_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahd_free_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel, int force)
+{
+	struct ahd_tmode_tstate *tstate;
+
+	/*
+	 * Don't clean up our "master" tstate.
+	 * It has our default user settings.
+	 */
+	if (scsi_id == ahd->our_id
+	 && force == FALSE)
+		return;
+
+	tstate = ahd->enabled_targets[scsi_id];
+	if (tstate != NULL)
+		kfree(tstate);
+	ahd->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest period to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+static void
+ahd_devlimited_syncrate(struct ahd_softc *ahd,
+			struct ahd_initiator_tinfo *tinfo,
+			u_int *period, u_int *ppr_options, role_t role)
+{
+	struct	ahd_transinfo *transinfo;
+	u_int	maxsync;
+
+	if ((ahd_inb(ahd, SBLKCTL) & ENAB40) != 0
+	 && (ahd_inb(ahd, SSTAT2) & EXP_ACTIVE) == 0) {
+		maxsync = AHD_SYNCRATE_PACED;
+	} else {
+		maxsync = AHD_SYNCRATE_ULTRA;
+		/* Can't do DT related options on an SE bus */
+		*ppr_options &= MSG_EXT_PPR_QAS_REQ;
+	}
+	/*
+	 * Never allow a value higher than our current goal
+	 * period otherwise we may allow a target initiated
+	 * negotiation to go above the limit as set by the
+	 * user.  In the case of an initiator initiated
+	 * sync negotiation, we limit based on the user
+	 * setting.  This allows the system to still accept
+	 * incoming negotiations even if target initiated
+	 * negotiation is not performed.
+	 */
+	if (role == ROLE_TARGET)
+		transinfo = &tinfo->user;
+	else 
+		transinfo = &tinfo->goal;
+	*ppr_options &= (transinfo->ppr_options|MSG_EXT_PPR_PCOMP_EN);
+	if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
+		maxsync = max(maxsync, (u_int)AHD_SYNCRATE_ULTRA2);
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+	}
+	if (transinfo->period == 0) {
+		*period = 0;
+		*ppr_options = 0;
+	} else {
+		*period = max(*period, (u_int)transinfo->period);
+		ahd_find_syncrate(ahd, period, ppr_options, maxsync);
+	}
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+void
+ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
+		  u_int *ppr_options, u_int maxsync)
+{
+	if (*period < maxsync)
+		*period = maxsync;
+
+	if ((*ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+	 && *period > AHD_SYNCRATE_MIN_DT)
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+		
+	if (*period > AHD_SYNCRATE_MIN)
+		*period = 0;
+
+	/* Honor PPR option conformance rules. */
+	if (*period > AHD_SYNCRATE_PACED)
+		*ppr_options &= ~MSG_EXT_PPR_RTI;
+
+	if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
+		*ppr_options &= (MSG_EXT_PPR_DT_REQ|MSG_EXT_PPR_QAS_REQ);
+
+	if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0)
+		*ppr_options &= MSG_EXT_PPR_QAS_REQ;
+
+	/* Skip all PACED only entries if IU is not available */
+	if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0
+	 && *period < AHD_SYNCRATE_DT)
+		*period = AHD_SYNCRATE_DT;
+
+	/* Skip all DT only entries if DT is not available */
+	if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+	 && *period < AHD_SYNCRATE_ULTRA2)
+		*period = AHD_SYNCRATE_ULTRA2;
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+static void
+ahd_validate_offset(struct ahd_softc *ahd,
+		    struct ahd_initiator_tinfo *tinfo,
+		    u_int period, u_int *offset, int wide,
+		    role_t role)
+{
+	u_int maxoffset;
+
+	/* Limit offset to what we can do */
+	if (period == 0)
+		maxoffset = 0;
+	else if (period <= AHD_SYNCRATE_PACED) {
+		if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0)
+			maxoffset = MAX_OFFSET_PACED_BUG;
+		else
+			maxoffset = MAX_OFFSET_PACED;
+	} else
+		maxoffset = MAX_OFFSET_NON_PACED;
+	*offset = min(*offset, maxoffset);
+	if (tinfo != NULL) {
+		if (role == ROLE_TARGET)
+			*offset = min(*offset, (u_int)tinfo->user.offset);
+		else
+			*offset = min(*offset, (u_int)tinfo->goal.offset);
+	}
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+static void
+ahd_validate_width(struct ahd_softc *ahd, struct ahd_initiator_tinfo *tinfo,
+		   u_int *bus_width, role_t role)
+{
+	switch (*bus_width) {
+	default:
+		if (ahd->features & AHD_WIDE) {
+			/* Respond Wide */
+			*bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+			break;
+		}
+		/* FALLTHROUGH */
+	case MSG_EXT_WDTR_BUS_8_BIT:
+		*bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+		break;
+	}
+	if (tinfo != NULL) {
+		if (role == ROLE_TARGET)
+			*bus_width = min((u_int)tinfo->user.width, *bus_width);
+		else
+			*bus_width = min((u_int)tinfo->goal.width, *bus_width);
+	}
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient opportunity.  This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahd_update_neg_request(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		       struct ahd_tmode_tstate *tstate,
+		       struct ahd_initiator_tinfo *tinfo, ahd_neg_type neg_type)
+{
+	u_int auto_negotiate_orig;
+
+	auto_negotiate_orig = tstate->auto_negotiate;
+	if (neg_type == AHD_NEG_ALWAYS) {
+		/*
+		 * Force our "current" settings to be
+		 * unknown so that unless a bus reset
+		 * occurs the need to renegotiate is
+		 * recorded persistently.
+		 */
+		if ((ahd->features & AHD_WIDE) != 0)
+			tinfo->curr.width = AHD_WIDTH_UNKNOWN;
+		tinfo->curr.period = AHD_PERIOD_UNKNOWN;
+		tinfo->curr.offset = AHD_OFFSET_UNKNOWN;
+	}
+	if (tinfo->curr.period != tinfo->goal.period
+	 || tinfo->curr.width != tinfo->goal.width
+	 || tinfo->curr.offset != tinfo->goal.offset
+	 || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+	 || (neg_type == AHD_NEG_IF_NON_ASYNC
+	  && (tinfo->goal.offset != 0
+	   || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+	   || tinfo->goal.ppr_options != 0)))
+		tstate->auto_negotiate |= devinfo->target_mask;
+	else
+		tstate->auto_negotiate &= ~devinfo->target_mask;
+
+	return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahd_set_syncrate(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		 u_int period, u_int offset, u_int ppr_options,
+		 u_int type, int paused)
+{
+	struct	ahd_initiator_tinfo *tinfo;
+	struct	ahd_tmode_tstate *tstate;
+	u_int	old_period;
+	u_int	old_offset;
+	u_int	old_ppr;
+	int	active;
+	int	update_needed;
+
+	active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
+	update_needed = 0;
+
+	if (period == 0 || offset == 0) {
+		period = 0;
+		offset = 0;
+	}
+
+	tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	if ((type & AHD_TRANS_USER) != 0) {
+		tinfo->user.period = period;
+		tinfo->user.offset = offset;
+		tinfo->user.ppr_options = ppr_options;
+	}
+
+	if ((type & AHD_TRANS_GOAL) != 0) {
+		tinfo->goal.period = period;
+		tinfo->goal.offset = offset;
+		tinfo->goal.ppr_options = ppr_options;
+	}
+
+	old_period = tinfo->curr.period;
+	old_offset = tinfo->curr.offset;
+	old_ppr	   = tinfo->curr.ppr_options;
+
+	if ((type & AHD_TRANS_CUR) != 0
+	 && (old_period != period
+	  || old_offset != offset
+	  || old_ppr != ppr_options)) {
+
+		update_needed++;
+
+		tinfo->curr.period = period;
+		tinfo->curr.offset = offset;
+		tinfo->curr.ppr_options = ppr_options;
+
+		ahd_send_async(ahd, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG);
+		if (bootverbose) {
+			if (offset != 0) {
+				int options;
+
+				printk("%s: target %d synchronous with "
+				       "period = 0x%x, offset = 0x%x",
+				       ahd_name(ahd), devinfo->target,
+				       period, offset);
+				options = 0;
+				if ((ppr_options & MSG_EXT_PPR_RD_STRM) != 0) {
+					printk("(RDSTRM");
+					options++;
+				}
+				if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+					printk("%s", options ? "|DT" : "(DT");
+					options++;
+				}
+				if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+					printk("%s", options ? "|IU" : "(IU");
+					options++;
+				}
+				if ((ppr_options & MSG_EXT_PPR_RTI) != 0) {
+					printk("%s", options ? "|RTI" : "(RTI");
+					options++;
+				}
+				if ((ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) {
+					printk("%s", options ? "|QAS" : "(QAS");
+					options++;
+				}
+				if (options != 0)
+					printk(")\n");
+				else
+					printk("\n");
+			} else {
+				printk("%s: target %d using "
+				       "asynchronous transfers%s\n",
+				       ahd_name(ahd), devinfo->target,
+				       (ppr_options & MSG_EXT_PPR_QAS_REQ) != 0
+				     ?  "(QAS)" : "");
+			}
+		}
+	}
+	/*
+	 * Always refresh the neg-table to handle the case of the
+	 * sequencer setting the ENATNO bit for a MK_MESSAGE request.
+	 * We will always renegotiate in that case if this is a
+	 * packetized request.  Also manage the busfree expected flag
+	 * from this common routine so that we catch changes due to
+	 * WDTR or SDTR messages.
+	 */
+	if ((type & AHD_TRANS_CUR) != 0) {
+		if (!paused)
+			ahd_pause(ahd);
+		ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+		if (!paused)
+			ahd_unpause(ahd);
+		if (ahd->msg_type != MSG_TYPE_NONE) {
+			if ((old_ppr & MSG_EXT_PPR_IU_REQ)
+			 != (ppr_options & MSG_EXT_PPR_IU_REQ)) {
+#ifdef AHD_DEBUG
+				if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+					ahd_print_devinfo(ahd, devinfo);
+					printk("Expecting IU Change busfree\n");
+				}
+#endif
+				ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
+					       |  MSG_FLAG_IU_REQ_CHANGED;
+			}
+			if ((old_ppr & MSG_EXT_PPR_IU_REQ) != 0) {
+#ifdef AHD_DEBUG
+				if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+					printk("PPR with IU_REQ outstanding\n");
+#endif
+				ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE;
+			}
+		}
+	}
+
+	update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+						tinfo, AHD_NEG_TO_GOAL);
+
+	if (update_needed && active)
+		ahd_update_pending_scbs(ahd);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahd_set_width(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+	      u_int width, u_int type, int paused)
+{
+	struct	ahd_initiator_tinfo *tinfo;
+	struct	ahd_tmode_tstate *tstate;
+	u_int	oldwidth;
+	int	active;
+	int	update_needed;
+
+	active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
+	update_needed = 0;
+	tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	if ((type & AHD_TRANS_USER) != 0)
+		tinfo->user.width = width;
+
+	if ((type & AHD_TRANS_GOAL) != 0)
+		tinfo->goal.width = width;
+
+	oldwidth = tinfo->curr.width;
+	if ((type & AHD_TRANS_CUR) != 0 && oldwidth != width) {
+
+		update_needed++;
+
+		tinfo->curr.width = width;
+		ahd_send_async(ahd, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG);
+		if (bootverbose) {
+			printk("%s: target %d using %dbit transfers\n",
+			       ahd_name(ahd), devinfo->target,
+			       8 * (0x01 << width));
+		}
+	}
+
+	if ((type & AHD_TRANS_CUR) != 0) {
+		if (!paused)
+			ahd_pause(ahd);
+		ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+		if (!paused)
+			ahd_unpause(ahd);
+	}
+
+	update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+						tinfo, AHD_NEG_TO_GOAL);
+	if (update_needed && active)
+		ahd_update_pending_scbs(ahd);
+
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+static void
+ahd_set_tags(struct ahd_softc *ahd, struct scsi_cmnd *cmd,
+	     struct ahd_devinfo *devinfo, ahd_queue_alg alg)
+{
+	struct scsi_device *sdev = cmd->device;
+
+	ahd_platform_set_tags(ahd, sdev, devinfo, alg);
+	ahd_send_async(ahd, devinfo->channel, devinfo->target,
+		       devinfo->lun, AC_TRANSFER_NEG);
+}
+
+static void
+ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		     struct ahd_transinfo *tinfo)
+{
+	ahd_mode_state	saved_modes;
+	u_int		period;
+	u_int		ppr_opts;
+	u_int		con_opts;
+	u_int		offset;
+	u_int		saved_negoaddr;
+	uint8_t		iocell_opts[sizeof(ahd->iocell_opts)];
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+	saved_negoaddr = ahd_inb(ahd, NEGOADDR);
+	ahd_outb(ahd, NEGOADDR, devinfo->target);
+	period = tinfo->period;
+	offset = tinfo->offset;
+	memcpy(iocell_opts, ahd->iocell_opts, sizeof(ahd->iocell_opts)); 
+	ppr_opts = tinfo->ppr_options & (MSG_EXT_PPR_QAS_REQ|MSG_EXT_PPR_DT_REQ
+					|MSG_EXT_PPR_IU_REQ|MSG_EXT_PPR_RTI);
+	con_opts = 0;
+	if (period == 0)
+		period = AHD_SYNCRATE_ASYNC;
+	if (period == AHD_SYNCRATE_160) {
+
+		if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0) {
+			/*
+			 * When the SPI4 spec was finalized, PACE transfers
+			 * was not made a configurable option in the PPR
+			 * message.  Instead it is assumed to be enabled for
+			 * any syncrate faster than 80MHz.  Nevertheless,
+			 * Harpoon2A4 allows this to be configurable.
+			 *
+			 * Harpoon2A4 also assumes at most 2 data bytes per
+			 * negotiated REQ/ACK offset.  Paced transfers take
+			 * 4, so we must adjust our offset.
+			 */
+			ppr_opts |= PPROPT_PACE;
+			offset *= 2;
+
+			/*
+			 * Harpoon2A assumed that there would be a
+			 * fallback rate between 160MHz and 80MHz,
+			 * so 7 is used as the period factor rather
+			 * than 8 for 160MHz.
+			 */
+			period = AHD_SYNCRATE_REVA_160;
+		}
+		if ((tinfo->ppr_options & MSG_EXT_PPR_PCOMP_EN) == 0)
+			iocell_opts[AHD_PRECOMP_SLEW_INDEX] &=
+			    ~AHD_PRECOMP_MASK;
+	} else {
+		/*
+		 * Precomp should be disabled for non-paced transfers.
+		 */
+		iocell_opts[AHD_PRECOMP_SLEW_INDEX] &= ~AHD_PRECOMP_MASK;
+
+		if ((ahd->features & AHD_NEW_IOCELL_OPTS) != 0
+		 && (ppr_opts & MSG_EXT_PPR_DT_REQ) != 0
+		 && (ppr_opts & MSG_EXT_PPR_IU_REQ) == 0) {
+			/*
+			 * Slow down our CRC interval to be
+			 * compatible with non-packetized
+			 * U160 devices that can't handle a
+			 * CRC at full speed.
+			 */
+			con_opts |= ENSLOWCRC;
+		}
+
+		if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0) {
+			/*
+			 * On H2A4, revert to a slower slewrate
+			 * on non-paced transfers.
+			 */
+			iocell_opts[AHD_PRECOMP_SLEW_INDEX] &=
+			    ~AHD_SLEWRATE_MASK;
+		}
+	}
+
+	ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PRECOMP_SLEW);
+	ahd_outb(ahd, ANNEXDAT, iocell_opts[AHD_PRECOMP_SLEW_INDEX]);
+	ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_AMPLITUDE);
+	ahd_outb(ahd, ANNEXDAT, iocell_opts[AHD_AMPLITUDE_INDEX]);
+
+	ahd_outb(ahd, NEGPERIOD, period);
+	ahd_outb(ahd, NEGPPROPTS, ppr_opts);
+	ahd_outb(ahd, NEGOFFSET, offset);
+
+	if (tinfo->width == MSG_EXT_WDTR_BUS_16_BIT)
+		con_opts |= WIDEXFER;
+
+	/*
+	 * Slow down our CRC interval to be
+	 * compatible with packetized U320 devices
+	 * that can't handle a CRC at full speed
+	 */
+	if (ahd->features & AHD_AIC79XXB_SLOWCRC) {
+		con_opts |= ENSLOWCRC;
+	}
+
+	/*
+	 * During packetized transfers, the target will
+	 * give us the opportunity to send command packets
+	 * without us asserting attention.
+	 */
+	if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
+		con_opts |= ENAUTOATNO;
+	ahd_outb(ahd, NEGCONOPTS, con_opts);
+	ahd_outb(ahd, NEGOADDR, saved_negoaddr);
+	ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * When the transfer settings for a connection change, setup for
+ * negotiation in pending SCBs to effect the change as quickly as
+ * possible.  We also cancel any negotiations that are scheduled
+ * for inflight SCBs that have not been started yet.
+ */
+static void
+ahd_update_pending_scbs(struct ahd_softc *ahd)
+{
+	struct		scb *pending_scb;
+	int		pending_scb_count;
+	int		paused;
+	u_int		saved_scbptr;
+	ahd_mode_state	saved_modes;
+
+	/*
+	 * Traverse the pending SCB list and ensure that all of the
+	 * SCBs there have the proper settings.  We can only safely
+	 * clear the negotiation required flag (setting requires the
+	 * execution queue to be modified) and this is only possible
+	 * if we are not already attempting to select out for this
+	 * SCB.  For this reason, all callers only call this routine
+	 * if we are changing the negotiation settings for the currently
+	 * active transaction on the bus.
+	 */
+	pending_scb_count = 0;
+	LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+		struct ahd_devinfo devinfo;
+		struct ahd_initiator_tinfo *tinfo;
+		struct ahd_tmode_tstate *tstate;
+
+		ahd_scb_devinfo(ahd, &devinfo, pending_scb);
+		tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+					    devinfo.our_scsiid,
+					    devinfo.target, &tstate);
+		if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+		 && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+			pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+			pending_scb->hscb->control &= ~MK_MESSAGE;
+		}
+		ahd_sync_scb(ahd, pending_scb,
+			     BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+		pending_scb_count++;
+	}
+
+	if (pending_scb_count == 0)
+		return;
+
+	if (ahd_is_paused(ahd)) {
+		paused = 1;
+	} else {
+		paused = 0;
+		ahd_pause(ahd);
+	}
+
+	/*
+	 * Force the sequencer to reinitialize the selection for
+	 * the command at the head of the execution queue if it
+	 * has already been setup.  The negotiation changes may
+	 * effect whether we select-out with ATN.  It is only
+	 * safe to clear ENSELO when the bus is not free and no
+	 * selection is in progres or completed.
+	 */
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	if ((ahd_inb(ahd, SCSISIGI) & BSYI) != 0
+	 && (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) == 0)
+		ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+	saved_scbptr = ahd_get_scbptr(ahd);
+	/* Ensure that the hscbs down on the card match the new information */
+	LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+		u_int	scb_tag;
+		u_int	control;
+
+		scb_tag = SCB_GET_TAG(pending_scb);
+		ahd_set_scbptr(ahd, scb_tag);
+		control = ahd_inb_scbram(ahd, SCB_CONTROL);
+		control &= ~MK_MESSAGE;
+		control |= pending_scb->hscb->control & MK_MESSAGE;
+		ahd_outb(ahd, SCB_CONTROL, control);
+	}
+	ahd_set_scbptr(ahd, saved_scbptr);
+	ahd_restore_modes(ahd, saved_modes);
+
+	if (paused == 0)
+		ahd_unpause(ahd);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahd_fetch_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+	ahd_mode_state	saved_modes;
+	u_int		saved_scsiid;
+	role_t		role;
+	int		our_id;
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+	if (ahd_inb(ahd, SSTAT0) & TARGET)
+		role = ROLE_TARGET;
+	else
+		role = ROLE_INITIATOR;
+
+	if (role == ROLE_TARGET
+	 && (ahd_inb(ahd, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+		/* We were selected, so pull our id from TARGIDIN */
+		our_id = ahd_inb(ahd, TARGIDIN) & OID;
+	} else if (role == ROLE_TARGET)
+		our_id = ahd_inb(ahd, TOWNID);
+	else
+		our_id = ahd_inb(ahd, IOWNID);
+
+	saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+	ahd_compile_devinfo(devinfo,
+			    our_id,
+			    SCSIID_TARGET(ahd, saved_scsiid),
+			    ahd_inb(ahd, SAVED_LUN),
+			    SCSIID_CHANNEL(ahd, saved_scsiid),
+			    role);
+	ahd_restore_modes(ahd, saved_modes);
+}
+
+void
+ahd_print_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+	printk("%s:%c:%d:%d: ", ahd_name(ahd), 'A',
+	       devinfo->target, devinfo->lun);
+}
+
+static const struct ahd_phase_table_entry*
+ahd_lookup_phase_entry(int phase)
+{
+	const struct ahd_phase_table_entry *entry;
+	const struct ahd_phase_table_entry *last_entry;
+
+	/*
+	 * num_phases doesn't include the default entry which
+	 * will be returned if the phase doesn't match.
+	 */
+	last_entry = &ahd_phase_table[num_phases];
+	for (entry = ahd_phase_table; entry < last_entry; entry++) {
+		if (phase == entry->phase)
+			break;
+	}
+	return (entry);
+}
+
+void
+ahd_compile_devinfo(struct ahd_devinfo *devinfo, u_int our_id, u_int target,
+		    u_int lun, char channel, role_t role)
+{
+	devinfo->our_scsiid = our_id;
+	devinfo->target = target;
+	devinfo->lun = lun;
+	devinfo->target_offset = target;
+	devinfo->channel = channel;
+	devinfo->role = role;
+	if (channel == 'B')
+		devinfo->target_offset += 8;
+	devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+static void
+ahd_scb_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		struct scb *scb)
+{
+	role_t	role;
+	int	our_id;
+
+	our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+	role = ROLE_INITIATOR;
+	if ((scb->hscb->control & TARGET_SCB) != 0)
+		role = ROLE_TARGET;
+	ahd_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahd, scb),
+			    SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahd, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted.  Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+			   struct scb *scb)
+{
+	/*
+	 * To facilitate adding multiple messages together,
+	 * each routine should increment the index and len
+	 * variables instead of setting them explicitly.
+	 */
+	ahd->msgout_index = 0;
+	ahd->msgout_len = 0;
+
+	if (ahd_currently_packetized(ahd))
+		ahd->msg_flags |= MSG_FLAG_PACKETIZED;
+
+	if (ahd->send_msg_perror
+	 && ahd_inb(ahd, MSG_OUT) == HOST_MSG) {
+		ahd->msgout_buf[ahd->msgout_index++] = ahd->send_msg_perror;
+		ahd->msgout_len++;
+		ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+			printk("Setting up for Parity Error delivery\n");
+#endif
+		return;
+	} else if (scb == NULL) {
+		printk("%s: WARNING. No pending message for "
+		       "I_T msgin.  Issuing NO-OP\n", ahd_name(ahd));
+		ahd->msgout_buf[ahd->msgout_index++] = MSG_NOOP;
+		ahd->msgout_len++;
+		ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+		return;
+	}
+
+	if ((scb->flags & SCB_DEVICE_RESET) == 0
+	 && (scb->flags & SCB_PACKETIZED) == 0
+	 && ahd_inb(ahd, MSG_OUT) == MSG_IDENTIFYFLAG) {
+		u_int identify_msg;
+
+		identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+		if ((scb->hscb->control & DISCENB) != 0)
+			identify_msg |= MSG_IDENTIFY_DISCFLAG;
+		ahd->msgout_buf[ahd->msgout_index++] = identify_msg;
+		ahd->msgout_len++;
+
+		if ((scb->hscb->control & TAG_ENB) != 0) {
+			ahd->msgout_buf[ahd->msgout_index++] =
+			    scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+			ahd->msgout_buf[ahd->msgout_index++] = SCB_GET_TAG(scb);
+			ahd->msgout_len += 2;
+		}
+	}
+
+	if (scb->flags & SCB_DEVICE_RESET) {
+		ahd->msgout_buf[ahd->msgout_index++] = MSG_BUS_DEV_RESET;
+		ahd->msgout_len++;
+		ahd_print_path(ahd, scb);
+		printk("Bus Device Reset Message Sent\n");
+		/*
+		 * Clear our selection hardware in advance of
+		 * the busfree.  We may have an entry in the waiting
+		 * Q for this target, and we don't want to go about
+		 * selecting while we handle the busfree and blow it
+		 * away.
+		 */
+		ahd_outb(ahd, SCSISEQ0, 0);
+	} else if ((scb->flags & SCB_ABORT) != 0) {
+
+		if ((scb->hscb->control & TAG_ENB) != 0) {
+			ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT_TAG;
+		} else {
+			ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT;
+		}
+		ahd->msgout_len++;
+		ahd_print_path(ahd, scb);
+		printk("Abort%s Message Sent\n",
+		       (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+		/*
+		 * Clear our selection hardware in advance of
+		 * the busfree.  We may have an entry in the waiting
+		 * Q for this target, and we don't want to go about
+		 * selecting while we handle the busfree and blow it
+		 * away.
+		 */
+		ahd_outb(ahd, SCSISEQ0, 0);
+	} else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+		ahd_build_transfer_msg(ahd, devinfo);
+		/*
+		 * Clear our selection hardware in advance of potential
+		 * PPR IU status change busfree.  We may have an entry in
+		 * the waiting Q for this target, and we don't want to go
+		 * about selecting while we handle the busfree and blow
+		 * it away.
+		 */
+		ahd_outb(ahd, SCSISEQ0, 0);
+	} else {
+		printk("ahd_intr: AWAITING_MSG for an SCB that "
+		       "does not have a waiting message\n");
+		printk("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+		       devinfo->target_mask);
+		panic("SCB = %d, SCB Control = %x:%x, MSG_OUT = %x "
+		      "SCB flags = %x", SCB_GET_TAG(scb), scb->hscb->control,
+		      ahd_inb_scbram(ahd, SCB_CONTROL), ahd_inb(ahd, MSG_OUT),
+		      scb->flags);
+	}
+
+	/*
+	 * Clear the MK_MESSAGE flag from the SCB so we aren't
+	 * asked to send this message again.
+	 */
+	ahd_outb(ahd, SCB_CONTROL,
+		 ahd_inb_scbram(ahd, SCB_CONTROL) & ~MK_MESSAGE);
+	scb->hscb->control &= ~MK_MESSAGE;
+	ahd->msgout_index = 0;
+	ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahd_build_transfer_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+	/*
+	 * We need to initiate transfer negotiations.
+	 * If our current and goal settings are identical,
+	 * we want to renegotiate due to a check condition.
+	 */
+	struct	ahd_initiator_tinfo *tinfo;
+	struct	ahd_tmode_tstate *tstate;
+	int	dowide;
+	int	dosync;
+	int	doppr;
+	u_int	period;
+	u_int	ppr_options;
+	u_int	offset;
+
+	tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	/*
+	 * Filter our period based on the current connection.
+	 * If we can't perform DT transfers on this segment (not in LVD
+	 * mode for instance), then our decision to issue a PPR message
+	 * may change.
+	 */
+	period = tinfo->goal.period;
+	offset = tinfo->goal.offset;
+	ppr_options = tinfo->goal.ppr_options;
+	/* Target initiated PPR is not allowed in the SCSI spec */
+	if (devinfo->role == ROLE_TARGET)
+		ppr_options = 0;
+	ahd_devlimited_syncrate(ahd, tinfo, &period,
+				&ppr_options, devinfo->role);
+	dowide = tinfo->curr.width != tinfo->goal.width;
+	dosync = tinfo->curr.offset != offset || tinfo->curr.period != period;
+	/*
+	 * Only use PPR if we have options that need it, even if the device
+	 * claims to support it.  There might be an expander in the way
+	 * that doesn't.
+	 */
+	doppr = ppr_options != 0;
+
+	if (!dowide && !dosync && !doppr) {
+		dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+		dosync = tinfo->goal.offset != 0;
+	}
+
+	if (!dowide && !dosync && !doppr) {
+		/*
+		 * Force async with a WDTR message if we have a wide bus,
+		 * or just issue an SDTR with a 0 offset.
+		 */
+		if ((ahd->features & AHD_WIDE) != 0)
+			dowide = 1;
+		else
+			dosync = 1;
+
+		if (bootverbose) {
+			ahd_print_devinfo(ahd, devinfo);
+			printk("Ensuring async\n");
+		}
+	}
+	/* Target initiated PPR is not allowed in the SCSI spec */
+	if (devinfo->role == ROLE_TARGET)
+		doppr = 0;
+
+	/*
+	 * Both the PPR message and SDTR message require the
+	 * goal syncrate to be limited to what the target device
+	 * is capable of handling (based on whether an LVD->SE
+	 * expander is on the bus), so combine these two cases.
+	 * Regardless, guarantee that if we are using WDTR and SDTR
+	 * messages that WDTR comes first.
+	 */
+	if (doppr || (dosync && !dowide)) {
+
+		offset = tinfo->goal.offset;
+		ahd_validate_offset(ahd, tinfo, period, &offset,
+				    doppr ? tinfo->goal.width
+					  : tinfo->curr.width,
+				    devinfo->role);
+		if (doppr) {
+			ahd_construct_ppr(ahd, devinfo, period, offset,
+					  tinfo->goal.width, ppr_options);
+		} else {
+			ahd_construct_sdtr(ahd, devinfo, period, offset);
+		}
+	} else {
+		ahd_construct_wdtr(ahd, devinfo, tinfo->goal.width);
+	}
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_sdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		   u_int period, u_int offset)
+{
+	if (offset == 0)
+		period = AHD_ASYNC_XFER_PERIOD;
+	ahd->msgout_index += spi_populate_sync_msg(
+			ahd->msgout_buf + ahd->msgout_index, period, offset);
+	ahd->msgout_len += 5;
+	if (bootverbose) {
+		printk("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+		       ahd_name(ahd), devinfo->channel, devinfo->target,
+		       devinfo->lun, period, offset);
+	}
+}
+
+/*
+ * Build a wide negotiateion message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_wdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		   u_int bus_width)
+{
+	ahd->msgout_index += spi_populate_width_msg(
+			ahd->msgout_buf + ahd->msgout_index, bus_width);
+	ahd->msgout_len += 4;
+	if (bootverbose) {
+		printk("(%s:%c:%d:%d): Sending WDTR %x\n",
+		       ahd_name(ahd), devinfo->channel, devinfo->target,
+		       devinfo->lun, bus_width);
+	}
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_ppr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		  u_int period, u_int offset, u_int bus_width,
+		  u_int ppr_options)
+{
+	/*
+	 * Always request precompensation from
+	 * the other target if we are running
+	 * at paced syncrates.
+	 */
+	if (period <= AHD_SYNCRATE_PACED)
+		ppr_options |= MSG_EXT_PPR_PCOMP_EN;
+	if (offset == 0)
+		period = AHD_ASYNC_XFER_PERIOD;
+	ahd->msgout_index += spi_populate_ppr_msg(
+			ahd->msgout_buf + ahd->msgout_index, period, offset,
+			bus_width, ppr_options);
+	ahd->msgout_len += 8;
+	if (bootverbose) {
+		printk("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+		       "offset %x, ppr_options %x\n", ahd_name(ahd),
+		       devinfo->channel, devinfo->target, devinfo->lun,
+		       bus_width, period, offset, ppr_options);
+	}
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahd_clear_msg_state(struct ahd_softc *ahd)
+{
+	ahd_mode_state saved_modes;
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	ahd->send_msg_perror = 0;
+	ahd->msg_flags = MSG_FLAG_NONE;
+	ahd->msgout_len = 0;
+	ahd->msgin_index = 0;
+	ahd->msg_type = MSG_TYPE_NONE;
+	if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
+		/*
+		 * The target didn't care to respond to our
+		 * message request, so clear ATN.
+		 */
+		ahd_outb(ahd, CLRSINT1, CLRATNO);
+	}
+	ahd_outb(ahd, MSG_OUT, MSG_NOOP);
+	ahd_outb(ahd, SEQ_FLAGS2,
+		 ahd_inb(ahd, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+	ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahd_handle_message_phase(struct ahd_softc *ahd)
+{
+	struct	ahd_devinfo devinfo;
+	u_int	bus_phase;
+	int	end_session;
+
+	ahd_fetch_devinfo(ahd, &devinfo);
+	end_session = FALSE;
+	bus_phase = ahd_inb(ahd, LASTPHASE);
+
+	if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0) {
+		printk("LQIRETRY for LQIPHASE_OUTPKT\n");
+		ahd_outb(ahd, LQCTL2, LQIRETRY);
+	}
+reswitch:
+	switch (ahd->msg_type) {
+	case MSG_TYPE_INITIATOR_MSGOUT:
+	{
+		int lastbyte;
+		int phasemis;
+		int msgdone;
+
+		if (ahd->msgout_len == 0 && ahd->send_msg_perror == 0)
+			panic("HOST_MSG_LOOP interrupt with no active message");
+
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+			ahd_print_devinfo(ahd, &devinfo);
+			printk("INITIATOR_MSG_OUT");
+		}
+#endif
+		phasemis = bus_phase != P_MESGOUT;
+		if (phasemis) {
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+				printk(" PHASEMIS %s\n",
+				       ahd_lookup_phase_entry(bus_phase)
+							     ->phasemsg);
+			}
+#endif
+			if (bus_phase == P_MESGIN) {
+				/*
+				 * Change gears and see if
+				 * this messages is of interest to
+				 * us or should be passed back to
+				 * the sequencer.
+				 */
+				ahd_outb(ahd, CLRSINT1, CLRATNO);
+				ahd->send_msg_perror = 0;
+				ahd->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+				ahd->msgin_index = 0;
+				goto reswitch;
+			}
+			end_session = TRUE;
+			break;
+		}
+
+		if (ahd->send_msg_perror) {
+			ahd_outb(ahd, CLRSINT1, CLRATNO);
+			ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+				printk(" byte 0x%x\n", ahd->send_msg_perror);
+#endif
+			/*
+			 * If we are notifying the target of a CRC error
+			 * during packetized operations, the target is
+			 * within its rights to acknowledge our message
+			 * with a busfree.
+			 */
+			if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0
+			 && ahd->send_msg_perror == MSG_INITIATOR_DET_ERR)
+				ahd->msg_flags |= MSG_FLAG_EXPECT_IDE_BUSFREE;
+
+			ahd_outb(ahd, RETURN_2, ahd->send_msg_perror);
+			ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_WRITE);
+			break;
+		}
+
+		msgdone	= ahd->msgout_index == ahd->msgout_len;
+		if (msgdone) {
+			/*
+			 * The target has requested a retry.
+			 * Re-assert ATN, reset our message index to
+			 * 0, and try again.
+			 */
+			ahd->msgout_index = 0;
+			ahd_assert_atn(ahd);
+		}
+
+		lastbyte = ahd->msgout_index == (ahd->msgout_len - 1);
+		if (lastbyte) {
+			/* Last byte is signified by dropping ATN */
+			ahd_outb(ahd, CLRSINT1, CLRATNO);
+		}
+
+		/*
+		 * Clear our interrupt status and present
+		 * the next byte on the bus.
+		 */
+		ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+			printk(" byte 0x%x\n",
+			       ahd->msgout_buf[ahd->msgout_index]);
+#endif
+		ahd_outb(ahd, RETURN_2, ahd->msgout_buf[ahd->msgout_index++]);
+		ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_WRITE);
+		break;
+	}
+	case MSG_TYPE_INITIATOR_MSGIN:
+	{
+		int phasemis;
+		int message_done;
+
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+			ahd_print_devinfo(ahd, &devinfo);
+			printk("INITIATOR_MSG_IN");
+		}
+#endif
+		phasemis = bus_phase != P_MESGIN;
+		if (phasemis) {
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+				printk(" PHASEMIS %s\n",
+				       ahd_lookup_phase_entry(bus_phase)
+							     ->phasemsg);
+			}
+#endif
+			ahd->msgin_index = 0;
+			if (bus_phase == P_MESGOUT
+			 && (ahd->send_msg_perror != 0
+			  || (ahd->msgout_len != 0
+			   && ahd->msgout_index == 0))) {
+				ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+				goto reswitch;
+			}
+			end_session = TRUE;
+			break;
+		}
+
+		/* Pull the byte in without acking it */
+		ahd->msgin_buf[ahd->msgin_index] = ahd_inb(ahd, SCSIBUS);
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+			printk(" byte 0x%x\n",
+			       ahd->msgin_buf[ahd->msgin_index]);
+#endif
+
+		message_done = ahd_parse_msg(ahd, &devinfo);
+
+		if (message_done) {
+			/*
+			 * Clear our incoming message buffer in case there
+			 * is another message following this one.
+			 */
+			ahd->msgin_index = 0;
+
+			/*
+			 * If this message illicited a response,
+			 * assert ATN so the target takes us to the
+			 * message out phase.
+			 */
+			if (ahd->msgout_len != 0) {
+#ifdef AHD_DEBUG
+				if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+					ahd_print_devinfo(ahd, &devinfo);
+					printk("Asserting ATN for response\n");
+				}
+#endif
+				ahd_assert_atn(ahd);
+			}
+		} else 
+			ahd->msgin_index++;
+
+		if (message_done == MSGLOOP_TERMINATED) {
+			end_session = TRUE;
+		} else {
+			/* Ack the byte */
+			ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+			ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_READ);
+		}
+		break;
+	}
+	case MSG_TYPE_TARGET_MSGIN:
+	{
+		int msgdone;
+		int msgout_request;
+
+		/*
+		 * By default, the message loop will continue.
+		 */
+		ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
+
+		if (ahd->msgout_len == 0)
+			panic("Target MSGIN with no active message");
+
+		/*
+		 * If we interrupted a mesgout session, the initiator
+		 * will not know this until our first REQ.  So, we
+		 * only honor mesgout requests after we've sent our
+		 * first byte.
+		 */
+		if ((ahd_inb(ahd, SCSISIGI) & ATNI) != 0
+		 && ahd->msgout_index > 0)
+			msgout_request = TRUE;
+		else
+			msgout_request = FALSE;
+
+		if (msgout_request) {
+
+			/*
+			 * Change gears and see if
+			 * this messages is of interest to
+			 * us or should be passed back to
+			 * the sequencer.
+			 */
+			ahd->msg_type = MSG_TYPE_TARGET_MSGOUT;
+			ahd_outb(ahd, SCSISIGO, P_MESGOUT | BSYO);
+			ahd->msgin_index = 0;
+			/* Dummy read to REQ for first byte */
+			ahd_inb(ahd, SCSIDAT);
+			ahd_outb(ahd, SXFRCTL0,
+				 ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+			break;
+		}
+
+		msgdone = ahd->msgout_index == ahd->msgout_len;
+		if (msgdone) {
+			ahd_outb(ahd, SXFRCTL0,
+				 ahd_inb(ahd, SXFRCTL0) & ~SPIOEN);
+			end_session = TRUE;
+			break;
+		}
+
+		/*
+		 * Present the next byte on the bus.
+		 */
+		ahd_outb(ahd, SXFRCTL0, ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+		ahd_outb(ahd, SCSIDAT, ahd->msgout_buf[ahd->msgout_index++]);
+		break;
+	}
+	case MSG_TYPE_TARGET_MSGOUT:
+	{
+		int lastbyte;
+		int msgdone;
+
+		/*
+		 * By default, the message loop will continue.
+		 */
+		ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
+
+		/*
+		 * The initiator signals that this is
+		 * the last byte by dropping ATN.
+		 */
+		lastbyte = (ahd_inb(ahd, SCSISIGI) & ATNI) == 0;
+
+		/*
+		 * Read the latched byte, but turn off SPIOEN first
+		 * so that we don't inadvertently cause a REQ for the
+		 * next byte.
+		 */
+		ahd_outb(ahd, SXFRCTL0, ahd_inb(ahd, SXFRCTL0) & ~SPIOEN);
+		ahd->msgin_buf[ahd->msgin_index] = ahd_inb(ahd, SCSIDAT);
+		msgdone = ahd_parse_msg(ahd, &devinfo);
+		if (msgdone == MSGLOOP_TERMINATED) {
+			/*
+			 * The message is *really* done in that it caused
+			 * us to go to bus free.  The sequencer has already
+			 * been reset at this point, so pull the ejection
+			 * handle.
+			 */
+			return;
+		}
+		
+		ahd->msgin_index++;
+
+		/*
+		 * XXX Read spec about initiator dropping ATN too soon
+		 *     and use msgdone to detect it.
+		 */
+		if (msgdone == MSGLOOP_MSGCOMPLETE) {
+			ahd->msgin_index = 0;
+
+			/*
+			 * If this message illicited a response, transition
+			 * to the Message in phase and send it.
+			 */
+			if (ahd->msgout_len != 0) {
+				ahd_outb(ahd, SCSISIGO, P_MESGIN | BSYO);
+				ahd_outb(ahd, SXFRCTL0,
+					 ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+				ahd->msg_type = MSG_TYPE_TARGET_MSGIN;
+				ahd->msgin_index = 0;
+				break;
+			}
+		}
+
+		if (lastbyte)
+			end_session = TRUE;
+		else {
+			/* Ask for the next byte. */
+			ahd_outb(ahd, SXFRCTL0,
+				 ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+		}
+
+		break;
+	}
+	default:
+		panic("Unknown REQINIT message type");
+	}
+
+	if (end_session) {
+		if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0) {
+			printk("%s: Returning to Idle Loop\n",
+			       ahd_name(ahd));
+			ahd_clear_msg_state(ahd);
+
+			/*
+			 * Perform the equivalent of a clear_target_state.
+			 */
+			ahd_outb(ahd, LASTPHASE, P_BUSFREE);
+			ahd_outb(ahd, SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT);
+			ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
+		} else {
+			ahd_clear_msg_state(ahd);
+			ahd_outb(ahd, RETURN_1, EXIT_MSG_LOOP);
+		}
+	}
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message.  If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type, u_int msgval, int full)
+{
+	int found;
+	u_int index;
+
+	found = FALSE;
+	index = 0;
+
+	while (index < ahd->msgout_len) {
+		if (ahd->msgout_buf[index] == MSG_EXTENDED) {
+			u_int end_index;
+
+			end_index = index + 1 + ahd->msgout_buf[index + 1];
+			if (ahd->msgout_buf[index+2] == msgval
+			 && type == AHDMSG_EXT) {
+
+				if (full) {
+					if (ahd->msgout_index > end_index)
+						found = TRUE;
+				} else if (ahd->msgout_index > index)
+					found = TRUE;
+			}
+			index = end_index;
+		} else if (ahd->msgout_buf[index] >= MSG_SIMPLE_TASK
+			&& ahd->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+			/* Skip tag type and tag id or residue param*/
+			index += 2;
+		} else {
+			/* Single byte message */
+			if (type == AHDMSG_1B
+			 && ahd->msgout_index > index
+			 && (ahd->msgout_buf[index] == msgval
+			  || ((ahd->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0
+			   && msgval == MSG_IDENTIFYFLAG)))
+				found = TRUE;
+			index++;
+		}
+
+		if (found)
+			break;
+	}
+	return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+	struct	ahd_initiator_tinfo *tinfo;
+	struct	ahd_tmode_tstate *tstate;
+	int	reject;
+	int	done;
+	int	response;
+
+	done = MSGLOOP_IN_PROG;
+	response = FALSE;
+	reject = FALSE;
+	tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	/*
+	 * Parse as much of the message as is available,
+	 * rejecting it if we don't support it.  When
+	 * the entire message is available and has been
+	 * handled, return MSGLOOP_MSGCOMPLETE, indicating
+	 * that we have parsed an entire message.
+	 *
+	 * In the case of extended messages, we accept the length
+	 * byte outright and perform more checking once we know the
+	 * extended message type.
+	 */
+	switch (ahd->msgin_buf[0]) {
+	case MSG_DISCONNECT:
+	case MSG_SAVEDATAPOINTER:
+	case MSG_CMDCOMPLETE:
+	case MSG_RESTOREPOINTERS:
+	case MSG_IGN_WIDE_RESIDUE:
+		/*
+		 * End our message loop as these are messages
+		 * the sequencer handles on its own.
+		 */
+		done = MSGLOOP_TERMINATED;
+		break;
+	case MSG_MESSAGE_REJECT:
+		response = ahd_handle_msg_reject(ahd, devinfo);
+		/* FALLTHROUGH */
+	case MSG_NOOP:
+		done = MSGLOOP_MSGCOMPLETE;
+		break;
+	case MSG_EXTENDED:
+	{
+		/* Wait for enough of the message to begin validation */
+		if (ahd->msgin_index < 2)
+			break;
+		switch (ahd->msgin_buf[2]) {
+		case MSG_EXT_SDTR:
+		{
+			u_int	 period;
+			u_int	 ppr_options;
+			u_int	 offset;
+			u_int	 saved_offset;
+			
+			if (ahd->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have both args before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_SDTR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahd->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+				break;
+
+			period = ahd->msgin_buf[3];
+			ppr_options = 0;
+			saved_offset = offset = ahd->msgin_buf[4];
+			ahd_devlimited_syncrate(ahd, tinfo, &period,
+						&ppr_options, devinfo->role);
+			ahd_validate_offset(ahd, tinfo, period, &offset,
+					    tinfo->curr.width, devinfo->role);
+			if (bootverbose) {
+				printk("(%s:%c:%d:%d): Received "
+				       "SDTR period %x, offset %x\n\t"
+				       "Filtered to period %x, offset %x\n",
+				       ahd_name(ahd), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       ahd->msgin_buf[3], saved_offset,
+				       period, offset);
+			}
+			ahd_set_syncrate(ahd, devinfo, period,
+					 offset, ppr_options,
+					 AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+					 /*paused*/TRUE);
+
+			/*
+			 * See if we initiated Sync Negotiation
+			 * and didn't have to fall down to async
+			 * transfers.
+			 */
+			if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+				/* We started it */
+				if (saved_offset != offset) {
+					/* Went too low - force async */
+					reject = TRUE;
+				}
+			} else {
+				/*
+				 * Send our own SDTR in reply
+				 */
+				if (bootverbose
+				 && devinfo->role == ROLE_INITIATOR) {
+					printk("(%s:%c:%d:%d): Target "
+					       "Initiated SDTR\n",
+					       ahd_name(ahd), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				}
+				ahd->msgout_index = 0;
+				ahd->msgout_len = 0;
+				ahd_construct_sdtr(ahd, devinfo,
+						   period, offset);
+				ahd->msgout_index = 0;
+				response = TRUE;
+			}
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		case MSG_EXT_WDTR:
+		{
+			u_int bus_width;
+			u_int saved_width;
+			u_int sending_reply;
+
+			sending_reply = FALSE;
+			if (ahd->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have our arg before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_WDTR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahd->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+				break;
+
+			bus_width = ahd->msgin_buf[3];
+			saved_width = bus_width;
+			ahd_validate_width(ahd, tinfo, &bus_width,
+					   devinfo->role);
+			if (bootverbose) {
+				printk("(%s:%c:%d:%d): Received WDTR "
+				       "%x filtered to %x\n",
+				       ahd_name(ahd), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       saved_width, bus_width);
+			}
+
+			if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+				/*
+				 * Don't send a WDTR back to the
+				 * target, since we asked first.
+				 * If the width went higher than our
+				 * request, reject it.
+				 */
+				if (saved_width > bus_width) {
+					reject = TRUE;
+					printk("(%s:%c:%d:%d): requested %dBit "
+					       "transfers.  Rejecting...\n",
+					       ahd_name(ahd), devinfo->channel,
+					       devinfo->target, devinfo->lun,
+					       8 * (0x01 << bus_width));
+					bus_width = 0;
+				}
+			} else {
+				/*
+				 * Send our own WDTR in reply
+				 */
+				if (bootverbose
+				 && devinfo->role == ROLE_INITIATOR) {
+					printk("(%s:%c:%d:%d): Target "
+					       "Initiated WDTR\n",
+					       ahd_name(ahd), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				}
+				ahd->msgout_index = 0;
+				ahd->msgout_len = 0;
+				ahd_construct_wdtr(ahd, devinfo, bus_width);
+				ahd->msgout_index = 0;
+				response = TRUE;
+				sending_reply = TRUE;
+			}
+			/*
+			 * After a wide message, we are async, but
+			 * some devices don't seem to honor this portion
+			 * of the spec.  Force a renegotiation of the
+			 * sync component of our transfer agreement even
+			 * if our goal is async.  By updating our width
+			 * after forcing the negotiation, we avoid
+			 * renegotiating for width.
+			 */
+			ahd_update_neg_request(ahd, devinfo, tstate,
+					       tinfo, AHD_NEG_ALWAYS);
+			ahd_set_width(ahd, devinfo, bus_width,
+				      AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+				      /*paused*/TRUE);
+			if (sending_reply == FALSE && reject == FALSE) {
+
+				/*
+				 * We will always have an SDTR to send.
+				 */
+				ahd->msgout_index = 0;
+				ahd->msgout_len = 0;
+				ahd_build_transfer_msg(ahd, devinfo);
+				ahd->msgout_index = 0;
+				response = TRUE;
+			}
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		case MSG_EXT_PPR:
+		{
+			u_int	period;
+			u_int	offset;
+			u_int	bus_width;
+			u_int	ppr_options;
+			u_int	saved_width;
+			u_int	saved_offset;
+			u_int	saved_ppr_options;
+
+			if (ahd->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have all args before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_PPR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahd->msgin_index < (MSG_EXT_PPR_LEN + 1))
+				break;
+
+			period = ahd->msgin_buf[3];
+			offset = ahd->msgin_buf[5];
+			bus_width = ahd->msgin_buf[6];
+			saved_width = bus_width;
+			ppr_options = ahd->msgin_buf[7];
+			/*
+			 * According to the spec, a DT only
+			 * period factor with no DT option
+			 * set implies async.
+			 */
+			if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+			 && period <= 9)
+				offset = 0;
+			saved_ppr_options = ppr_options;
+			saved_offset = offset;
+
+			/*
+			 * Transfer options are only available if we
+			 * are negotiating wide.
+			 */
+			if (bus_width == 0)
+				ppr_options &= MSG_EXT_PPR_QAS_REQ;
+
+			ahd_validate_width(ahd, tinfo, &bus_width,
+					   devinfo->role);
+			ahd_devlimited_syncrate(ahd, tinfo, &period,
+						&ppr_options, devinfo->role);
+			ahd_validate_offset(ahd, tinfo, period, &offset,
+					    bus_width, devinfo->role);
+
+			if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, TRUE)) {
+				/*
+				 * If we are unable to do any of the
+				 * requested options (we went too low),
+				 * then we'll have to reject the message.
+				 */
+				if (saved_width > bus_width
+				 || saved_offset != offset
+				 || saved_ppr_options != ppr_options) {
+					reject = TRUE;
+					period = 0;
+					offset = 0;
+					bus_width = 0;
+					ppr_options = 0;
+				}
+			} else {
+				if (devinfo->role != ROLE_TARGET)
+					printk("(%s:%c:%d:%d): Target "
+					       "Initiated PPR\n",
+					       ahd_name(ahd), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				else
+					printk("(%s:%c:%d:%d): Initiator "
+					       "Initiated PPR\n",
+					       ahd_name(ahd), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				ahd->msgout_index = 0;
+				ahd->msgout_len = 0;
+				ahd_construct_ppr(ahd, devinfo, period, offset,
+						  bus_width, ppr_options);
+				ahd->msgout_index = 0;
+				response = TRUE;
+			}
+			if (bootverbose) {
+				printk("(%s:%c:%d:%d): Received PPR width %x, "
+				       "period %x, offset %x,options %x\n"
+				       "\tFiltered to width %x, period %x, "
+				       "offset %x, options %x\n",
+				       ahd_name(ahd), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       saved_width, ahd->msgin_buf[3],
+				       saved_offset, saved_ppr_options,
+				       bus_width, period, offset, ppr_options);
+			}
+			ahd_set_width(ahd, devinfo, bus_width,
+				      AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+				      /*paused*/TRUE);
+			ahd_set_syncrate(ahd, devinfo, period,
+					 offset, ppr_options,
+					 AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+					 /*paused*/TRUE);
+
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		default:
+			/* Unknown extended message.  Reject it. */
+			reject = TRUE;
+			break;
+		}
+		break;
+	}
+#ifdef AHD_TARGET_MODE
+	case MSG_BUS_DEV_RESET:
+		ahd_handle_devreset(ahd, devinfo, CAM_LUN_WILDCARD,
+				    CAM_BDR_SENT,
+				    "Bus Device Reset Received",
+				    /*verbose_level*/0);
+		ahd_restart(ahd);
+		done = MSGLOOP_TERMINATED;
+		break;
+	case MSG_ABORT_TAG:
+	case MSG_ABORT:
+	case MSG_CLEAR_QUEUE:
+	{
+		int tag;
+
+		/* Target mode messages */
+		if (devinfo->role != ROLE_TARGET) {
+			reject = TRUE;
+			break;
+		}
+		tag = SCB_LIST_NULL;
+		if (ahd->msgin_buf[0] == MSG_ABORT_TAG)
+			tag = ahd_inb(ahd, INITIATOR_TAG);
+		ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
+			       devinfo->lun, tag, ROLE_TARGET,
+			       CAM_REQ_ABORTED);
+
+		tstate = ahd->enabled_targets[devinfo->our_scsiid];
+		if (tstate != NULL) {
+			struct ahd_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[devinfo->lun];
+			if (lstate != NULL) {
+				ahd_queue_lstate_event(ahd, lstate,
+						       devinfo->our_scsiid,
+						       ahd->msgin_buf[0],
+						       /*arg*/tag);
+				ahd_send_lstate_events(ahd, lstate);
+			}
+		}
+		ahd_restart(ahd);
+		done = MSGLOOP_TERMINATED;
+		break;
+	}
+#endif
+	case MSG_QAS_REQUEST:
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+			printk("%s: QAS request.  SCSISIGI == 0x%x\n",
+			       ahd_name(ahd), ahd_inb(ahd, SCSISIGI));
+#endif
+		ahd->msg_flags |= MSG_FLAG_EXPECT_QASREJ_BUSFREE;
+		/* FALLTHROUGH */
+	case MSG_TERM_IO_PROC:
+	default:
+		reject = TRUE;
+		break;
+	}
+
+	if (reject) {
+		/*
+		 * Setup to reject the message.
+		 */
+		ahd->msgout_index = 0;
+		ahd->msgout_len = 1;
+		ahd->msgout_buf[0] = MSG_MESSAGE_REJECT;
+		done = MSGLOOP_MSGCOMPLETE;
+		response = TRUE;
+	}
+
+	if (done != MSGLOOP_IN_PROG && !response)
+		/* Clear the outgoing message buffer */
+		ahd->msgout_len = 0;
+
+	return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+	/*
+	 * 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.
+	 */
+	struct scb *scb;
+	struct ahd_initiator_tinfo *tinfo;
+	struct ahd_tmode_tstate *tstate;
+	u_int scb_index;
+	u_int last_msg;
+	int   response = 0;
+
+	scb_index = ahd_get_scbptr(ahd);
+	scb = ahd_lookup_scb(ahd, scb_index);
+	tinfo = ahd_fetch_transinfo(ahd, devinfo->channel,
+				    devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	/* Might be necessary */
+	last_msg = ahd_inb(ahd, LAST_MSG);
+
+	if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+		if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/TRUE)
+		 && tinfo->goal.period <= AHD_SYNCRATE_PACED) {
+			/*
+			 * Target may not like our SPI-4 PPR Options.
+			 * Attempt to negotiate 80MHz which will turn
+			 * off these options.
+			 */
+			if (bootverbose) {
+				printk("(%s:%c:%d:%d): PPR Rejected. "
+				       "Trying simple U160 PPR\n",
+				       ahd_name(ahd), devinfo->channel,
+				       devinfo->target, devinfo->lun);
+			}
+			tinfo->goal.period = AHD_SYNCRATE_DT;
+			tinfo->goal.ppr_options &= MSG_EXT_PPR_IU_REQ
+						|  MSG_EXT_PPR_QAS_REQ
+						|  MSG_EXT_PPR_DT_REQ;
+		} else {
+			/*
+			 * Target does not support the PPR message.
+			 * Attempt to negotiate SPI-2 style.
+			 */
+			if (bootverbose) {
+				printk("(%s:%c:%d:%d): PPR Rejected. "
+				       "Trying WDTR/SDTR\n",
+				       ahd_name(ahd), devinfo->channel,
+				       devinfo->target, devinfo->lun);
+			}
+			tinfo->goal.ppr_options = 0;
+			tinfo->curr.transport_version = 2;
+			tinfo->goal.transport_version = 2;
+		}
+		ahd->msgout_index = 0;
+		ahd->msgout_len = 0;
+		ahd_build_transfer_msg(ahd, devinfo);
+		ahd->msgout_index = 0;
+		response = 1;
+	} else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+		/* note 8bit xfers */
+		printk("(%s:%c:%d:%d): refuses WIDE negotiation.  Using "
+		       "8bit transfers\n", ahd_name(ahd),
+		       devinfo->channel, devinfo->target, devinfo->lun);
+		ahd_set_width(ahd, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+			      AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+			      /*paused*/TRUE);
+		/*
+		 * No need to clear the sync rate.  If the target
+		 * did not accept the command, our syncrate is
+		 * unaffected.  If the target started the negotiation,
+		 * but rejected our response, we already cleared the
+		 * sync rate before sending our WDTR.
+		 */
+		if (tinfo->goal.offset != tinfo->curr.offset) {
+
+			/* Start the sync negotiation */
+			ahd->msgout_index = 0;
+			ahd->msgout_len = 0;
+			ahd_build_transfer_msg(ahd, devinfo);
+			ahd->msgout_index = 0;
+			response = 1;
+		}
+	} else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+		/* note asynch xfers and clear flag */
+		ahd_set_syncrate(ahd, devinfo, /*period*/0,
+				 /*offset*/0, /*ppr_options*/0,
+				 AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+				 /*paused*/TRUE);
+		printk("(%s:%c:%d:%d): refuses synchronous negotiation. "
+		       "Using asynchronous transfers\n",
+		       ahd_name(ahd), devinfo->channel,
+		       devinfo->target, devinfo->lun);
+	} else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+		int tag_type;
+		int mask;
+
+		tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+		if (tag_type == MSG_SIMPLE_TASK) {
+			printk("(%s:%c:%d:%d): refuses tagged commands.  "
+			       "Performing non-tagged I/O\n", ahd_name(ahd),
+			       devinfo->channel, devinfo->target, devinfo->lun);
+			ahd_set_tags(ahd, scb->io_ctx, devinfo, AHD_QUEUE_NONE);
+			mask = ~0x23;
+		} else {
+			printk("(%s:%c:%d:%d): refuses %s tagged commands.  "
+			       "Performing simple queue tagged I/O only\n",
+			       ahd_name(ahd), devinfo->channel, devinfo->target,
+			       devinfo->lun, tag_type == MSG_ORDERED_TASK
+			       ? "ordered" : "head of queue");
+			ahd_set_tags(ahd, scb->io_ctx, devinfo, AHD_QUEUE_BASIC);
+			mask = ~0x03;
+		}
+
+		/*
+		 * Resend the identify for this CCB as the target
+		 * may believe that the selection is invalid otherwise.
+		 */
+		ahd_outb(ahd, SCB_CONTROL,
+			 ahd_inb_scbram(ahd, SCB_CONTROL) & mask);
+	 	scb->hscb->control &= mask;
+		ahd_set_transaction_tag(scb, /*enabled*/FALSE,
+					/*type*/MSG_SIMPLE_TASK);
+		ahd_outb(ahd, MSG_OUT, MSG_IDENTIFYFLAG);
+		ahd_assert_atn(ahd);
+		ahd_busy_tcl(ahd, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+			     SCB_GET_TAG(scb));
+
+		/*
+		 * Requeue all tagged commands for this target
+		 * currently in our possession so they can be
+		 * converted to untagged commands.
+		 */
+		ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+				   SCB_GET_CHANNEL(ahd, scb),
+				   SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+				   ROLE_INITIATOR, CAM_REQUEUE_REQ,
+				   SEARCH_COMPLETE);
+	} else if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_IDENTIFYFLAG, TRUE)) {
+		/*
+		 * Most likely the device believes that we had
+		 * previously negotiated packetized.
+		 */
+		ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
+			       |  MSG_FLAG_IU_REQ_CHANGED;
+
+		ahd_force_renegotiation(ahd, devinfo);
+		ahd->msgout_index = 0;
+		ahd->msgout_len = 0;
+		ahd_build_transfer_msg(ahd, devinfo);
+		ahd->msgout_index = 0;
+		response = 1;
+	} else {
+		/*
+		 * Otherwise, we ignore it.
+		 */
+		printk("%s:%c:%d: Message reject for %x -- ignored\n",
+		       ahd_name(ahd), devinfo->channel, devinfo->target,
+		       last_msg);
+	}
+	return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahd_handle_ign_wide_residue(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+	u_int scb_index;
+	struct scb *scb;
+
+	scb_index = ahd_get_scbptr(ahd);
+	scb = ahd_lookup_scb(ahd, scb_index);
+	/*
+	 * XXX Actually check data direction in the sequencer?
+	 * Perhaps add datadir to some spare bits in the hscb?
+	 */
+	if ((ahd_inb(ahd, SEQ_FLAGS) & DPHASE) == 0
+	 || ahd_get_transfer_dir(scb) != CAM_DIR_IN) {
+		/*
+		 * Ignore the message if we haven't
+		 * seen an appropriate data phase yet.
+		 */
+	} else {
+		/*
+		 * If the residual occurred on the last
+		 * transfer and the transfer request was
+		 * expected to end on an odd count, do
+		 * nothing.  Otherwise, subtract a byte
+		 * and update the residual count accordingly.
+		 */
+		uint32_t sgptr;
+
+		sgptr = ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR);
+		if ((sgptr & SG_LIST_NULL) != 0
+		 && (ahd_inb_scbram(ahd, SCB_TASK_ATTRIBUTE)
+		     & SCB_XFERLEN_ODD) != 0) {
+			/*
+			 * If the residual occurred on the last
+			 * transfer and the transfer request was
+			 * expected to end on an odd count, do
+			 * nothing.
+			 */
+		} else {
+			uint32_t data_cnt;
+			uint64_t data_addr;
+			uint32_t sglen;
+
+			/* Pull in the rest of the sgptr */
+			sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
+			data_cnt = ahd_inl_scbram(ahd, SCB_RESIDUAL_DATACNT);
+			if ((sgptr & SG_LIST_NULL) != 0) {
+				/*
+				 * The residual data count is not updated
+				 * for the command run to completion case.
+				 * Explicitly zero the count.
+				 */
+				data_cnt &= ~AHD_SG_LEN_MASK;
+			}
+			data_addr = ahd_inq(ahd, SHADDR);
+			data_cnt += 1;
+			data_addr -= 1;
+			sgptr &= SG_PTR_MASK;
+			if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+				struct ahd_dma64_seg *sg;
+
+				sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+				/*
+				 * The residual sg ptr points to the next S/G
+				 * to load so we must go back one.
+				 */
+				sg--;
+				sglen = ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+				if (sg != scb->sg_list
+				 && sglen < (data_cnt & AHD_SG_LEN_MASK)) {
+
+					sg--;
+					sglen = ahd_le32toh(sg->len);
+					/*
+					 * Preserve High Address and SG_LIST
+					 * bits while setting the count to 1.
+					 */
+					data_cnt = 1|(sglen&(~AHD_SG_LEN_MASK));
+					data_addr = ahd_le64toh(sg->addr)
+						  + (sglen & AHD_SG_LEN_MASK)
+						  - 1;
+
+					/*
+					 * Increment sg so it points to the
+					 * "next" sg.
+					 */
+					sg++;
+					sgptr = ahd_sg_virt_to_bus(ahd, scb,
+								   sg);
+				}
+			} else {
+				struct ahd_dma_seg *sg;
+
+				sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+				/*
+				 * The residual sg ptr points to the next S/G
+				 * to load so we must go back one.
+				 */
+				sg--;
+				sglen = ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+				if (sg != scb->sg_list
+				 && sglen < (data_cnt & AHD_SG_LEN_MASK)) {
+
+					sg--;
+					sglen = ahd_le32toh(sg->len);
+					/*
+					 * Preserve High Address and SG_LIST
+					 * bits while setting the count to 1.
+					 */
+					data_cnt = 1|(sglen&(~AHD_SG_LEN_MASK));
+					data_addr = ahd_le32toh(sg->addr)
+						  + (sglen & AHD_SG_LEN_MASK)
+						  - 1;
+
+					/*
+					 * Increment sg so it points to the
+					 * "next" sg.
+					 */
+					sg++;
+					sgptr = ahd_sg_virt_to_bus(ahd, scb,
+								  sg);
+				}
+			}
+			/*
+			 * Toggle the "oddness" of the transfer length
+			 * to handle this mid-transfer ignore wide
+			 * residue.  This ensures that the oddness is
+			 * correct for subsequent data transfers.
+			 */
+			ahd_outb(ahd, SCB_TASK_ATTRIBUTE,
+			    ahd_inb_scbram(ahd, SCB_TASK_ATTRIBUTE)
+			    ^ SCB_XFERLEN_ODD);
+
+			ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+			ahd_outl(ahd, SCB_RESIDUAL_DATACNT, data_cnt);
+			/*
+			 * The FIFO's pointers will be updated if/when the
+			 * sequencer re-enters a data phase.
+			 */
+		}
+	}
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahd_reinitialize_dataptrs(struct ahd_softc *ahd)
+{
+	struct		 scb *scb;
+	ahd_mode_state	 saved_modes;
+	u_int		 scb_index;
+	u_int		 wait;
+	uint32_t	 sgptr;
+	uint32_t	 resid;
+	uint64_t	 dataptr;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK,
+			 AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK);
+			 
+	scb_index = ahd_get_scbptr(ahd);
+	scb = ahd_lookup_scb(ahd, scb_index);
+
+	/*
+	 * Release and reacquire the FIFO so we
+	 * have a clean slate.
+	 */
+	ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
+	wait = 1000;
+	while (--wait && !(ahd_inb(ahd, MDFFSTAT) & FIFOFREE))
+		ahd_delay(100);
+	if (wait == 0) {
+		ahd_print_path(ahd, scb);
+		printk("ahd_reinitialize_dataptrs: Forcing FIFO free.\n");
+		ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
+	}
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	ahd_outb(ahd, DFFSTAT,
+		 ahd_inb(ahd, DFFSTAT)
+		| (saved_modes == 0x11 ? CURRFIFO_1 : CURRFIFO_0));
+
+	/*
+	 * Determine initial values for data_addr and data_cnt
+	 * for resuming the data phase.
+	 */
+	sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
+	sgptr &= SG_PTR_MASK;
+
+	resid = (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT + 2) << 16)
+	      | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT + 1) << 8)
+	      | ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT);
+
+	if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+		struct ahd_dma64_seg *sg;
+
+		sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+		/* The residual sg_ptr always points to the next sg */
+		sg--;
+
+		dataptr = ahd_le64toh(sg->addr)
+			+ (ahd_le32toh(sg->len) & AHD_SG_LEN_MASK)
+			- resid;
+		ahd_outl(ahd, HADDR + 4, dataptr >> 32);
+	} else {
+		struct	 ahd_dma_seg *sg;
+
+		sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+		/* The residual sg_ptr always points to the next sg */
+		sg--;
+
+		dataptr = ahd_le32toh(sg->addr)
+			+ (ahd_le32toh(sg->len) & AHD_SG_LEN_MASK)
+			- resid;
+		ahd_outb(ahd, HADDR + 4,
+			 (ahd_le32toh(sg->len) & ~AHD_SG_LEN_MASK) >> 24);
+	}
+	ahd_outl(ahd, HADDR, dataptr);
+	ahd_outb(ahd, HCNT + 2, resid >> 16);
+	ahd_outb(ahd, HCNT + 1, resid >> 8);
+	ahd_outb(ahd, HCNT, resid);
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		    u_int lun, cam_status status, char *message,
+		    int verbose_level)
+{
+#ifdef AHD_TARGET_MODE
+	struct ahd_tmode_tstate* tstate;
+#endif
+	int found;
+
+	found = ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
+			       lun, SCB_LIST_NULL, devinfo->role,
+			       status);
+
+#ifdef AHD_TARGET_MODE
+	/*
+	 * Send an immediate notify ccb to all target mord peripheral
+	 * drivers affected by this action.
+	 */
+	tstate = ahd->enabled_targets[devinfo->our_scsiid];
+	if (tstate != NULL) {
+		u_int cur_lun;
+		u_int max_lun;
+
+		if (lun != CAM_LUN_WILDCARD) {
+			cur_lun = 0;
+			max_lun = AHD_NUM_LUNS - 1;
+		} else {
+			cur_lun = lun;
+			max_lun = lun;
+		}
+		for (;cur_lun <= max_lun; cur_lun++) {
+			struct ahd_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[cur_lun];
+			if (lstate == NULL)
+				continue;
+
+			ahd_queue_lstate_event(ahd, lstate, devinfo->our_scsiid,
+					       MSG_BUS_DEV_RESET, /*arg*/0);
+			ahd_send_lstate_events(ahd, lstate);
+		}
+	}
+#endif
+
+	/*
+	 * Go back to async/narrow transfers and renegotiate.
+	 */
+	ahd_set_width(ahd, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+		      AHD_TRANS_CUR, /*paused*/TRUE);
+	ahd_set_syncrate(ahd, devinfo, /*period*/0, /*offset*/0,
+			 /*ppr_options*/0, AHD_TRANS_CUR,
+			 /*paused*/TRUE);
+	
+	if (status != CAM_SEL_TIMEOUT)
+		ahd_send_async(ahd, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_SENT_BDR);
+
+	if (message != NULL && bootverbose)
+		printk("%s: %s on %c:%d. %d SCBs aborted\n", ahd_name(ahd),
+		       message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHD_TARGET_MODE
+static void
+ahd_setup_target_msgin(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+		       struct scb *scb)
+{
+
+	/*              
+	 * To facilitate adding multiple messages together,
+	 * each routine should increment the index and len
+	 * variables instead of setting them explicitly.
+	 */             
+	ahd->msgout_index = 0;
+	ahd->msgout_len = 0;
+
+	if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+		ahd_build_transfer_msg(ahd, devinfo);
+	else
+		panic("ahd_intr: AWAITING target message with no message");
+
+	ahd->msgout_index = 0;
+	ahd->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+static u_int
+ahd_sglist_size(struct ahd_softc *ahd)
+{
+	bus_size_t list_size;
+
+	list_size = sizeof(struct ahd_dma_seg) * AHD_NSEG;
+	if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+		list_size = sizeof(struct ahd_dma64_seg) * AHD_NSEG;
+	return (list_size);
+}
+
+/*
+ * Calculate the optimum S/G List allocation size.  S/G elements used
+ * for a given transaction must be physically contiguous.  Assume the
+ * OS will allocate full pages to us, so it doesn't make sense to request
+ * less than a page.
+ */
+static u_int
+ahd_sglist_allocsize(struct ahd_softc *ahd)
+{
+	bus_size_t sg_list_increment;
+	bus_size_t sg_list_size;
+	bus_size_t max_list_size;
+	bus_size_t best_list_size;
+
+	/* Start out with the minimum required for AHD_NSEG. */
+	sg_list_increment = ahd_sglist_size(ahd);
+	sg_list_size = sg_list_increment;
+
+	/* Get us as close as possible to a page in size. */
+	while ((sg_list_size + sg_list_increment) <= PAGE_SIZE)
+		sg_list_size += sg_list_increment;
+
+	/*
+	 * Try to reduce the amount of wastage by allocating
+	 * multiple pages.
+	 */
+	best_list_size = sg_list_size;
+	max_list_size = roundup(sg_list_increment, PAGE_SIZE);
+	if (max_list_size < 4 * PAGE_SIZE)
+		max_list_size = 4 * PAGE_SIZE;
+	if (max_list_size > (AHD_SCB_MAX_ALLOC * sg_list_increment))
+		max_list_size = (AHD_SCB_MAX_ALLOC * sg_list_increment);
+	while ((sg_list_size + sg_list_increment) <= max_list_size
+	   &&  (sg_list_size % PAGE_SIZE) != 0) {
+		bus_size_t new_mod;
+		bus_size_t best_mod;
+
+		sg_list_size += sg_list_increment;
+		new_mod = sg_list_size % PAGE_SIZE;
+		best_mod = best_list_size % PAGE_SIZE;
+		if (new_mod > best_mod || new_mod == 0) {
+			best_list_size = sg_list_size;
+		}
+	}
+	return (best_list_size);
+}
+
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahd_softc *
+ahd_alloc(void *platform_arg, char *name)
+{
+	struct  ahd_softc *ahd;
+
+#ifndef	__FreeBSD__
+	ahd = kmalloc(sizeof(*ahd), GFP_ATOMIC);
+	if (!ahd) {
+		printk("aic7xxx: cannot malloc softc!\n");
+		kfree(name);
+		return NULL;
+	}
+#else
+	ahd = device_get_softc((device_t)platform_arg);
+#endif
+	memset(ahd, 0, sizeof(*ahd));
+	ahd->seep_config = kmalloc(sizeof(*ahd->seep_config), GFP_ATOMIC);
+	if (ahd->seep_config == NULL) {
+#ifndef	__FreeBSD__
+		kfree(ahd);
+#endif
+		kfree(name);
+		return (NULL);
+	}
+	LIST_INIT(&ahd->pending_scbs);
+	/* We don't know our unit number until the OSM sets it */
+	ahd->name = name;
+	ahd->unit = -1;
+	ahd->description = NULL;
+	ahd->bus_description = NULL;
+	ahd->channel = 'A';
+	ahd->chip = AHD_NONE;
+	ahd->features = AHD_FENONE;
+	ahd->bugs = AHD_BUGNONE;
+	ahd->flags = AHD_SPCHK_ENB_A|AHD_RESET_BUS_A|AHD_TERM_ENB_A
+		   | AHD_EXTENDED_TRANS_A|AHD_STPWLEVEL_A;
+	ahd_timer_init(&ahd->reset_timer);
+	ahd_timer_init(&ahd->stat_timer);
+	ahd->int_coalescing_timer = AHD_INT_COALESCING_TIMER_DEFAULT;
+	ahd->int_coalescing_maxcmds = AHD_INT_COALESCING_MAXCMDS_DEFAULT;
+	ahd->int_coalescing_mincmds = AHD_INT_COALESCING_MINCMDS_DEFAULT;
+	ahd->int_coalescing_threshold = AHD_INT_COALESCING_THRESHOLD_DEFAULT;
+	ahd->int_coalescing_stop_threshold =
+	    AHD_INT_COALESCING_STOP_THRESHOLD_DEFAULT;
+
+	if (ahd_platform_alloc(ahd, platform_arg) != 0) {
+		ahd_free(ahd);
+		ahd = NULL;
+	}
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_MEMORY) != 0) {
+		printk("%s: scb size = 0x%x, hscb size = 0x%x\n",
+		       ahd_name(ahd), (u_int)sizeof(struct scb),
+		       (u_int)sizeof(struct hardware_scb));
+	}
+#endif
+	return (ahd);
+}
+
+int
+ahd_softc_init(struct ahd_softc *ahd)
+{
+
+	ahd->unpause = 0;
+	ahd->pause = PAUSE; 
+	return (0);
+}
+
+void
+ahd_set_unit(struct ahd_softc *ahd, int unit)
+{
+	ahd->unit = unit;
+}
+
+void
+ahd_set_name(struct ahd_softc *ahd, char *name)
+{
+	if (ahd->name != NULL)
+		kfree(ahd->name);
+	ahd->name = name;
+}
+
+void
+ahd_free(struct ahd_softc *ahd)
+{
+	int i;
+
+	switch (ahd->init_level) {
+	default:
+	case 5:
+		ahd_shutdown(ahd);
+		/* FALLTHROUGH */
+	case 4:
+		ahd_dmamap_unload(ahd, ahd->shared_data_dmat,
+				  ahd->shared_data_map.dmamap);
+		/* FALLTHROUGH */
+	case 3:
+		ahd_dmamem_free(ahd, ahd->shared_data_dmat, ahd->qoutfifo,
+				ahd->shared_data_map.dmamap);
+		ahd_dmamap_destroy(ahd, ahd->shared_data_dmat,
+				   ahd->shared_data_map.dmamap);
+		/* FALLTHROUGH */
+	case 2:
+		ahd_dma_tag_destroy(ahd, ahd->shared_data_dmat);
+	case 1:
+#ifndef __linux__
+		ahd_dma_tag_destroy(ahd, ahd->buffer_dmat);
+#endif
+		break;
+	case 0:
+		break;
+	}
+
+#ifndef __linux__
+	ahd_dma_tag_destroy(ahd, ahd->parent_dmat);
+#endif
+	ahd_platform_free(ahd);
+	ahd_fini_scbdata(ahd);
+	for (i = 0; i < AHD_NUM_TARGETS; i++) {
+		struct ahd_tmode_tstate *tstate;
+
+		tstate = ahd->enabled_targets[i];
+		if (tstate != NULL) {
+#ifdef AHD_TARGET_MODE
+			int j;
+
+			for (j = 0; j < AHD_NUM_LUNS; j++) {
+				struct ahd_tmode_lstate *lstate;
+
+				lstate = tstate->enabled_luns[j];
+				if (lstate != NULL) {
+					xpt_free_path(lstate->path);
+					kfree(lstate);
+				}
+			}
+#endif
+			kfree(tstate);
+		}
+	}
+#ifdef AHD_TARGET_MODE
+	if (ahd->black_hole != NULL) {
+		xpt_free_path(ahd->black_hole->path);
+		kfree(ahd->black_hole);
+	}
+#endif
+	if (ahd->name != NULL)
+		kfree(ahd->name);
+	if (ahd->seep_config != NULL)
+		kfree(ahd->seep_config);
+	if (ahd->saved_stack != NULL)
+		kfree(ahd->saved_stack);
+#ifndef __FreeBSD__
+	kfree(ahd);
+#endif
+	return;
+}
+
+static void
+ahd_shutdown(void *arg)
+{
+	struct	ahd_softc *ahd;
+
+	ahd = (struct ahd_softc *)arg;
+
+	/*
+	 * Stop periodic timer callbacks.
+	 */
+	ahd_timer_stop(&ahd->reset_timer);
+	ahd_timer_stop(&ahd->stat_timer);
+
+	/* This will reset most registers to 0, but not all */
+	ahd_reset(ahd, /*reinit*/FALSE);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset.  If "reinit" is
+ * non-zero, this reset occurred after initial configuration
+ * and the caller requests that the chip be fully reinitialized
+ * to a runable state.  Chip interrupts are *not* enabled after
+ * a reinitialization.  The caller must enable interrupts via
+ * ahd_intr_enable().
+ */
+int
+ahd_reset(struct ahd_softc *ahd, int reinit)
+{
+	u_int	 sxfrctl1;
+	int	 wait;
+	uint32_t cmd;
+	
+	/*
+	 * Preserve the value of the SXFRCTL1 register for all channels.
+	 * It contains settings that affect termination and we don't want
+	 * to disturb the integrity of the bus.
+	 */
+	ahd_pause(ahd);
+	ahd_update_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	sxfrctl1 = ahd_inb(ahd, SXFRCTL1);
+
+	cmd = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
+	if ((ahd->bugs & AHD_PCIX_CHIPRST_BUG) != 0) {
+		uint32_t mod_cmd;
+
+		/*
+		 * A4 Razor #632
+		 * During the assertion of CHIPRST, the chip
+		 * does not disable its parity logic prior to
+		 * the start of the reset.  This may cause a
+		 * parity error to be detected and thus a
+		 * spurious SERR or PERR assertion.  Disble
+		 * PERR and SERR responses during the CHIPRST.
+		 */
+		mod_cmd = cmd & ~(PCIM_CMD_PERRESPEN|PCIM_CMD_SERRESPEN);
+		ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+				     mod_cmd, /*bytes*/2);
+	}
+	ahd_outb(ahd, HCNTRL, CHIPRST | ahd->pause);
+
+	/*
+	 * Ensure that the reset has finished.  We delay 1000us
+	 * prior to reading the register to make sure the chip
+	 * has sufficiently completed its reset to handle register
+	 * accesses.
+	 */
+	wait = 1000;
+	do {
+		ahd_delay(1000);
+	} while (--wait && !(ahd_inb(ahd, HCNTRL) & CHIPRSTACK));
+
+	if (wait == 0) {
+		printk("%s: WARNING - Failed chip reset!  "
+		       "Trying to initialize anyway.\n", ahd_name(ahd));
+	}
+	ahd_outb(ahd, HCNTRL, ahd->pause);
+
+	if ((ahd->bugs & AHD_PCIX_CHIPRST_BUG) != 0) {
+		/*
+		 * Clear any latched PCI error status and restore
+		 * previous SERR and PERR response enables.
+		 */
+		ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+				     0xFF, /*bytes*/1);
+		ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+				     cmd, /*bytes*/2);
+	}
+
+	/*
+	 * Mode should be SCSI after a chip reset, but lets
+	 * set it just to be safe.  We touch the MODE_PTR
+	 * register directly so as to bypass the lazy update
+	 * code in ahd_set_modes().
+	 */
+	ahd_known_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	ahd_outb(ahd, MODE_PTR,
+		 ahd_build_mode_state(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI));
+
+	/*
+	 * Restore SXFRCTL1.
+	 *
+	 * We must always initialize STPWEN to 1 before we
+	 * restore the saved values.  STPWEN is initialized
+	 * to a tri-state condition which can only be cleared
+	 * by turning it on.
+	 */
+	ahd_outb(ahd, SXFRCTL1, sxfrctl1|STPWEN);
+	ahd_outb(ahd, SXFRCTL1, sxfrctl1);
+
+	/* Determine chip configuration */
+	ahd->features &= ~AHD_WIDE;
+	if ((ahd_inb(ahd, SBLKCTL) & SELWIDE) != 0)
+		ahd->features |= AHD_WIDE;
+
+	/*
+	 * If a recovery action has forced a chip reset,
+	 * re-initialize the chip to our liking.
+	 */
+	if (reinit != 0)
+		ahd_chip_init(ahd);
+
+	return (0);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+static int
+ahd_probe_scbs(struct ahd_softc *ahd) {
+	int i;
+
+	AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+			 ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+	for (i = 0; i < AHD_SCB_MAX; i++) {
+		int j;
+
+		ahd_set_scbptr(ahd, i);
+		ahd_outw(ahd, SCB_BASE, i);
+		for (j = 2; j < 64; j++)
+			ahd_outb(ahd, SCB_BASE+j, 0);
+		/* Start out life as unallocated (needing an abort) */
+		ahd_outb(ahd, SCB_CONTROL, MK_MESSAGE);
+		if (ahd_inw_scbram(ahd, SCB_BASE) != i)
+			break;
+		ahd_set_scbptr(ahd, 0);
+		if (ahd_inw_scbram(ahd, SCB_BASE) != 0)
+			break;
+	}
+	return (i);
+}
+
+static void
+ahd_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 
+{
+	dma_addr_t *baddr;
+
+	baddr = (dma_addr_t *)arg;
+	*baddr = segs->ds_addr;
+}
+
+static void
+ahd_initialize_hscbs(struct ahd_softc *ahd)
+{
+	int i;
+
+	for (i = 0; i < ahd->scb_data.maxhscbs; i++) {
+		ahd_set_scbptr(ahd, i);
+
+		/* Clear the control byte. */
+		ahd_outb(ahd, SCB_CONTROL, 0);
+
+		/* Set the next pointer */
+		ahd_outw(ahd, SCB_NEXT, SCB_LIST_NULL);
+	}
+}
+
+static int
+ahd_init_scbdata(struct ahd_softc *ahd)
+{
+	struct	scb_data *scb_data;
+	int	i;
+
+	scb_data = &ahd->scb_data;
+	TAILQ_INIT(&scb_data->free_scbs);
+	for (i = 0; i < AHD_NUM_TARGETS * AHD_NUM_LUNS_NONPKT; i++)
+		LIST_INIT(&scb_data->free_scb_lists[i]);
+	LIST_INIT(&scb_data->any_dev_free_scb_list);
+	SLIST_INIT(&scb_data->hscb_maps);
+	SLIST_INIT(&scb_data->sg_maps);
+	SLIST_INIT(&scb_data->sense_maps);
+
+	/* Determine the number of hardware SCBs and initialize them */
+	scb_data->maxhscbs = ahd_probe_scbs(ahd);
+	if (scb_data->maxhscbs == 0) {
+		printk("%s: No SCB space found\n", ahd_name(ahd));
+		return (ENXIO);
+	}
+
+	ahd_initialize_hscbs(ahd);
+
+	/*
+	 * Create our DMA tags.  These tags define the kinds of device
+	 * accessible memory allocations and memory mappings we will
+	 * need to perform during normal operation.
+	 *
+	 * Unless we need to further restrict the allocation, we rely
+	 * on the restrictions of the parent dmat, hence the common
+	 * use of MAXADDR and MAXSIZE.
+	 */
+
+	/* DMA tag for our hardware scb structures */
+	if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       PAGE_SIZE, /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->hscb_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* DMA tag for our S/G structures. */
+	if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/8,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       ahd_sglist_allocsize(ahd), /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->sg_dmat) != 0) {
+		goto error_exit;
+	}
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_MEMORY) != 0)
+		printk("%s: ahd_sglist_allocsize = 0x%x\n", ahd_name(ahd),
+		       ahd_sglist_allocsize(ahd));
+#endif
+
+	scb_data->init_level++;
+
+	/* DMA tag for our sense buffers.  We allocate in page sized chunks */
+	if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       PAGE_SIZE, /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->sense_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Perform initial CCB allocation */
+	ahd_alloc_scbs(ahd);
+
+	if (scb_data->numscbs == 0) {
+		printk("%s: ahd_init_scbdata - "
+		       "Unable to allocate initial scbs\n",
+		       ahd_name(ahd));
+		goto error_exit;
+	}
+
+	/*
+	 * Note that we were successful
+	 */
+	return (0); 
+
+error_exit:
+
+	return (ENOMEM);
+}
+
+static struct scb *
+ahd_find_scb_by_tag(struct ahd_softc *ahd, u_int tag)
+{
+	struct scb *scb;
+
+	/*
+	 * Look on the pending list.
+	 */
+	LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+		if (SCB_GET_TAG(scb) == tag)
+			return (scb);
+	}
+
+	/*
+	 * Then on all of the collision free lists.
+	 */
+	TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+		struct scb *list_scb;
+
+		list_scb = scb;
+		do {
+			if (SCB_GET_TAG(list_scb) == tag)
+				return (list_scb);
+			list_scb = LIST_NEXT(list_scb, collision_links);
+		} while (list_scb);
+	}
+
+	/*
+	 * And finally on the generic free list.
+	 */
+	LIST_FOREACH(scb, &ahd->scb_data.any_dev_free_scb_list, links.le) {
+		if (SCB_GET_TAG(scb) == tag)
+			return (scb);
+	}
+
+	return (NULL);
+}
+
+static void
+ahd_fini_scbdata(struct ahd_softc *ahd)
+{
+	struct scb_data *scb_data;
+
+	scb_data = &ahd->scb_data;
+	if (scb_data == NULL)
+		return;
+
+	switch (scb_data->init_level) {
+	default:
+	case 7:
+	{
+		struct map_node *sns_map;
+
+		while ((sns_map = SLIST_FIRST(&scb_data->sense_maps)) != NULL) {
+			SLIST_REMOVE_HEAD(&scb_data->sense_maps, links);
+			ahd_dmamap_unload(ahd, scb_data->sense_dmat,
+					  sns_map->dmamap);
+			ahd_dmamem_free(ahd, scb_data->sense_dmat,
+					sns_map->vaddr, sns_map->dmamap);
+			kfree(sns_map);
+		}
+		ahd_dma_tag_destroy(ahd, scb_data->sense_dmat);
+		/* FALLTHROUGH */
+	}
+	case 6:
+	{
+		struct map_node *sg_map;
+
+		while ((sg_map = SLIST_FIRST(&scb_data->sg_maps)) != NULL) {
+			SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+			ahd_dmamap_unload(ahd, scb_data->sg_dmat,
+					  sg_map->dmamap);
+			ahd_dmamem_free(ahd, scb_data->sg_dmat,
+					sg_map->vaddr, sg_map->dmamap);
+			kfree(sg_map);
+		}
+		ahd_dma_tag_destroy(ahd, scb_data->sg_dmat);
+		/* FALLTHROUGH */
+	}
+	case 5:
+	{
+		struct map_node *hscb_map;
+
+		while ((hscb_map = SLIST_FIRST(&scb_data->hscb_maps)) != NULL) {
+			SLIST_REMOVE_HEAD(&scb_data->hscb_maps, links);
+			ahd_dmamap_unload(ahd, scb_data->hscb_dmat,
+					  hscb_map->dmamap);
+			ahd_dmamem_free(ahd, scb_data->hscb_dmat,
+					hscb_map->vaddr, hscb_map->dmamap);
+			kfree(hscb_map);
+		}
+		ahd_dma_tag_destroy(ahd, scb_data->hscb_dmat);
+		/* FALLTHROUGH */
+	}
+	case 4:
+	case 3:
+	case 2:
+	case 1:
+	case 0:
+		break;
+	}
+}
+
+/*
+ * DSP filter Bypass must be enabled until the first selection
+ * after a change in bus mode (Razor #491 and #493).
+ */
+static void
+ahd_setup_iocell_workaround(struct ahd_softc *ahd)
+{
+	ahd_mode_state saved_modes;
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+	ahd_outb(ahd, DSPDATACTL, ahd_inb(ahd, DSPDATACTL)
+	       | BYPASSENAB | RCVROFFSTDIS | XMITOFFSTDIS);
+	ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) | (ENSELDO|ENSELDI));
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_MISC) != 0)
+		printk("%s: Setting up iocell workaround\n", ahd_name(ahd));
+#endif
+	ahd_restore_modes(ahd, saved_modes);
+	ahd->flags &= ~AHD_HAD_FIRST_SEL;
+}
+
+static void
+ahd_iocell_first_selection(struct ahd_softc *ahd)
+{
+	ahd_mode_state	saved_modes;
+	u_int		sblkctl;
+
+	if ((ahd->flags & AHD_HAD_FIRST_SEL) != 0)
+		return;
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	sblkctl = ahd_inb(ahd, SBLKCTL);
+	ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_MISC) != 0)
+		printk("%s: iocell first selection\n", ahd_name(ahd));
+#endif
+	if ((sblkctl & ENAB40) != 0) {
+		ahd_outb(ahd, DSPDATACTL,
+			 ahd_inb(ahd, DSPDATACTL) & ~BYPASSENAB);
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MISC) != 0)
+			printk("%s: BYPASS now disabled\n", ahd_name(ahd));
+#endif
+	}
+	ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) & ~(ENSELDO|ENSELDI));
+	ahd_outb(ahd, CLRINT, CLRSCSIINT);
+	ahd_restore_modes(ahd, saved_modes);
+	ahd->flags |= AHD_HAD_FIRST_SEL;
+}
+
+/*************************** SCB Management ***********************************/
+static void
+ahd_add_col_list(struct ahd_softc *ahd, struct scb *scb, u_int col_idx)
+{
+	struct	scb_list *free_list;
+	struct	scb_tailq *free_tailq;
+	struct	scb *first_scb;
+
+	scb->flags |= SCB_ON_COL_LIST;
+	AHD_SET_SCB_COL_IDX(scb, col_idx);
+	free_list = &ahd->scb_data.free_scb_lists[col_idx];
+	free_tailq = &ahd->scb_data.free_scbs;
+	first_scb = LIST_FIRST(free_list);
+	if (first_scb != NULL) {
+		LIST_INSERT_AFTER(first_scb, scb, collision_links);
+	} else {
+		LIST_INSERT_HEAD(free_list, scb, collision_links);
+		TAILQ_INSERT_TAIL(free_tailq, scb, links.tqe);
+	}
+}
+
+static void
+ahd_rem_col_list(struct ahd_softc *ahd, struct scb *scb)
+{
+	struct	scb_list *free_list;
+	struct	scb_tailq *free_tailq;
+	struct	scb *first_scb;
+	u_int	col_idx;
+
+	scb->flags &= ~SCB_ON_COL_LIST;
+	col_idx = AHD_GET_SCB_COL_IDX(ahd, scb);
+	free_list = &ahd->scb_data.free_scb_lists[col_idx];
+	free_tailq = &ahd->scb_data.free_scbs;
+	first_scb = LIST_FIRST(free_list);
+	if (first_scb == scb) {
+		struct scb *next_scb;
+
+		/*
+		 * Maintain order in the collision free
+		 * lists for fairness if this device has
+		 * other colliding tags active.
+		 */
+		next_scb = LIST_NEXT(scb, collision_links);
+		if (next_scb != NULL) {
+			TAILQ_INSERT_AFTER(free_tailq, scb,
+					   next_scb, links.tqe);
+		}
+		TAILQ_REMOVE(free_tailq, scb, links.tqe);
+	}
+	LIST_REMOVE(scb, collision_links);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+struct scb *
+ahd_get_scb(struct ahd_softc *ahd, u_int col_idx)
+{
+	struct scb *scb;
+	int tries;
+
+	tries = 0;
+look_again:
+	TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+		if (AHD_GET_SCB_COL_IDX(ahd, scb) != col_idx) {
+			ahd_rem_col_list(ahd, scb);
+			goto found;
+		}
+	}
+	if ((scb = LIST_FIRST(&ahd->scb_data.any_dev_free_scb_list)) == NULL) {
+
+		if (tries++ != 0)
+			return (NULL);
+		ahd_alloc_scbs(ahd);
+		goto look_again;
+	}
+	LIST_REMOVE(scb, links.le);
+	if (col_idx != AHD_NEVER_COL_IDX
+	 && (scb->col_scb != NULL)
+	 && (scb->col_scb->flags & SCB_ACTIVE) == 0) {
+		LIST_REMOVE(scb->col_scb, links.le);
+		ahd_add_col_list(ahd, scb->col_scb, col_idx);
+	}
+found:
+	scb->flags |= SCB_ACTIVE;
+	return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+void
+ahd_free_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+	/* Clean up for the next user */
+	scb->flags = SCB_FLAG_NONE;
+	scb->hscb->control = 0;
+	ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = NULL;
+
+	if (scb->col_scb == NULL) {
+
+		/*
+		 * No collision possible.  Just free normally.
+		 */
+		LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+				 scb, links.le);
+	} else if ((scb->col_scb->flags & SCB_ON_COL_LIST) != 0) {
+
+		/*
+		 * The SCB we might have collided with is on
+		 * a free collision list.  Put both SCBs on
+		 * the generic list.
+		 */
+		ahd_rem_col_list(ahd, scb->col_scb);
+		LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+				 scb, links.le);
+		LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+				 scb->col_scb, links.le);
+	} else if ((scb->col_scb->flags
+		  & (SCB_PACKETIZED|SCB_ACTIVE)) == SCB_ACTIVE
+		&& (scb->col_scb->hscb->control & TAG_ENB) != 0) {
+
+		/*
+		 * The SCB we might collide with on the next allocation
+		 * is still active in a non-packetized, tagged, context.
+		 * Put us on the SCB collision list.
+		 */
+		ahd_add_col_list(ahd, scb,
+				 AHD_GET_SCB_COL_IDX(ahd, scb->col_scb));
+	} else {
+		/*
+		 * The SCB we might collide with on the next allocation
+		 * is either active in a packetized context, or free.
+		 * Since we can't collide, put this SCB on the generic
+		 * free list.
+		 */
+		LIST_INSERT_HEAD(&ahd->scb_data.any_dev_free_scb_list,
+				 scb, links.le);
+	}
+
+	ahd_platform_scb_free(ahd, scb);
+}
+
+static void
+ahd_alloc_scbs(struct ahd_softc *ahd)
+{
+	struct scb_data *scb_data;
+	struct scb	*next_scb;
+	struct hardware_scb *hscb;
+	struct map_node *hscb_map;
+	struct map_node *sg_map;
+	struct map_node *sense_map;
+	uint8_t		*segs;
+	uint8_t		*sense_data;
+	dma_addr_t	 hscb_busaddr;
+	dma_addr_t	 sg_busaddr;
+	dma_addr_t	 sense_busaddr;
+	int		 newcount;
+	int		 i;
+
+	scb_data = &ahd->scb_data;
+	if (scb_data->numscbs >= AHD_SCB_MAX_ALLOC)
+		/* Can't allocate any more */
+		return;
+
+	if (scb_data->scbs_left != 0) {
+		int offset;
+
+		offset = (PAGE_SIZE / sizeof(*hscb)) - scb_data->scbs_left;
+		hscb_map = SLIST_FIRST(&scb_data->hscb_maps);
+		hscb = &((struct hardware_scb *)hscb_map->vaddr)[offset];
+		hscb_busaddr = hscb_map->physaddr + (offset * sizeof(*hscb));
+	} else {
+		hscb_map = kmalloc(sizeof(*hscb_map), GFP_ATOMIC);
+
+		if (hscb_map == NULL)
+			return;
+
+		/* Allocate the next batch of hardware SCBs */
+		if (ahd_dmamem_alloc(ahd, scb_data->hscb_dmat,
+				     (void **)&hscb_map->vaddr,
+				     BUS_DMA_NOWAIT, &hscb_map->dmamap) != 0) {
+			kfree(hscb_map);
+			return;
+		}
+
+		SLIST_INSERT_HEAD(&scb_data->hscb_maps, hscb_map, links);
+
+		ahd_dmamap_load(ahd, scb_data->hscb_dmat, hscb_map->dmamap,
+				hscb_map->vaddr, PAGE_SIZE, ahd_dmamap_cb,
+				&hscb_map->physaddr, /*flags*/0);
+
+		hscb = (struct hardware_scb *)hscb_map->vaddr;
+		hscb_busaddr = hscb_map->physaddr;
+		scb_data->scbs_left = PAGE_SIZE / sizeof(*hscb);
+	}
+
+	if (scb_data->sgs_left != 0) {
+		int offset;
+
+		offset = ((ahd_sglist_allocsize(ahd) / ahd_sglist_size(ahd))
+		       - scb_data->sgs_left) * ahd_sglist_size(ahd);
+		sg_map = SLIST_FIRST(&scb_data->sg_maps);
+		segs = sg_map->vaddr + offset;
+		sg_busaddr = sg_map->physaddr + offset;
+	} else {
+		sg_map = kmalloc(sizeof(*sg_map), GFP_ATOMIC);
+
+		if (sg_map == NULL)
+			return;
+
+		/* Allocate the next batch of S/G lists */
+		if (ahd_dmamem_alloc(ahd, scb_data->sg_dmat,
+				     (void **)&sg_map->vaddr,
+				     BUS_DMA_NOWAIT, &sg_map->dmamap) != 0) {
+			kfree(sg_map);
+			return;
+		}
+
+		SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+		ahd_dmamap_load(ahd, scb_data->sg_dmat, sg_map->dmamap,
+				sg_map->vaddr, ahd_sglist_allocsize(ahd),
+				ahd_dmamap_cb, &sg_map->physaddr, /*flags*/0);
+
+		segs = sg_map->vaddr;
+		sg_busaddr = sg_map->physaddr;
+		scb_data->sgs_left =
+		    ahd_sglist_allocsize(ahd) / ahd_sglist_size(ahd);
+#ifdef AHD_DEBUG
+		if (ahd_debug & AHD_SHOW_MEMORY)
+			printk("Mapped SG data\n");
+#endif
+	}
+
+	if (scb_data->sense_left != 0) {
+		int offset;
+
+		offset = PAGE_SIZE - (AHD_SENSE_BUFSIZE * scb_data->sense_left);
+		sense_map = SLIST_FIRST(&scb_data->sense_maps);
+		sense_data = sense_map->vaddr + offset;
+		sense_busaddr = sense_map->physaddr + offset;
+	} else {
+		sense_map = kmalloc(sizeof(*sense_map), GFP_ATOMIC);
+
+		if (sense_map == NULL)
+			return;
+
+		/* Allocate the next batch of sense buffers */
+		if (ahd_dmamem_alloc(ahd, scb_data->sense_dmat,
+				     (void **)&sense_map->vaddr,
+				     BUS_DMA_NOWAIT, &sense_map->dmamap) != 0) {
+			kfree(sense_map);
+			return;
+		}
+
+		SLIST_INSERT_HEAD(&scb_data->sense_maps, sense_map, links);
+
+		ahd_dmamap_load(ahd, scb_data->sense_dmat, sense_map->dmamap,
+				sense_map->vaddr, PAGE_SIZE, ahd_dmamap_cb,
+				&sense_map->physaddr, /*flags*/0);
+
+		sense_data = sense_map->vaddr;
+		sense_busaddr = sense_map->physaddr;
+		scb_data->sense_left = PAGE_SIZE / AHD_SENSE_BUFSIZE;
+#ifdef AHD_DEBUG
+		if (ahd_debug & AHD_SHOW_MEMORY)
+			printk("Mapped sense data\n");
+#endif
+	}
+
+	newcount = min(scb_data->sense_left, scb_data->scbs_left);
+	newcount = min(newcount, scb_data->sgs_left);
+	newcount = min(newcount, (AHD_SCB_MAX_ALLOC - scb_data->numscbs));
+	for (i = 0; i < newcount; i++) {
+		struct scb_platform_data *pdata;
+		u_int col_tag;
+#ifndef __linux__
+		int error;
+#endif
+
+		next_scb = kmalloc(sizeof(*next_scb), GFP_ATOMIC);
+		if (next_scb == NULL)
+			break;
+
+		pdata = kmalloc(sizeof(*pdata), GFP_ATOMIC);
+		if (pdata == NULL) {
+			kfree(next_scb);
+			break;
+		}
+		next_scb->platform_data = pdata;
+		next_scb->hscb_map = hscb_map;
+		next_scb->sg_map = sg_map;
+		next_scb->sense_map = sense_map;
+		next_scb->sg_list = segs;
+		next_scb->sense_data = sense_data;
+		next_scb->sense_busaddr = sense_busaddr;
+		memset(hscb, 0, sizeof(*hscb));
+		next_scb->hscb = hscb;
+		hscb->hscb_busaddr = ahd_htole32(hscb_busaddr);
+
+		/*
+		 * The sequencer always starts with the second entry.
+		 * The first entry is embedded in the scb.
+		 */
+		next_scb->sg_list_busaddr = sg_busaddr;
+		if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+			next_scb->sg_list_busaddr
+			    += sizeof(struct ahd_dma64_seg);
+		else
+			next_scb->sg_list_busaddr += sizeof(struct ahd_dma_seg);
+		next_scb->ahd_softc = ahd;
+		next_scb->flags = SCB_FLAG_NONE;
+#ifndef __linux__
+		error = ahd_dmamap_create(ahd, ahd->buffer_dmat, /*flags*/0,
+					  &next_scb->dmamap);
+		if (error != 0) {
+			kfree(next_scb);
+			kfree(pdata);
+			break;
+		}
+#endif
+		next_scb->hscb->tag = ahd_htole16(scb_data->numscbs);
+		col_tag = scb_data->numscbs ^ 0x100;
+		next_scb->col_scb = ahd_find_scb_by_tag(ahd, col_tag);
+		if (next_scb->col_scb != NULL)
+			next_scb->col_scb->col_scb = next_scb;
+		ahd_free_scb(ahd, next_scb);
+		hscb++;
+		hscb_busaddr += sizeof(*hscb);
+		segs += ahd_sglist_size(ahd);
+		sg_busaddr += ahd_sglist_size(ahd);
+		sense_data += AHD_SENSE_BUFSIZE;
+		sense_busaddr += AHD_SENSE_BUFSIZE;
+		scb_data->numscbs++;
+		scb_data->sense_left--;
+		scb_data->scbs_left--;
+		scb_data->sgs_left--;
+	}
+}
+
+void
+ahd_controller_info(struct ahd_softc *ahd, char *buf)
+{
+	const char *speed;
+	const char *type;
+	int len;
+
+	len = sprintf(buf, "%s: ", ahd_chip_names[ahd->chip & AHD_CHIPID_MASK]);
+	buf += len;
+
+	speed = "Ultra320 ";
+	if ((ahd->features & AHD_WIDE) != 0) {
+		type = "Wide ";
+	} else {
+		type = "Single ";
+	}
+	len = sprintf(buf, "%s%sChannel %c, SCSI Id=%d, ",
+		      speed, type, ahd->channel, ahd->our_id);
+	buf += len;
+
+	sprintf(buf, "%s, %d SCBs", ahd->bus_description,
+		ahd->scb_data.maxhscbs);
+}
+
+static const char *channel_strings[] = {
+	"Primary Low",
+	"Primary High",
+	"Secondary Low", 
+	"Secondary High"
+};
+
+static const char *termstat_strings[] = {
+	"Terminated Correctly",
+	"Over Terminated",
+	"Under Terminated",
+	"Not Configured"
+};
+
+/***************************** Timer Facilities *******************************/
+#define ahd_timer_init init_timer
+#define ahd_timer_stop del_timer_sync
+typedef void ahd_linux_callback_t (u_long);
+
+static void
+ahd_timer_reset(ahd_timer_t *timer, int usec, ahd_callback_t *func, void *arg)
+{
+	struct ahd_softc *ahd;
+
+	ahd = (struct ahd_softc *)arg;
+	del_timer(timer);
+	timer->data = (u_long)arg;
+	timer->expires = jiffies + (usec * HZ)/1000000;
+	timer->function = (ahd_linux_callback_t*)func;
+	add_timer(timer);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahd_init(struct ahd_softc *ahd)
+{
+	uint8_t		*next_vaddr;
+	dma_addr_t	 next_baddr;
+	size_t		 driver_data_size;
+	int		 i;
+	int		 error;
+	u_int		 warn_user;
+	uint8_t		 current_sensing;
+	uint8_t		 fstat;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+	ahd->stack_size = ahd_probe_stack_size(ahd);
+	ahd->saved_stack = kmalloc(ahd->stack_size * sizeof(uint16_t), GFP_ATOMIC);
+	if (ahd->saved_stack == NULL)
+		return (ENOMEM);
+
+	/*
+	 * Verify that the compiler hasn't over-aggressively
+	 * padded important structures.
+	 */
+	if (sizeof(struct hardware_scb) != 64)
+		panic("Hardware SCB size is incorrect");
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_DEBUG_SEQUENCER) != 0)
+		ahd->flags |= AHD_SEQUENCER_DEBUG;
+#endif
+
+	/*
+	 * Default to allowing initiator operations.
+	 */
+	ahd->flags |= AHD_INITIATORROLE;
+
+	/*
+	 * Only allow target mode features if this unit has them enabled.
+	 */
+	if ((AHD_TMODE_ENABLE & (0x1 << ahd->unit)) == 0)
+		ahd->features &= ~AHD_TARGETMODE;
+
+#ifndef __linux__
+	/* DMA tag for mapping buffers into device visible space. */
+	if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/ahd->flags & AHD_39BIT_ADDRESSING
+					? (dma_addr_t)0x7FFFFFFFFFULL
+					: BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       /*maxsize*/(AHD_NSEG - 1) * PAGE_SIZE,
+			       /*nsegments*/AHD_NSEG,
+			       /*maxsegsz*/AHD_MAXTRANSFER_SIZE,
+			       /*flags*/BUS_DMA_ALLOCNOW,
+			       &ahd->buffer_dmat) != 0) {
+		return (ENOMEM);
+	}
+#endif
+
+	ahd->init_level++;
+
+	/*
+	 * DMA tag for our command fifos and other data in system memory
+	 * the card's sequencer must be able to access.  For initiator
+	 * roles, we need to allocate space for the qoutfifo.  When providing
+	 * for the target mode role, we must additionally provide space for
+	 * the incoming target command fifo.
+	 */
+	driver_data_size = AHD_SCB_MAX * sizeof(*ahd->qoutfifo)
+			 + sizeof(struct hardware_scb);
+	if ((ahd->features & AHD_TARGETMODE) != 0)
+		driver_data_size += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+	if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0)
+		driver_data_size += PKT_OVERRUN_BUFSIZE;
+	if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       driver_data_size,
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &ahd->shared_data_dmat) != 0) {
+		return (ENOMEM);
+	}
+
+	ahd->init_level++;
+
+	/* Allocation of driver data */
+	if (ahd_dmamem_alloc(ahd, ahd->shared_data_dmat,
+			     (void **)&ahd->shared_data_map.vaddr,
+			     BUS_DMA_NOWAIT,
+			     &ahd->shared_data_map.dmamap) != 0) {
+		return (ENOMEM);
+	}
+
+	ahd->init_level++;
+
+	/* And permanently map it in */
+	ahd_dmamap_load(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap,
+			ahd->shared_data_map.vaddr, driver_data_size,
+			ahd_dmamap_cb, &ahd->shared_data_map.physaddr,
+			/*flags*/0);
+	ahd->qoutfifo = (struct ahd_completion *)ahd->shared_data_map.vaddr;
+	next_vaddr = (uint8_t *)&ahd->qoutfifo[AHD_QOUT_SIZE];
+	next_baddr = ahd->shared_data_map.physaddr
+		   + AHD_QOUT_SIZE*sizeof(struct ahd_completion);
+	if ((ahd->features & AHD_TARGETMODE) != 0) {
+		ahd->targetcmds = (struct target_cmd *)next_vaddr;
+		next_vaddr += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+		next_baddr += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+	}
+
+	if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+		ahd->overrun_buf = next_vaddr;
+		next_vaddr += PKT_OVERRUN_BUFSIZE;
+		next_baddr += PKT_OVERRUN_BUFSIZE;
+	}
+
+	/*
+	 * We need one SCB to serve as the "next SCB".  Since the
+	 * tag identifier in this SCB will never be used, there is
+	 * no point in using a valid HSCB tag from an SCB pulled from
+	 * the standard free pool.  So, we allocate this "sentinel"
+	 * specially from the DMA safe memory chunk used for the QOUTFIFO.
+	 */
+	ahd->next_queued_hscb = (struct hardware_scb *)next_vaddr;
+	ahd->next_queued_hscb_map = &ahd->shared_data_map;
+	ahd->next_queued_hscb->hscb_busaddr = ahd_htole32(next_baddr);
+
+	ahd->init_level++;
+
+	/* Allocate SCB data now that buffer_dmat is initialized */
+	if (ahd_init_scbdata(ahd) != 0)
+		return (ENOMEM);
+
+	if ((ahd->flags & AHD_INITIATORROLE) == 0)
+		ahd->flags &= ~AHD_RESET_BUS_A;
+
+	/*
+	 * Before committing these settings to the chip, give
+	 * the OSM one last chance to modify our configuration.
+	 */
+	ahd_platform_init(ahd);
+
+	/* Bring up the chip. */
+	ahd_chip_init(ahd);
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+	if ((ahd->flags & AHD_CURRENT_SENSING) == 0)
+		goto init_done;
+
+	/*
+	 * Verify termination based on current draw and
+	 * warn user if the bus is over/under terminated.
+	 */
+	error = ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL,
+				   CURSENSE_ENB);
+	if (error != 0) {
+		printk("%s: current sensing timeout 1\n", ahd_name(ahd));
+		goto init_done;
+	}
+	for (i = 20, fstat = FLX_FSTAT_BUSY;
+	     (fstat & FLX_FSTAT_BUSY) != 0 && i; i--) {
+		error = ahd_read_flexport(ahd, FLXADDR_FLEXSTAT, &fstat);
+		if (error != 0) {
+			printk("%s: current sensing timeout 2\n",
+			       ahd_name(ahd));
+			goto init_done;
+		}
+	}
+	if (i == 0) {
+		printk("%s: Timedout during current-sensing test\n",
+		       ahd_name(ahd));
+		goto init_done;
+	}
+
+	/* Latch Current Sensing status. */
+	error = ahd_read_flexport(ahd, FLXADDR_CURRENT_STAT, &current_sensing);
+	if (error != 0) {
+		printk("%s: current sensing timeout 3\n", ahd_name(ahd));
+		goto init_done;
+	}
+
+	/* Diable current sensing. */
+	ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, 0);
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_TERMCTL) != 0) {
+		printk("%s: current_sensing == 0x%x\n",
+		       ahd_name(ahd), current_sensing);
+	}
+#endif
+	warn_user = 0;
+	for (i = 0; i < 4; i++, current_sensing >>= FLX_CSTAT_SHIFT) {
+		u_int term_stat;
+
+		term_stat = (current_sensing & FLX_CSTAT_MASK);
+		switch (term_stat) {
+		case FLX_CSTAT_OVER:
+		case FLX_CSTAT_UNDER:
+			warn_user++;
+		case FLX_CSTAT_INVALID:
+		case FLX_CSTAT_OKAY:
+			if (warn_user == 0 && bootverbose == 0)
+				break;
+			printk("%s: %s Channel %s\n", ahd_name(ahd),
+			       channel_strings[i], termstat_strings[term_stat]);
+			break;
+		}
+	}
+	if (warn_user) {
+		printk("%s: WARNING. Termination is not configured correctly.\n"
+		       "%s: WARNING. SCSI bus operations may FAIL.\n",
+		       ahd_name(ahd), ahd_name(ahd));
+	}
+init_done:
+	ahd_restart(ahd);
+	ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US,
+			ahd_stat_timer, ahd);
+	return (0);
+}
+
+/*
+ * (Re)initialize chip state after a chip reset.
+ */
+static void
+ahd_chip_init(struct ahd_softc *ahd)
+{
+	uint32_t busaddr;
+	u_int	 sxfrctl1;
+	u_int	 scsiseq_template;
+	u_int	 wait;
+	u_int	 i;
+	u_int	 target;
+
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	/*
+	 * Take the LED out of diagnostic mode
+	 */
+	ahd_outb(ahd, SBLKCTL, ahd_inb(ahd, SBLKCTL) & ~(DIAGLEDEN|DIAGLEDON));
+
+	/*
+	 * Return HS_MAILBOX to its default value.
+	 */
+	ahd->hs_mailbox = 0;
+	ahd_outb(ahd, HS_MAILBOX, 0);
+
+	/* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1. */
+	ahd_outb(ahd, IOWNID, ahd->our_id);
+	ahd_outb(ahd, TOWNID, ahd->our_id);
+	sxfrctl1 = (ahd->flags & AHD_TERM_ENB_A) != 0 ? STPWEN : 0;
+	sxfrctl1 |= (ahd->flags & AHD_SPCHK_ENB_A) != 0 ? ENSPCHK : 0;
+	if ((ahd->bugs & AHD_LONG_SETIMO_BUG)
+	 && (ahd->seltime != STIMESEL_MIN)) {
+		/*
+		 * The selection timer duration is twice as long
+		 * as it should be.  Halve it by adding "1" to
+		 * the user specified setting.
+		 */
+		sxfrctl1 |= ahd->seltime + STIMESEL_BUG_ADJ;
+	} else {
+		sxfrctl1 |= ahd->seltime;
+	}
+		
+	ahd_outb(ahd, SXFRCTL0, DFON);
+	ahd_outb(ahd, SXFRCTL1, sxfrctl1|ahd->seltime|ENSTIMER|ACTNEGEN);
+	ahd_outb(ahd, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+
+	/*
+	 * Now that termination is set, wait for up
+	 * to 500ms for our transceivers to settle.  If
+	 * the adapter does not have a cable attached,
+	 * the transceivers may never settle, so don't
+	 * complain if we fail here.
+	 */
+	for (wait = 10000;
+	     (ahd_inb(ahd, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+	     wait--)
+		ahd_delay(100);
+
+	/* Clear any false bus resets due to the transceivers settling */
+	ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
+	ahd_outb(ahd, CLRINT, CLRSCSIINT);
+
+	/* Initialize mode specific S/G state. */
+	for (i = 0; i < 2; i++) {
+		ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+		ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+		ahd_outb(ahd, SG_STATE, 0);
+		ahd_outb(ahd, CLRSEQINTSRC, 0xFF);
+		ahd_outb(ahd, SEQIMODE,
+			 ENSAVEPTRS|ENCFG4DATA|ENCFG4ISTAT
+			|ENCFG4TSTAT|ENCFG4ICMD|ENCFG4TCMD);
+	}
+
+	ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+	ahd_outb(ahd, DSCOMMAND0, ahd_inb(ahd, DSCOMMAND0)|MPARCKEN|CACHETHEN);
+	ahd_outb(ahd, DFF_THRSH, RD_DFTHRSH_75|WR_DFTHRSH_75);
+	ahd_outb(ahd, SIMODE0, ENIOERR|ENOVERRUN);
+	ahd_outb(ahd, SIMODE3, ENNTRAMPERR|ENOSRAMPERR);
+	if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
+		ahd_outb(ahd, OPTIONMODE, AUTOACKEN|AUTO_MSGOUT_DE);
+	} else {
+		ahd_outb(ahd, OPTIONMODE, AUTOACKEN|BUSFREEREV|AUTO_MSGOUT_DE);
+	}
+	ahd_outb(ahd, SCSCHKN, CURRFIFODEF|WIDERESEN|SHVALIDSTDIS);
+	if ((ahd->chip & AHD_BUS_MASK) == AHD_PCIX)
+		/*
+		 * Do not issue a target abort when a split completion
+		 * error occurs.  Let our PCIX interrupt handler deal
+		 * with it instead. H2A4 Razor #625
+		 */
+		ahd_outb(ahd, PCIXCTL, ahd_inb(ahd, PCIXCTL) | SPLTSTADIS);
+
+	if ((ahd->bugs & AHD_LQOOVERRUN_BUG) != 0)
+		ahd_outb(ahd, LQOSCSCTL, LQONOCHKOVER);
+
+	/*
+	 * Tweak IOCELL settings.
+	 */
+	if ((ahd->flags & AHD_HP_BOARD) != 0) {
+		for (i = 0; i < NUMDSPS; i++) {
+			ahd_outb(ahd, DSPSELECT, i);
+			ahd_outb(ahd, WRTBIASCTL, WRTBIASCTL_HP_DEFAULT);
+		}
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MISC) != 0)
+			printk("%s: WRTBIASCTL now 0x%x\n", ahd_name(ahd),
+			       WRTBIASCTL_HP_DEFAULT);
+#endif
+	}
+	ahd_setup_iocell_workaround(ahd);
+
+	/*
+	 * Enable LQI Manager interrupts.
+	 */
+	ahd_outb(ahd, LQIMODE1, ENLQIPHASE_LQ|ENLQIPHASE_NLQ|ENLIQABORT
+			      | ENLQICRCI_LQ|ENLQICRCI_NLQ|ENLQIBADLQI
+			      | ENLQIOVERI_LQ|ENLQIOVERI_NLQ);
+	ahd_outb(ahd, LQOMODE0, ENLQOATNLQ|ENLQOATNPKT|ENLQOTCRC);
+	/*
+	 * We choose to have the sequencer catch LQOPHCHGINPKT errors
+	 * manually for the command phase at the start of a packetized
+	 * selection case.  ENLQOBUSFREE should be made redundant by
+	 * the BUSFREE interrupt, but it seems that some LQOBUSFREE
+	 * events fail to assert the BUSFREE interrupt so we must
+	 * also enable LQOBUSFREE interrupts.
+	 */
+	ahd_outb(ahd, LQOMODE1, ENLQOBUSFREE);
+
+	/*
+	 * Setup sequencer interrupt handlers.
+	 */
+	ahd_outw(ahd, INTVEC1_ADDR, ahd_resolve_seqaddr(ahd, LABEL_seq_isr));
+	ahd_outw(ahd, INTVEC2_ADDR, ahd_resolve_seqaddr(ahd, LABEL_timer_isr));
+
+	/*
+	 * Setup SCB Offset registers.
+	 */
+	if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+		ahd_outb(ahd, LUNPTR, offsetof(struct hardware_scb,
+			 pkt_long_lun));
+	} else {
+		ahd_outb(ahd, LUNPTR, offsetof(struct hardware_scb, lun));
+	}
+	ahd_outb(ahd, CMDLENPTR, offsetof(struct hardware_scb, cdb_len));
+	ahd_outb(ahd, ATTRPTR, offsetof(struct hardware_scb, task_attribute));
+	ahd_outb(ahd, FLAGPTR, offsetof(struct hardware_scb, task_management));
+	ahd_outb(ahd, CMDPTR, offsetof(struct hardware_scb,
+				       shared_data.idata.cdb));
+	ahd_outb(ahd, QNEXTPTR,
+		 offsetof(struct hardware_scb, next_hscb_busaddr));
+	ahd_outb(ahd, ABRTBITPTR, MK_MESSAGE_BIT_OFFSET);
+	ahd_outb(ahd, ABRTBYTEPTR, offsetof(struct hardware_scb, control));
+	if ((ahd->bugs & AHD_PKT_LUN_BUG) != 0) {
+		ahd_outb(ahd, LUNLEN,
+			 sizeof(ahd->next_queued_hscb->pkt_long_lun) - 1);
+	} else {
+		ahd_outb(ahd, LUNLEN, LUNLEN_SINGLE_LEVEL_LUN);
+	}
+	ahd_outb(ahd, CDBLIMIT, SCB_CDB_LEN_PTR - 1);
+	ahd_outb(ahd, MAXCMD, 0xFF);
+	ahd_outb(ahd, SCBAUTOPTR,
+		 AUSCBPTR_EN | offsetof(struct hardware_scb, tag));
+
+	/* We haven't been enabled for target mode yet. */
+	ahd_outb(ahd, MULTARGID, 0);
+	ahd_outb(ahd, MULTARGID + 1, 0);
+
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	/* Initialize the negotiation table. */
+	if ((ahd->features & AHD_NEW_IOCELL_OPTS) == 0) {
+		/*
+		 * Clear the spare bytes in the neg table to avoid
+		 * spurious parity errors.
+		 */
+		for (target = 0; target < AHD_NUM_TARGETS; target++) {
+			ahd_outb(ahd, NEGOADDR, target);
+			ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PER_DEV0);
+			for (i = 0; i < AHD_NUM_PER_DEV_ANNEXCOLS; i++)
+				ahd_outb(ahd, ANNEXDAT, 0);
+		}
+	}
+	for (target = 0; target < AHD_NUM_TARGETS; target++) {
+		struct	 ahd_devinfo devinfo;
+		struct	 ahd_initiator_tinfo *tinfo;
+		struct	 ahd_tmode_tstate *tstate;
+
+		tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+					    target, &tstate);
+		ahd_compile_devinfo(&devinfo, ahd->our_id,
+				    target, CAM_LUN_WILDCARD,
+				    'A', ROLE_INITIATOR);
+		ahd_update_neg_table(ahd, &devinfo, &tinfo->curr);
+	}
+
+	ahd_outb(ahd, CLRSINT3, NTRAMPERR|OSRAMPERR);
+	ahd_outb(ahd, CLRINT, CLRSCSIINT);
+
+#ifdef NEEDS_MORE_TESTING
+	/*
+	 * Always enable abort on incoming L_Qs if this feature is
+	 * supported.  We use this to catch invalid SCB references.
+	 */
+	if ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0)
+		ahd_outb(ahd, LQCTL1, ABORTPENDING);
+	else
+#endif
+		ahd_outb(ahd, LQCTL1, 0);
+
+	/* All of our queues are empty */
+	ahd->qoutfifonext = 0;
+	ahd->qoutfifonext_valid_tag = QOUTFIFO_ENTRY_VALID;
+	ahd_outb(ahd, QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID);
+	for (i = 0; i < AHD_QOUT_SIZE; i++)
+		ahd->qoutfifo[i].valid_tag = 0;
+	ahd_sync_qoutfifo(ahd, BUS_DMASYNC_PREREAD);
+
+	ahd->qinfifonext = 0;
+	for (i = 0; i < AHD_QIN_SIZE; i++)
+		ahd->qinfifo[i] = SCB_LIST_NULL;
+
+	if ((ahd->features & AHD_TARGETMODE) != 0) {
+		/* All target command blocks start out invalid. */
+		for (i = 0; i < AHD_TMODE_CMDS; i++)
+			ahd->targetcmds[i].cmd_valid = 0;
+		ahd_sync_tqinfifo(ahd, BUS_DMASYNC_PREREAD);
+		ahd->tqinfifonext = 1;
+		ahd_outb(ahd, KERNEL_TQINPOS, ahd->tqinfifonext - 1);
+		ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);
+	}
+
+	/* Initialize Scratch Ram. */
+	ahd_outb(ahd, SEQ_FLAGS, 0);
+	ahd_outb(ahd, SEQ_FLAGS2, 0);
+
+	/* We don't have any waiting selections */
+	ahd_outw(ahd, WAITING_TID_HEAD, SCB_LIST_NULL);
+	ahd_outw(ahd, WAITING_TID_TAIL, SCB_LIST_NULL);
+	ahd_outw(ahd, MK_MESSAGE_SCB, SCB_LIST_NULL);
+	ahd_outw(ahd, MK_MESSAGE_SCSIID, 0xFF);
+	for (i = 0; i < AHD_NUM_TARGETS; i++)
+		ahd_outw(ahd, WAITING_SCB_TAILS + (2 * i), SCB_LIST_NULL);
+
+	/*
+	 * Nobody is waiting to be DMAed into the QOUTFIFO.
+	 */
+	ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);
+	ahd_outw(ahd, COMPLETE_SCB_DMAINPROG_HEAD, SCB_LIST_NULL);
+	ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
+	ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, SCB_LIST_NULL);
+	ahd_outw(ahd, COMPLETE_ON_QFREEZE_HEAD, SCB_LIST_NULL);
+
+	/*
+	 * The Freeze Count is 0.
+	 */
+	ahd->qfreeze_cnt = 0;
+	ahd_outw(ahd, QFREEZE_COUNT, 0);
+	ahd_outw(ahd, KERNEL_QFREEZE_COUNT, 0);
+
+	/*
+	 * Tell the sequencer where it can find our arrays in memory.
+	 */
+	busaddr = ahd->shared_data_map.physaddr;
+	ahd_outl(ahd, SHARED_DATA_ADDR, busaddr);
+	ahd_outl(ahd, QOUTFIFO_NEXT_ADDR, busaddr);
+
+	/*
+	 * Setup the allowed SCSI Sequences based on operational mode.
+	 * If we are a target, we'll enable select in operations once
+	 * we've had a lun enabled.
+	 */
+	scsiseq_template = ENAUTOATNP;
+	if ((ahd->flags & AHD_INITIATORROLE) != 0)
+		scsiseq_template |= ENRSELI;
+	ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq_template);
+
+	/* There are no busy SCBs yet. */
+	for (target = 0; target < AHD_NUM_TARGETS; target++) {
+		int lun;
+
+		for (lun = 0; lun < AHD_NUM_LUNS_NONPKT; lun++)
+			ahd_unbusy_tcl(ahd, BUILD_TCL_RAW(target, 'A', lun));
+	}
+
+	/*
+	 * Initialize the group code to command length table.
+	 * Vendor Unique codes are set to 0 so we only capture
+	 * the first byte of the cdb.  These can be overridden
+	 * when target mode is enabled.
+	 */
+	ahd_outb(ahd, CMDSIZE_TABLE, 5);
+	ahd_outb(ahd, CMDSIZE_TABLE + 1, 9);
+	ahd_outb(ahd, CMDSIZE_TABLE + 2, 9);
+	ahd_outb(ahd, CMDSIZE_TABLE + 3, 0);
+	ahd_outb(ahd, CMDSIZE_TABLE + 4, 15);
+	ahd_outb(ahd, CMDSIZE_TABLE + 5, 11);
+	ahd_outb(ahd, CMDSIZE_TABLE + 6, 0);
+	ahd_outb(ahd, CMDSIZE_TABLE + 7, 0);
+		
+	/* Tell the sequencer of our initial queue positions */
+	ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+	ahd_outb(ahd, QOFF_CTLSTA, SCB_QSIZE_512);
+	ahd->qinfifonext = 0;
+	ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+	ahd_set_hescb_qoff(ahd, 0);
+	ahd_set_snscb_qoff(ahd, 0);
+	ahd_set_sescb_qoff(ahd, 0);
+	ahd_set_sdscb_qoff(ahd, 0);
+
+	/*
+	 * Tell the sequencer which SCB will be the next one it receives.
+	 */
+	busaddr = ahd_le32toh(ahd->next_queued_hscb->hscb_busaddr);
+	ahd_outl(ahd, NEXT_QUEUED_SCB_ADDR, busaddr);
+
+	/*
+	 * Default to coalescing disabled.
+	 */
+	ahd_outw(ahd, INT_COALESCING_CMDCOUNT, 0);
+	ahd_outw(ahd, CMDS_PENDING, 0);
+	ahd_update_coalescing_values(ahd, ahd->int_coalescing_timer,
+				     ahd->int_coalescing_maxcmds,
+				     ahd->int_coalescing_mincmds);
+	ahd_enable_coalescing(ahd, FALSE);
+
+	ahd_loadseq(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+	if (ahd->features & AHD_AIC79XXB_SLOWCRC) {
+		u_int negodat3 = ahd_inb(ahd, NEGCONOPTS);
+
+		negodat3 |= ENSLOWCRC;
+		ahd_outb(ahd, NEGCONOPTS, negodat3);
+		negodat3 = ahd_inb(ahd, NEGCONOPTS);
+		if (!(negodat3 & ENSLOWCRC))
+			printk("aic79xx: failed to set the SLOWCRC bit\n");
+		else
+			printk("aic79xx: SLOWCRC bit set\n");
+	}
+}
+
+/*
+ * Setup default device and controller settings.
+ * This should only be called if our probe has
+ * determined that no configuration data is available.
+ */
+int
+ahd_default_config(struct ahd_softc *ahd)
+{
+	int	targ;
+
+	ahd->our_id = 7;
+
+	/*
+	 * Allocate a tstate to house information for our
+	 * initiator presence on the bus as well as the user
+	 * data for any target mode initiator.
+	 */
+	if (ahd_alloc_tstate(ahd, ahd->our_id, 'A') == NULL) {
+		printk("%s: unable to allocate ahd_tmode_tstate.  "
+		       "Failing attach\n", ahd_name(ahd));
+		return (ENOMEM);
+	}
+
+	for (targ = 0; targ < AHD_NUM_TARGETS; targ++) {
+		struct	 ahd_devinfo devinfo;
+		struct	 ahd_initiator_tinfo *tinfo;
+		struct	 ahd_tmode_tstate *tstate;
+		uint16_t target_mask;
+
+		tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+					    targ, &tstate);
+		/*
+		 * We support SPC2 and SPI4.
+		 */
+		tinfo->user.protocol_version = 4;
+		tinfo->user.transport_version = 4;
+
+		target_mask = 0x01 << targ;
+		ahd->user_discenable |= target_mask;
+		tstate->discenable |= target_mask;
+		ahd->user_tagenable |= target_mask;
+#ifdef AHD_FORCE_160
+		tinfo->user.period = AHD_SYNCRATE_DT;
+#else
+		tinfo->user.period = AHD_SYNCRATE_160;
+#endif
+		tinfo->user.offset = MAX_OFFSET;
+		tinfo->user.ppr_options = MSG_EXT_PPR_RD_STRM
+					| MSG_EXT_PPR_WR_FLOW
+					| MSG_EXT_PPR_HOLD_MCS
+					| MSG_EXT_PPR_IU_REQ
+					| MSG_EXT_PPR_QAS_REQ
+					| MSG_EXT_PPR_DT_REQ;
+		if ((ahd->features & AHD_RTI) != 0)
+			tinfo->user.ppr_options |= MSG_EXT_PPR_RTI;
+
+		tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+		/*
+		 * Start out Async/Narrow/Untagged and with
+		 * conservative protocol support.
+		 */
+		tinfo->goal.protocol_version = 2;
+		tinfo->goal.transport_version = 2;
+		tinfo->curr.protocol_version = 2;
+		tinfo->curr.transport_version = 2;
+		ahd_compile_devinfo(&devinfo, ahd->our_id,
+				    targ, CAM_LUN_WILDCARD,
+				    'A', ROLE_INITIATOR);
+		tstate->tagenable &= ~target_mask;
+		ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+			      AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE);
+		ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0,
+				 /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL,
+				 /*paused*/TRUE);
+	}
+	return (0);
+}
+
+/*
+ * Parse device configuration information.
+ */
+int
+ahd_parse_cfgdata(struct ahd_softc *ahd, struct seeprom_config *sc)
+{
+	int targ;
+	int max_targ;
+
+	max_targ = sc->max_targets & CFMAXTARG;
+	ahd->our_id = sc->brtime_id & CFSCSIID;
+
+	/*
+	 * Allocate a tstate to house information for our
+	 * initiator presence on the bus as well as the user
+	 * data for any target mode initiator.
+	 */
+	if (ahd_alloc_tstate(ahd, ahd->our_id, 'A') == NULL) {
+		printk("%s: unable to allocate ahd_tmode_tstate.  "
+		       "Failing attach\n", ahd_name(ahd));
+		return (ENOMEM);
+	}
+
+	for (targ = 0; targ < max_targ; targ++) {
+		struct	 ahd_devinfo devinfo;
+		struct	 ahd_initiator_tinfo *tinfo;
+		struct	 ahd_transinfo *user_tinfo;
+		struct	 ahd_tmode_tstate *tstate;
+		uint16_t target_mask;
+
+		tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+					    targ, &tstate);
+		user_tinfo = &tinfo->user;
+
+		/*
+		 * We support SPC2 and SPI4.
+		 */
+		tinfo->user.protocol_version = 4;
+		tinfo->user.transport_version = 4;
+
+		target_mask = 0x01 << targ;
+		ahd->user_discenable &= ~target_mask;
+		tstate->discenable &= ~target_mask;
+		ahd->user_tagenable &= ~target_mask;
+		if (sc->device_flags[targ] & CFDISC) {
+			tstate->discenable |= target_mask;
+			ahd->user_discenable |= target_mask;
+			ahd->user_tagenable |= target_mask;
+		} else {
+			/*
+			 * Cannot be packetized without disconnection.
+			 */
+			sc->device_flags[targ] &= ~CFPACKETIZED;
+		}
+
+		user_tinfo->ppr_options = 0;
+		user_tinfo->period = (sc->device_flags[targ] & CFXFER);
+		if (user_tinfo->period < CFXFER_ASYNC) {
+			if (user_tinfo->period <= AHD_PERIOD_10MHz)
+				user_tinfo->ppr_options |= MSG_EXT_PPR_DT_REQ;
+			user_tinfo->offset = MAX_OFFSET;
+		} else  {
+			user_tinfo->offset = 0;
+			user_tinfo->period = AHD_ASYNC_XFER_PERIOD;
+		}
+#ifdef AHD_FORCE_160
+		if (user_tinfo->period <= AHD_SYNCRATE_160)
+			user_tinfo->period = AHD_SYNCRATE_DT;
+#endif
+
+		if ((sc->device_flags[targ] & CFPACKETIZED) != 0) {
+			user_tinfo->ppr_options |= MSG_EXT_PPR_RD_STRM
+						|  MSG_EXT_PPR_WR_FLOW
+						|  MSG_EXT_PPR_HOLD_MCS
+						|  MSG_EXT_PPR_IU_REQ;
+			if ((ahd->features & AHD_RTI) != 0)
+				user_tinfo->ppr_options |= MSG_EXT_PPR_RTI;
+		}
+
+		if ((sc->device_flags[targ] & CFQAS) != 0)
+			user_tinfo->ppr_options |= MSG_EXT_PPR_QAS_REQ;
+
+		if ((sc->device_flags[targ] & CFWIDEB) != 0)
+			user_tinfo->width = MSG_EXT_WDTR_BUS_16_BIT;
+		else
+			user_tinfo->width = MSG_EXT_WDTR_BUS_8_BIT;
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_MISC) != 0)
+			printk("(%d): %x:%x:%x:%x\n", targ, user_tinfo->width,
+			       user_tinfo->period, user_tinfo->offset,
+			       user_tinfo->ppr_options);
+#endif
+		/*
+		 * Start out Async/Narrow/Untagged and with
+		 * conservative protocol support.
+		 */
+		tstate->tagenable &= ~target_mask;
+		tinfo->goal.protocol_version = 2;
+		tinfo->goal.transport_version = 2;
+		tinfo->curr.protocol_version = 2;
+		tinfo->curr.transport_version = 2;
+		ahd_compile_devinfo(&devinfo, ahd->our_id,
+				    targ, CAM_LUN_WILDCARD,
+				    'A', ROLE_INITIATOR);
+		ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+			      AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE);
+		ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0,
+				 /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL,
+				 /*paused*/TRUE);
+	}
+
+	ahd->flags &= ~AHD_SPCHK_ENB_A;
+	if (sc->bios_control & CFSPARITY)
+		ahd->flags |= AHD_SPCHK_ENB_A;
+
+	ahd->flags &= ~AHD_RESET_BUS_A;
+	if (sc->bios_control & CFRESETB)
+		ahd->flags |= AHD_RESET_BUS_A;
+
+	ahd->flags &= ~AHD_EXTENDED_TRANS_A;
+	if (sc->bios_control & CFEXTEND)
+		ahd->flags |= AHD_EXTENDED_TRANS_A;
+
+	ahd->flags &= ~AHD_BIOS_ENABLED;
+	if ((sc->bios_control & CFBIOSSTATE) == CFBS_ENABLED)
+		ahd->flags |= AHD_BIOS_ENABLED;
+
+	ahd->flags &= ~AHD_STPWLEVEL_A;
+	if ((sc->adapter_control & CFSTPWLEVEL) != 0)
+		ahd->flags |= AHD_STPWLEVEL_A;
+
+	return (0);
+}
+
+/*
+ * Parse device configuration information.
+ */
+int
+ahd_parse_vpddata(struct ahd_softc *ahd, struct vpd_config *vpd)
+{
+	int error;
+
+	error = ahd_verify_vpd_cksum(vpd);
+	if (error == 0)
+		return (EINVAL);
+	if ((vpd->bios_flags & VPDBOOTHOST) != 0)
+		ahd->flags |= AHD_BOOT_CHANNEL;
+	return (0);
+}
+
+void
+ahd_intr_enable(struct ahd_softc *ahd, int enable)
+{
+	u_int hcntrl;
+
+	hcntrl = ahd_inb(ahd, HCNTRL);
+	hcntrl &= ~INTEN;
+	ahd->pause &= ~INTEN;
+	ahd->unpause &= ~INTEN;
+	if (enable) {
+		hcntrl |= INTEN;
+		ahd->pause |= INTEN;
+		ahd->unpause |= INTEN;
+	}
+	ahd_outb(ahd, HCNTRL, hcntrl);
+}
+
+static void
+ahd_update_coalescing_values(struct ahd_softc *ahd, u_int timer, u_int maxcmds,
+			     u_int mincmds)
+{
+	if (timer > AHD_TIMER_MAX_US)
+		timer = AHD_TIMER_MAX_US;
+	ahd->int_coalescing_timer = timer;
+
+	if (maxcmds > AHD_INT_COALESCING_MAXCMDS_MAX)
+		maxcmds = AHD_INT_COALESCING_MAXCMDS_MAX;
+	if (mincmds > AHD_INT_COALESCING_MINCMDS_MAX)
+		mincmds = AHD_INT_COALESCING_MINCMDS_MAX;
+	ahd->int_coalescing_maxcmds = maxcmds;
+	ahd_outw(ahd, INT_COALESCING_TIMER, timer / AHD_TIMER_US_PER_TICK);
+	ahd_outb(ahd, INT_COALESCING_MAXCMDS, -maxcmds);
+	ahd_outb(ahd, INT_COALESCING_MINCMDS, -mincmds);
+}
+
+static void
+ahd_enable_coalescing(struct ahd_softc *ahd, int enable)
+{
+
+	ahd->hs_mailbox &= ~ENINT_COALESCE;
+	if (enable)
+		ahd->hs_mailbox |= ENINT_COALESCE;
+	ahd_outb(ahd, HS_MAILBOX, ahd->hs_mailbox);
+	ahd_flush_device_writes(ahd);
+	ahd_run_qoutfifo(ahd);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahd_pause_and_flushwork(struct ahd_softc *ahd)
+{
+	u_int intstat;
+	u_int maxloops;
+
+	maxloops = 1000;
+	ahd->flags |= AHD_ALL_INTERRUPTS;
+	ahd_pause(ahd);
+	/*
+	 * Freeze the outgoing selections.  We do this only
+	 * until we are safely paused without further selections
+	 * pending.
+	 */
+	ahd->qfreeze_cnt--;
+	ahd_outw(ahd, KERNEL_QFREEZE_COUNT, ahd->qfreeze_cnt);
+	ahd_outb(ahd, SEQ_FLAGS2, ahd_inb(ahd, SEQ_FLAGS2) | SELECTOUT_QFROZEN);
+	do {
+
+		ahd_unpause(ahd);
+		/*
+		 * Give the sequencer some time to service
+		 * any active selections.
+		 */
+		ahd_delay(500);
+
+		ahd_intr(ahd);
+		ahd_pause(ahd);
+		intstat = ahd_inb(ahd, INTSTAT);
+		if ((intstat & INT_PEND) == 0) {
+			ahd_clear_critical_section(ahd);
+			intstat = ahd_inb(ahd, INTSTAT);
+		}
+	} while (--maxloops
+	      && (intstat != 0xFF || (ahd->features & AHD_REMOVABLE) == 0)
+	      && ((intstat & INT_PEND) != 0
+	       || (ahd_inb(ahd, SCSISEQ0) & ENSELO) != 0
+	       || (ahd_inb(ahd, SSTAT0) & (SELDO|SELINGO)) != 0));
+
+	if (maxloops == 0) {
+		printk("Infinite interrupt loop, INTSTAT = %x",
+		      ahd_inb(ahd, INTSTAT));
+	}
+	ahd->qfreeze_cnt++;
+	ahd_outw(ahd, KERNEL_QFREEZE_COUNT, ahd->qfreeze_cnt);
+
+	ahd_flush_qoutfifo(ahd);
+
+	ahd->flags &= ~AHD_ALL_INTERRUPTS;
+}
+
+#ifdef CONFIG_PM
+int
+ahd_suspend(struct ahd_softc *ahd)
+{
+
+	ahd_pause_and_flushwork(ahd);
+
+	if (LIST_FIRST(&ahd->pending_scbs) != NULL) {
+		ahd_unpause(ahd);
+		return (EBUSY);
+	}
+	ahd_shutdown(ahd);
+	return (0);
+}
+
+void
+ahd_resume(struct ahd_softc *ahd)
+{
+
+	ahd_reset(ahd, /*reinit*/TRUE);
+	ahd_intr_enable(ahd, TRUE); 
+	ahd_restart(ahd);
+}
+#endif
+
+/************************** Busy Target Table *********************************/
+/*
+ * Set SCBPTR to the SCB that contains the busy
+ * table entry for TCL.  Return the offset into
+ * the SCB that contains the entry for TCL.
+ * saved_scbid is dereferenced and set to the
+ * scbid that should be restored once manipualtion
+ * of the TCL entry is complete.
+ */
+static inline u_int
+ahd_index_busy_tcl(struct ahd_softc *ahd, u_int *saved_scbid, u_int tcl)
+{
+	/*
+	 * Index to the SCB that contains the busy entry.
+	 */
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	*saved_scbid = ahd_get_scbptr(ahd);
+	ahd_set_scbptr(ahd, TCL_LUN(tcl)
+		     | ((TCL_TARGET_OFFSET(tcl) & 0xC) << 4));
+
+	/*
+	 * And now calculate the SCB offset to the entry.
+	 * Each entry is 2 bytes wide, hence the
+	 * multiplication by 2.
+	 */
+	return (((TCL_TARGET_OFFSET(tcl) & 0x3) << 1) + SCB_DISCONNECTED_LISTS);
+}
+
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ */
+static u_int
+ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl)
+{
+	u_int scbid;
+	u_int scb_offset;
+	u_int saved_scbptr;
+		
+	scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
+	scbid = ahd_inw_scbram(ahd, scb_offset);
+	ahd_set_scbptr(ahd, saved_scbptr);
+	return (scbid);
+}
+
+static void
+ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl, u_int scbid)
+{
+	u_int scb_offset;
+	u_int saved_scbptr;
+		
+	scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
+	ahd_outw(ahd, scb_offset, scbid);
+	ahd_set_scbptr(ahd, saved_scbptr);
+}
+
+/************************** SCB and SCB queue management **********************/
+static int
+ahd_match_scb(struct ahd_softc *ahd, struct scb *scb, int target,
+	      char channel, int lun, u_int tag, role_t role)
+{
+	int targ = SCB_GET_TARGET(ahd, scb);
+	char chan = SCB_GET_CHANNEL(ahd, scb);
+	int slun = SCB_GET_LUN(scb);
+	int match;
+
+	match = ((chan == channel) || (channel == ALL_CHANNELS));
+	if (match != 0)
+		match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+	if (match != 0)
+		match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+	if (match != 0) {
+#ifdef AHD_TARGET_MODE
+		int group;
+
+		group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+		if (role == ROLE_INITIATOR) {
+			match = (group != XPT_FC_GROUP_TMODE)
+			      && ((tag == SCB_GET_TAG(scb))
+			       || (tag == SCB_LIST_NULL));
+		} else if (role == ROLE_TARGET) {
+			match = (group == XPT_FC_GROUP_TMODE)
+			      && ((tag == scb->io_ctx->csio.tag_id)
+			       || (tag == SCB_LIST_NULL));
+		}
+#else /* !AHD_TARGET_MODE */
+		match = ((tag == SCB_GET_TAG(scb)) || (tag == SCB_LIST_NULL));
+#endif /* AHD_TARGET_MODE */
+	}
+
+	return match;
+}
+
+static void
+ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
+{
+	int	target;
+	char	channel;
+	int	lun;
+
+	target = SCB_GET_TARGET(ahd, scb);
+	lun = SCB_GET_LUN(scb);
+	channel = SCB_GET_CHANNEL(ahd, scb);
+	
+	ahd_search_qinfifo(ahd, target, channel, lun,
+			   /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+			   CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+	ahd_platform_freeze_devq(ahd, scb);
+}
+
+void
+ahd_qinfifo_requeue_tail(struct ahd_softc *ahd, struct scb *scb)
+{
+	struct scb	*prev_scb;
+	ahd_mode_state	 saved_modes;
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+	prev_scb = NULL;
+	if (ahd_qinfifo_count(ahd) != 0) {
+		u_int prev_tag;
+		u_int prev_pos;
+
+		prev_pos = AHD_QIN_WRAP(ahd->qinfifonext - 1);
+		prev_tag = ahd->qinfifo[prev_pos];
+		prev_scb = ahd_lookup_scb(ahd, prev_tag);
+	}
+	ahd_qinfifo_requeue(ahd, prev_scb, scb);
+	ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+	ahd_restore_modes(ahd, saved_modes);
+}
+
+static void
+ahd_qinfifo_requeue(struct ahd_softc *ahd, struct scb *prev_scb,
+		    struct scb *scb)
+{
+	if (prev_scb == NULL) {
+		uint32_t busaddr;
+
+		busaddr = ahd_le32toh(scb->hscb->hscb_busaddr);
+		ahd_outl(ahd, NEXT_QUEUED_SCB_ADDR, busaddr);
+	} else {
+		prev_scb->hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
+		ahd_sync_scb(ahd, prev_scb, 
+			     BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+	}
+	ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
+	ahd->qinfifonext++;
+	scb->hscb->next_hscb_busaddr = ahd->next_queued_hscb->hscb_busaddr;
+	ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahd_qinfifo_count(struct ahd_softc *ahd)
+{
+	u_int qinpos;
+	u_int wrap_qinpos;
+	u_int wrap_qinfifonext;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+	qinpos = ahd_get_snscb_qoff(ahd);
+	wrap_qinpos = AHD_QIN_WRAP(qinpos);
+	wrap_qinfifonext = AHD_QIN_WRAP(ahd->qinfifonext);
+	if (wrap_qinfifonext >= wrap_qinpos)
+		return (wrap_qinfifonext - wrap_qinpos);
+	else
+		return (wrap_qinfifonext
+		      + ARRAY_SIZE(ahd->qinfifo) - wrap_qinpos);
+}
+
+static void
+ahd_reset_cmds_pending(struct ahd_softc *ahd)
+{
+	struct		scb *scb;
+	ahd_mode_state	saved_modes;
+	u_int		pending_cmds;
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+	/*
+	 * Don't count any commands as outstanding that the
+	 * sequencer has already marked for completion.
+	 */
+	ahd_flush_qoutfifo(ahd);
+
+	pending_cmds = 0;
+	LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+		pending_cmds++;
+	}
+	ahd_outw(ahd, CMDS_PENDING, pending_cmds - ahd_qinfifo_count(ahd));
+	ahd_restore_modes(ahd, saved_modes);
+	ahd->flags &= ~AHD_UPDATE_PEND_CMDS;
+}
+
+static void
+ahd_done_with_status(struct ahd_softc *ahd, struct scb *scb, uint32_t status)
+{
+	cam_status ostat;
+	cam_status cstat;
+
+	ostat = ahd_get_transaction_status(scb);
+	if (ostat == CAM_REQ_INPROG)
+		ahd_set_transaction_status(scb, status);
+	cstat = ahd_get_transaction_status(scb);
+	if (cstat != CAM_REQ_CMP)
+		ahd_freeze_scb(scb);
+	ahd_done(ahd, scb);
+}
+
+int
+ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel,
+		   int lun, u_int tag, role_t role, uint32_t status,
+		   ahd_search_action action)
+{
+	struct scb	*scb;
+	struct scb	*mk_msg_scb;
+	struct scb	*prev_scb;
+	ahd_mode_state	 saved_modes;
+	u_int		 qinstart;
+	u_int		 qinpos;
+	u_int		 qintail;
+	u_int		 tid_next;
+	u_int		 tid_prev;
+	u_int		 scbid;
+	u_int		 seq_flags2;
+	u_int		 savedscbptr;
+	uint32_t	 busaddr;
+	int		 found;
+	int		 targets;
+
+	/* Must be in CCHAN mode */
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+	/*
+	 * Halt any pending SCB DMA.  The sequencer will reinitiate
+	 * this dma if the qinfifo is not empty once we unpause.
+	 */
+	if ((ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN|CCSCBDIR))
+	 == (CCARREN|CCSCBEN|CCSCBDIR)) {
+		ahd_outb(ahd, CCSCBCTL,
+			 ahd_inb(ahd, CCSCBCTL) & ~(CCARREN|CCSCBEN));
+		while ((ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN)) != 0)
+			;
+	}
+	/* Determine sequencer's position in the qinfifo. */
+	qintail = AHD_QIN_WRAP(ahd->qinfifonext);
+	qinstart = ahd_get_snscb_qoff(ahd);
+	qinpos = AHD_QIN_WRAP(qinstart);
+	found = 0;
+	prev_scb = NULL;
+
+	if (action == SEARCH_PRINT) {
+		printk("qinstart = %d qinfifonext = %d\nQINFIFO:",
+		       qinstart, ahd->qinfifonext);
+	}
+
+	/*
+	 * Start with an empty queue.  Entries that are not chosen
+	 * for removal will be re-added to the queue as we go.
+	 */
+	ahd->qinfifonext = qinstart;
+	busaddr = ahd_le32toh(ahd->next_queued_hscb->hscb_busaddr);
+	ahd_outl(ahd, NEXT_QUEUED_SCB_ADDR, busaddr);
+
+	while (qinpos != qintail) {
+		scb = ahd_lookup_scb(ahd, ahd->qinfifo[qinpos]);
+		if (scb == NULL) {
+			printk("qinpos = %d, SCB index = %d\n",
+				qinpos, ahd->qinfifo[qinpos]);
+			panic("Loop 1\n");
+		}
+
+		if (ahd_match_scb(ahd, scb, target, channel, lun, tag, role)) {
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+				if ((scb->flags & SCB_ACTIVE) == 0)
+					printk("Inactive SCB in qinfifo\n");
+				ahd_done_with_status(ahd, scb, status);
+				/* FALLTHROUGH */
+			case SEARCH_REMOVE:
+				break;
+			case SEARCH_PRINT:
+				printk(" 0x%x", ahd->qinfifo[qinpos]);
+				/* FALLTHROUGH */
+			case SEARCH_COUNT:
+				ahd_qinfifo_requeue(ahd, prev_scb, scb);
+				prev_scb = scb;
+				break;
+			}
+		} else {
+			ahd_qinfifo_requeue(ahd, prev_scb, scb);
+			prev_scb = scb;
+		}
+		qinpos = AHD_QIN_WRAP(qinpos+1);
+	}
+
+	ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+
+	if (action == SEARCH_PRINT)
+		printk("\nWAITING_TID_QUEUES:\n");
+
+	/*
+	 * Search waiting for selection lists.  We traverse the
+	 * list of "their ids" waiting for selection and, if
+	 * appropriate, traverse the SCBs of each "their id"
+	 * looking for matches.
+	 */
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	seq_flags2 = ahd_inb(ahd, SEQ_FLAGS2);
+	if ((seq_flags2 & PENDING_MK_MESSAGE) != 0) {
+		scbid = ahd_inw(ahd, MK_MESSAGE_SCB);
+		mk_msg_scb = ahd_lookup_scb(ahd, scbid);
+	} else
+		mk_msg_scb = NULL;
+	savedscbptr = ahd_get_scbptr(ahd);
+	tid_next = ahd_inw(ahd, WAITING_TID_HEAD);
+	tid_prev = SCB_LIST_NULL;
+	targets = 0;
+	for (scbid = tid_next; !SCBID_IS_NULL(scbid); scbid = tid_next) {
+		u_int tid_head;
+		u_int tid_tail;
+
+		targets++;
+		if (targets > AHD_NUM_TARGETS)
+			panic("TID LIST LOOP");
+
+		if (scbid >= ahd->scb_data.numscbs) {
+			printk("%s: Waiting TID List inconsistency. "
+			       "SCB index == 0x%x, yet numscbs == 0x%x.",
+			       ahd_name(ahd), scbid, ahd->scb_data.numscbs);
+			ahd_dump_card_state(ahd);
+			panic("for safety");
+		}
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			printk("%s: SCB = 0x%x Not Active!\n",
+			       ahd_name(ahd), scbid);
+			panic("Waiting TID List traversal\n");
+		}
+		ahd_set_scbptr(ahd, scbid);
+		tid_next = ahd_inw_scbram(ahd, SCB_NEXT2);
+		if (ahd_match_scb(ahd, scb, target, channel, CAM_LUN_WILDCARD,
+				  SCB_LIST_NULL, ROLE_UNKNOWN) == 0) {
+			tid_prev = scbid;
+			continue;
+		}
+
+		/*
+		 * We found a list of scbs that needs to be searched.
+		 */
+		if (action == SEARCH_PRINT)
+			printk("       %d ( ", SCB_GET_TARGET(ahd, scb));
+		tid_head = scbid;
+		found += ahd_search_scb_list(ahd, target, channel,
+					     lun, tag, role, status,
+					     action, &tid_head, &tid_tail,
+					     SCB_GET_TARGET(ahd, scb));
+		/*
+		 * Check any MK_MESSAGE SCB that is still waiting to
+		 * enter this target's waiting for selection queue.
+		 */
+		if (mk_msg_scb != NULL
+		 && ahd_match_scb(ahd, mk_msg_scb, target, channel,
+				  lun, tag, role)) {
+
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+				if ((mk_msg_scb->flags & SCB_ACTIVE) == 0)
+					printk("Inactive SCB pending MK_MSG\n");
+				ahd_done_with_status(ahd, mk_msg_scb, status);
+				/* FALLTHROUGH */
+			case SEARCH_REMOVE:
+			{
+				u_int tail_offset;
+
+				printk("Removing MK_MSG scb\n");
+
+				/*
+				 * Reset our tail to the tail of the
+				 * main per-target list.
+				 */
+				tail_offset = WAITING_SCB_TAILS
+				    + (2 * SCB_GET_TARGET(ahd, mk_msg_scb));
+				ahd_outw(ahd, tail_offset, tid_tail);
+
+				seq_flags2 &= ~PENDING_MK_MESSAGE;
+				ahd_outb(ahd, SEQ_FLAGS2, seq_flags2);
+				ahd_outw(ahd, CMDS_PENDING,
+					 ahd_inw(ahd, CMDS_PENDING)-1);
+				mk_msg_scb = NULL;
+				break;
+			}
+			case SEARCH_PRINT:
+				printk(" 0x%x", SCB_GET_TAG(scb));
+				/* FALLTHROUGH */
+			case SEARCH_COUNT:
+				break;
+			}
+		}
+
+		if (mk_msg_scb != NULL
+		 && SCBID_IS_NULL(tid_head)
+		 && ahd_match_scb(ahd, scb, target, channel, CAM_LUN_WILDCARD,
+				  SCB_LIST_NULL, ROLE_UNKNOWN)) {
+
+			/*
+			 * When removing the last SCB for a target
+			 * queue with a pending MK_MESSAGE scb, we
+			 * must queue the MK_MESSAGE scb.
+			 */
+			printk("Queueing mk_msg_scb\n");
+			tid_head = ahd_inw(ahd, MK_MESSAGE_SCB);
+			seq_flags2 &= ~PENDING_MK_MESSAGE;
+			ahd_outb(ahd, SEQ_FLAGS2, seq_flags2);
+			mk_msg_scb = NULL;
+		}
+		if (tid_head != scbid)
+			ahd_stitch_tid_list(ahd, tid_prev, tid_head, tid_next);
+		if (!SCBID_IS_NULL(tid_head))
+			tid_prev = tid_head;
+		if (action == SEARCH_PRINT)
+			printk(")\n");
+	}
+
+	/* Restore saved state. */
+	ahd_set_scbptr(ahd, savedscbptr);
+	ahd_restore_modes(ahd, saved_modes);
+	return (found);
+}
+
+static int
+ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
+		    int lun, u_int tag, role_t role, uint32_t status,
+		    ahd_search_action action, u_int *list_head, 
+		    u_int *list_tail, u_int tid)
+{
+	struct	scb *scb;
+	u_int	scbid;
+	u_int	next;
+	u_int	prev;
+	int	found;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	found = 0;
+	prev = SCB_LIST_NULL;
+	next = *list_head;
+	*list_tail = SCB_LIST_NULL;
+	for (scbid = next; !SCBID_IS_NULL(scbid); scbid = next) {
+		if (scbid >= ahd->scb_data.numscbs) {
+			printk("%s:SCB List inconsistency. "
+			       "SCB == 0x%x, yet numscbs == 0x%x.",
+			       ahd_name(ahd), scbid, ahd->scb_data.numscbs);
+			ahd_dump_card_state(ahd);
+			panic("for safety");
+		}
+		scb = ahd_lookup_scb(ahd, scbid);
+		if (scb == NULL) {
+			printk("%s: SCB = %d Not Active!\n",
+			       ahd_name(ahd), scbid);
+			panic("Waiting List traversal\n");
+		}
+		ahd_set_scbptr(ahd, scbid);
+		*list_tail = scbid;
+		next = ahd_inw_scbram(ahd, SCB_NEXT);
+		if (ahd_match_scb(ahd, scb, target, channel,
+				  lun, SCB_LIST_NULL, role) == 0) {
+			prev = scbid;
+			continue;
+		}
+		found++;
+		switch (action) {
+		case SEARCH_COMPLETE:
+			if ((scb->flags & SCB_ACTIVE) == 0)
+				printk("Inactive SCB in Waiting List\n");
+			ahd_done_with_status(ahd, scb, status);
+			/* FALLTHROUGH */
+		case SEARCH_REMOVE:
+			ahd_rem_wscb(ahd, scbid, prev, next, tid);
+			*list_tail = prev;
+			if (SCBID_IS_NULL(prev))
+				*list_head = next;
+			break;
+		case SEARCH_PRINT:
+			printk("0x%x ", scbid);
+		case SEARCH_COUNT:
+			prev = scbid;
+			break;
+		}
+		if (found > AHD_SCB_MAX)
+			panic("SCB LIST LOOP");
+	}
+	if (action == SEARCH_COMPLETE
+	 || action == SEARCH_REMOVE)
+		ahd_outw(ahd, CMDS_PENDING, ahd_inw(ahd, CMDS_PENDING) - found);
+	return (found);
+}
+
+static void
+ahd_stitch_tid_list(struct ahd_softc *ahd, u_int tid_prev,
+		    u_int tid_cur, u_int tid_next)
+{
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+	if (SCBID_IS_NULL(tid_cur)) {
+
+		/* Bypass current TID list */
+		if (SCBID_IS_NULL(tid_prev)) {
+			ahd_outw(ahd, WAITING_TID_HEAD, tid_next);
+		} else {
+			ahd_set_scbptr(ahd, tid_prev);
+			ahd_outw(ahd, SCB_NEXT2, tid_next);
+		}
+		if (SCBID_IS_NULL(tid_next))
+			ahd_outw(ahd, WAITING_TID_TAIL, tid_prev);
+	} else {
+
+		/* Stitch through tid_cur */
+		if (SCBID_IS_NULL(tid_prev)) {
+			ahd_outw(ahd, WAITING_TID_HEAD, tid_cur);
+		} else {
+			ahd_set_scbptr(ahd, tid_prev);
+			ahd_outw(ahd, SCB_NEXT2, tid_cur);
+		}
+		ahd_set_scbptr(ahd, tid_cur);
+		ahd_outw(ahd, SCB_NEXT2, tid_next);
+
+		if (SCBID_IS_NULL(tid_next))
+			ahd_outw(ahd, WAITING_TID_TAIL, tid_cur);
+	}
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
+	     u_int prev, u_int next, u_int tid)
+{
+	u_int tail_offset;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	if (!SCBID_IS_NULL(prev)) {
+		ahd_set_scbptr(ahd, prev);
+		ahd_outw(ahd, SCB_NEXT, next);
+	}
+
+	/*
+	 * SCBs that have MK_MESSAGE set in them may
+	 * cause the tail pointer to be updated without
+	 * setting the next pointer of the previous tail.
+	 * Only clear the tail if the removed SCB was
+	 * the tail.
+	 */
+	tail_offset = WAITING_SCB_TAILS + (2 * tid);
+	if (SCBID_IS_NULL(next)
+	 && ahd_inw(ahd, tail_offset) == scbid)
+		ahd_outw(ahd, tail_offset, prev);
+
+	ahd_add_scb_to_free_list(ahd, scbid);
+	return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs.  This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahd_add_scb_to_free_list(struct ahd_softc *ahd, u_int scbid)
+{
+/* XXX Need some other mechanism to designate "free". */
+	/*
+	 * Invalidate the tag so that our abort
+	 * routines don't think it's active.
+	ahd_outb(ahd, SCB_TAG, SCB_LIST_NULL);
+	 */
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG.  This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+static int
+ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel,
+	       int lun, u_int tag, role_t role, uint32_t status)
+{
+	struct		scb *scbp;
+	struct		scb *scbp_next;
+	u_int		i, j;
+	u_int		maxtarget;
+	u_int		minlun;
+	u_int		maxlun;
+	int		found;
+	ahd_mode_state	saved_modes;
+
+	/* restore this when we're done */
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+	found = ahd_search_qinfifo(ahd, target, channel, lun, SCB_LIST_NULL,
+				   role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+	/*
+	 * Clean out the busy target table for any untagged commands.
+	 */
+	i = 0;
+	maxtarget = 16;
+	if (target != CAM_TARGET_WILDCARD) {
+		i = target;
+		if (channel == 'B')
+			i += 8;
+		maxtarget = i + 1;
+	}
+
+	if (lun == CAM_LUN_WILDCARD) {
+		minlun = 0;
+		maxlun = AHD_NUM_LUNS_NONPKT;
+	} else if (lun >= AHD_NUM_LUNS_NONPKT) {
+		minlun = maxlun = 0;
+	} else {
+		minlun = lun;
+		maxlun = lun + 1;
+	}
+
+	if (role != ROLE_TARGET) {
+		for (;i < maxtarget; i++) {
+			for (j = minlun;j < maxlun; j++) {
+				u_int scbid;
+				u_int tcl;
+
+				tcl = BUILD_TCL_RAW(i, 'A', j);
+				scbid = ahd_find_busy_tcl(ahd, tcl);
+				scbp = ahd_lookup_scb(ahd, scbid);
+				if (scbp == NULL
+				 || ahd_match_scb(ahd, scbp, target, channel,
+						  lun, tag, role) == 0)
+					continue;
+				ahd_unbusy_tcl(ahd, BUILD_TCL_RAW(i, 'A', j));
+			}
+		}
+	}
+
+	/*
+	 * Don't abort commands that have already completed,
+	 * but haven't quite made it up to the host yet.
+	 */
+	ahd_flush_qoutfifo(ahd);
+
+	/*
+	 * Go through the pending CCB list and look for
+	 * commands for this target that are still active.
+	 * These are other tagged commands that were
+	 * disconnected when the reset occurred.
+	 */
+	scbp_next = LIST_FIRST(&ahd->pending_scbs);
+	while (scbp_next != NULL) {
+		scbp = scbp_next;
+		scbp_next = LIST_NEXT(scbp, pending_links);
+		if (ahd_match_scb(ahd, scbp, target, channel, lun, tag, role)) {
+			cam_status ostat;
+
+			ostat = ahd_get_transaction_status(scbp);
+			if (ostat == CAM_REQ_INPROG)
+				ahd_set_transaction_status(scbp, status);
+			if (ahd_get_transaction_status(scbp) != CAM_REQ_CMP)
+				ahd_freeze_scb(scbp);
+			if ((scbp->flags & SCB_ACTIVE) == 0)
+				printk("Inactive SCB on pending list\n");
+			ahd_done(ahd, scbp);
+			found++;
+		}
+	}
+	ahd_restore_modes(ahd, saved_modes);
+	ahd_platform_abort_scbs(ahd, target, channel, lun, tag, role, status);
+	ahd->flags |= AHD_UPDATE_PEND_CMDS;
+	return found;
+}
+
+static void
+ahd_reset_current_bus(struct ahd_softc *ahd)
+{
+	uint8_t scsiseq;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) & ~ENSCSIRST);
+	scsiseq = ahd_inb(ahd, SCSISEQ0) & ~(ENSELO|ENARBO|SCSIRSTO);
+	ahd_outb(ahd, SCSISEQ0, scsiseq | SCSIRSTO);
+	ahd_flush_device_writes(ahd);
+	ahd_delay(AHD_BUSRESET_DELAY);
+	/* Turn off the bus reset */
+	ahd_outb(ahd, SCSISEQ0, scsiseq);
+	ahd_flush_device_writes(ahd);
+	ahd_delay(AHD_BUSRESET_DELAY);
+	if ((ahd->bugs & AHD_SCSIRST_BUG) != 0) {
+		/*
+		 * 2A Razor #474
+		 * Certain chip state is not cleared for
+		 * SCSI bus resets that we initiate, so
+		 * we must reset the chip.
+		 */
+		ahd_reset(ahd, /*reinit*/TRUE);
+		ahd_intr_enable(ahd, /*enable*/TRUE);
+		AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	}
+
+	ahd_clear_intstat(ahd);
+}
+
+int
+ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
+{
+	struct	ahd_devinfo caminfo;
+	u_int	initiator;
+	u_int	target;
+	u_int	max_scsiid;
+	int	found;
+	u_int	fifo;
+	u_int	next_fifo;
+	uint8_t scsiseq;
+
+	/*
+	 * Check if the last bus reset is cleared
+	 */
+	if (ahd->flags & AHD_BUS_RESET_ACTIVE) {
+		printk("%s: bus reset still active\n",
+		       ahd_name(ahd));
+		return 0;
+	}
+	ahd->flags |= AHD_BUS_RESET_ACTIVE;
+
+	ahd->pending_device = NULL;
+
+	ahd_compile_devinfo(&caminfo,
+			    CAM_TARGET_WILDCARD,
+			    CAM_TARGET_WILDCARD,
+			    CAM_LUN_WILDCARD,
+			    channel, ROLE_UNKNOWN);
+	ahd_pause(ahd);
+
+	/* Make sure the sequencer is in a safe location. */
+	ahd_clear_critical_section(ahd);
+
+	/*
+	 * Run our command complete fifos to ensure that we perform
+	 * completion processing on any commands that 'completed'
+	 * before the reset occurred.
+	 */
+	ahd_run_qoutfifo(ahd);
+#ifdef AHD_TARGET_MODE
+	if ((ahd->flags & AHD_TARGETROLE) != 0) {
+		ahd_run_tqinfifo(ahd, /*paused*/TRUE);
+	}
+#endif
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+	/*
+	 * Disable selections so no automatic hardware
+	 * functions will modify chip state.
+	 */
+	ahd_outb(ahd, SCSISEQ0, 0);
+	ahd_outb(ahd, SCSISEQ1, 0);
+
+	/*
+	 * Safely shut down our DMA engines.  Always start with
+	 * the FIFO that is not currently active (if any are
+	 * actively connected).
+	 */
+	next_fifo = fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;
+	if (next_fifo > CURRFIFO_1)
+		/* If disconneced, arbitrarily start with FIFO1. */
+		next_fifo = fifo = 0;
+	do {
+		next_fifo ^= CURRFIFO_1;
+		ahd_set_modes(ahd, next_fifo, next_fifo);
+		ahd_outb(ahd, DFCNTRL,
+			 ahd_inb(ahd, DFCNTRL) & ~(SCSIEN|HDMAEN));
+		while ((ahd_inb(ahd, DFCNTRL) & HDMAENACK) != 0)
+			ahd_delay(10);
+		/*
+		 * Set CURRFIFO to the now inactive channel.
+		 */
+		ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+		ahd_outb(ahd, DFFSTAT, next_fifo);
+	} while (next_fifo != fifo);
+
+	/*
+	 * Reset the bus if we are initiating this reset
+	 */
+	ahd_clear_msg_state(ahd);
+	ahd_outb(ahd, SIMODE1,
+		 ahd_inb(ahd, SIMODE1) & ~(ENBUSFREE|ENSCSIRST));
+
+	if (initiate_reset)
+		ahd_reset_current_bus(ahd);
+
+	ahd_clear_intstat(ahd);
+
+	/*
+	 * Clean up all the state information for the
+	 * pending transactions on this bus.
+	 */
+	found = ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, channel,
+			       CAM_LUN_WILDCARD, SCB_LIST_NULL,
+			       ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+	/*
+	 * Cleanup anything left in the FIFOs.
+	 */
+	ahd_clear_fifo(ahd, 0);
+	ahd_clear_fifo(ahd, 1);
+
+	/*
+	 * Clear SCSI interrupt status
+	 */
+	ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
+
+	/*
+	 * Reenable selections
+	 */
+	ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
+	scsiseq = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+	ahd_outb(ahd, SCSISEQ1, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+
+	max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
+#ifdef AHD_TARGET_MODE
+	/*
+	 * Send an immediate notify ccb to all target more peripheral
+	 * drivers affected by this action.
+	 */
+	for (target = 0; target <= max_scsiid; target++) {
+		struct ahd_tmode_tstate* tstate;
+		u_int lun;
+
+		tstate = ahd->enabled_targets[target];
+		if (tstate == NULL)
+			continue;
+		for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+			struct ahd_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[lun];
+			if (lstate == NULL)
+				continue;
+
+			ahd_queue_lstate_event(ahd, lstate, CAM_TARGET_WILDCARD,
+					       EVENT_TYPE_BUS_RESET, /*arg*/0);
+			ahd_send_lstate_events(ahd, lstate);
+		}
+	}
+#endif
+	/*
+	 * Revert to async/narrow transfers until we renegotiate.
+	 */
+	for (target = 0; target <= max_scsiid; target++) {
+
+		if (ahd->enabled_targets[target] == NULL)
+			continue;
+		for (initiator = 0; initiator <= max_scsiid; initiator++) {
+			struct ahd_devinfo devinfo;
+
+			ahd_compile_devinfo(&devinfo, target, initiator,
+					    CAM_LUN_WILDCARD,
+					    'A', ROLE_UNKNOWN);
+			ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+				      AHD_TRANS_CUR, /*paused*/TRUE);
+			ahd_set_syncrate(ahd, &devinfo, /*period*/0,
+					 /*offset*/0, /*ppr_options*/0,
+					 AHD_TRANS_CUR, /*paused*/TRUE);
+		}
+	}
+
+	/* Notify the XPT that a bus reset occurred */
+	ahd_send_async(ahd, caminfo.channel, CAM_TARGET_WILDCARD,
+		       CAM_LUN_WILDCARD, AC_BUS_RESET);
+
+	ahd_restart(ahd);
+
+	return (found);
+}
+
+/**************************** Statistics Processing ***************************/
+static void
+ahd_stat_timer(void *arg)
+{
+	struct	ahd_softc *ahd = arg;
+	u_long	s;
+	int	enint_coal;
+	
+	ahd_lock(ahd, &s);
+
+	enint_coal = ahd->hs_mailbox & ENINT_COALESCE;
+	if (ahd->cmdcmplt_total > ahd->int_coalescing_threshold)
+		enint_coal |= ENINT_COALESCE;
+	else if (ahd->cmdcmplt_total < ahd->int_coalescing_stop_threshold)
+		enint_coal &= ~ENINT_COALESCE;
+
+	if (enint_coal != (ahd->hs_mailbox & ENINT_COALESCE)) {
+		ahd_enable_coalescing(ahd, enint_coal);
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_INT_COALESCING) != 0)
+			printk("%s: Interrupt coalescing "
+			       "now %sabled. Cmds %d\n",
+			       ahd_name(ahd),
+			       (enint_coal & ENINT_COALESCE) ? "en" : "dis",
+			       ahd->cmdcmplt_total);
+#endif
+	}
+
+	ahd->cmdcmplt_bucket = (ahd->cmdcmplt_bucket+1) & (AHD_STAT_BUCKETS-1);
+	ahd->cmdcmplt_total -= ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket];
+	ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket] = 0;
+	ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US,
+			ahd_stat_timer, ahd);
+	ahd_unlock(ahd, &s);
+}
+
+/****************************** Status Processing *****************************/
+
+static void
+ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
+{
+	struct	hardware_scb *hscb;
+	int	paused;
+
+	/*
+	 * The sequencer freezes its select-out queue
+	 * anytime a SCSI status error occurs.  We must
+	 * handle the error and increment our qfreeze count
+	 * to allow the sequencer to continue.  We don't
+	 * bother clearing critical sections here since all
+	 * operations are on data structures that the sequencer
+	 * is not touching once the queue is frozen.
+	 */
+	hscb = scb->hscb; 
+
+	if (ahd_is_paused(ahd)) {
+		paused = 1;
+	} else {
+		paused = 0;
+		ahd_pause(ahd);
+	}
+
+	/* Freeze the queue until the client sees the error. */
+	ahd_freeze_devq(ahd, scb);
+	ahd_freeze_scb(scb);
+	ahd->qfreeze_cnt++;
+	ahd_outw(ahd, KERNEL_QFREEZE_COUNT, ahd->qfreeze_cnt);
+
+	if (paused == 0)
+		ahd_unpause(ahd);
+
+	/* Don't want to clobber the original sense code */
+	if ((scb->flags & SCB_SENSE) != 0) {
+		/*
+		 * Clear the SCB_SENSE Flag and perform
+		 * a normal command completion.
+		 */
+		scb->flags &= ~SCB_SENSE;
+		ahd_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+		ahd_done(ahd, scb);
+		return;
+	}
+	ahd_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+	ahd_set_scsi_status(scb, hscb->shared_data.istatus.scsi_status);
+	switch (hscb->shared_data.istatus.scsi_status) {
+	case STATUS_PKT_SENSE:
+	{
+		struct scsi_status_iu_header *siu;
+
+		ahd_sync_sense(ahd, scb, BUS_DMASYNC_POSTREAD);
+		siu = (struct scsi_status_iu_header *)scb->sense_data;
+		ahd_set_scsi_status(scb, siu->status);
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_SENSE) != 0) {
+			ahd_print_path(ahd, scb);
+			printk("SCB 0x%x Received PKT Status of 0x%x\n",
+			       SCB_GET_TAG(scb), siu->status);
+			printk("\tflags = 0x%x, sense len = 0x%x, "
+			       "pktfail = 0x%x\n",
+			       siu->flags, scsi_4btoul(siu->sense_length),
+			       scsi_4btoul(siu->pkt_failures_length));
+		}
+#endif
+		if ((siu->flags & SIU_RSPVALID) != 0) {
+			ahd_print_path(ahd, scb);
+			if (scsi_4btoul(siu->pkt_failures_length) < 4) {
+				printk("Unable to parse pkt_failures\n");
+			} else {
+
+				switch (SIU_PKTFAIL_CODE(siu)) {
+				case SIU_PFC_NONE:
+					printk("No packet failure found\n");
+					break;
+				case SIU_PFC_CIU_FIELDS_INVALID:
+					printk("Invalid Command IU Field\n");
+					break;
+				case SIU_PFC_TMF_NOT_SUPPORTED:
+					printk("TMF not supported\n");
+					break;
+				case SIU_PFC_TMF_FAILED:
+					printk("TMF failed\n");
+					break;
+				case SIU_PFC_INVALID_TYPE_CODE:
+					printk("Invalid L_Q Type code\n");
+					break;
+				case SIU_PFC_ILLEGAL_REQUEST:
+					printk("Illegal request\n");
+				default:
+					break;
+				}
+			}
+			if (siu->status == SCSI_STATUS_OK)
+				ahd_set_transaction_status(scb,
+							   CAM_REQ_CMP_ERR);
+		}
+		if ((siu->flags & SIU_SNSVALID) != 0) {
+			scb->flags |= SCB_PKT_SENSE;
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_SENSE) != 0)
+				printk("Sense data available\n");
+#endif
+		}
+		ahd_done(ahd, scb);
+		break;
+	}
+	case SCSI_STATUS_CMD_TERMINATED:
+	case SCSI_STATUS_CHECK_COND:
+	{
+		struct ahd_devinfo devinfo;
+		struct ahd_dma_seg *sg;
+		struct scsi_sense *sc;
+		struct ahd_initiator_tinfo *targ_info;
+		struct ahd_tmode_tstate *tstate;
+		struct ahd_transinfo *tinfo;
+#ifdef AHD_DEBUG
+		if (ahd_debug & AHD_SHOW_SENSE) {
+			ahd_print_path(ahd, scb);
+			printk("SCB %d: requests Check Status\n",
+			       SCB_GET_TAG(scb));
+		}
+#endif
+
+		if (ahd_perform_autosense(scb) == 0)
+			break;
+
+		ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
+				    SCB_GET_TARGET(ahd, scb),
+				    SCB_GET_LUN(scb),
+				    SCB_GET_CHANNEL(ahd, scb),
+				    ROLE_INITIATOR);
+		targ_info = ahd_fetch_transinfo(ahd,
+						devinfo.channel,
+						devinfo.our_scsiid,
+						devinfo.target,
+						&tstate);
+		tinfo = &targ_info->curr;
+		sg = scb->sg_list;
+		sc = (struct scsi_sense *)hscb->shared_data.idata.cdb;
+		/*
+		 * Save off the residual if there is one.
+		 */
+		ahd_update_residual(ahd, scb);
+#ifdef AHD_DEBUG
+		if (ahd_debug & AHD_SHOW_SENSE) {
+			ahd_print_path(ahd, scb);
+			printk("Sending Sense\n");
+		}
+#endif
+		scb->sg_count = 0;
+		sg = ahd_sg_setup(ahd, scb, sg, ahd_get_sense_bufaddr(ahd, scb),
+				  ahd_get_sense_bufsize(ahd, scb),
+				  /*last*/TRUE);
+		sc->opcode = REQUEST_SENSE;
+		sc->byte2 = 0;
+		if (tinfo->protocol_version <= SCSI_REV_2
+		 && SCB_GET_LUN(scb) < 8)
+			sc->byte2 = SCB_GET_LUN(scb) << 5;
+		sc->unused[0] = 0;
+		sc->unused[1] = 0;
+		sc->length = ahd_get_sense_bufsize(ahd, scb);
+		sc->control = 0;
+
+		/*
+		 * We can't allow the target to disconnect.
+		 * This will be an untagged transaction and
+		 * having the target disconnect will make this
+		 * transaction indestinguishable from outstanding
+		 * tagged transactions.
+		 */
+		hscb->control = 0;
+
+		/*
+		 * This request sense could be because the
+		 * the device lost power or in some other
+		 * way has lost our transfer negotiations.
+		 * Renegotiate if appropriate.  Unit attention
+		 * errors will be reported before any data
+		 * phases occur.
+		 */
+		if (ahd_get_residual(scb) == ahd_get_transfer_length(scb)) {
+			ahd_update_neg_request(ahd, &devinfo,
+					       tstate, targ_info,
+					       AHD_NEG_IF_NON_ASYNC);
+		}
+		if (tstate->auto_negotiate & devinfo.target_mask) {
+			hscb->control |= MK_MESSAGE;
+			scb->flags &=
+			    ~(SCB_NEGOTIATE|SCB_ABORT|SCB_DEVICE_RESET);
+			scb->flags |= SCB_AUTO_NEGOTIATE;
+		}
+		hscb->cdb_len = sizeof(*sc);
+		ahd_setup_data_scb(ahd, scb);
+		scb->flags |= SCB_SENSE;
+		ahd_queue_scb(ahd, scb);
+		break;
+	}
+	case SCSI_STATUS_OK:
+		printk("%s: Interrupted for status of 0???\n",
+		       ahd_name(ahd));
+		/* FALLTHROUGH */
+	default:
+		ahd_done(ahd, scb);
+		break;
+	}
+}
+
+static void
+ahd_handle_scb_status(struct ahd_softc *ahd, struct scb *scb)
+{
+	if (scb->hscb->shared_data.istatus.scsi_status != 0) {
+		ahd_handle_scsi_status(ahd, scb);
+	} else {
+		ahd_calc_residual(ahd, scb);
+		ahd_done(ahd, scb);
+	}
+}
+
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+static void
+ahd_calc_residual(struct ahd_softc *ahd, struct scb *scb)
+{
+	struct hardware_scb *hscb;
+	struct initiator_status *spkt;
+	uint32_t sgptr;
+	uint32_t resid_sgptr;
+	uint32_t resid;
+
+	/*
+	 * 5 cases.
+	 * 1) No residual.
+	 *    SG_STATUS_VALID clear in sgptr.
+	 * 2) Transferless command
+	 * 3) Never performed any transfers.
+	 *    sgptr has SG_FULL_RESID set.
+	 * 4) No residual but target did not
+	 *    save data pointers after the
+	 *    last transfer, so sgptr was
+	 *    never updated.
+	 * 5) We have a partial residual.
+	 *    Use residual_sgptr to determine
+	 *    where we are.
+	 */
+
+	hscb = scb->hscb;
+	sgptr = ahd_le32toh(hscb->sgptr);
+	if ((sgptr & SG_STATUS_VALID) == 0)
+		/* Case 1 */
+		return;
+	sgptr &= ~SG_STATUS_VALID;
+
+	if ((sgptr & SG_LIST_NULL) != 0)
+		/* Case 2 */
+		return;
+
+	/*
+	 * Residual fields are the same in both
+	 * target and initiator status packets,
+	 * so we can always use the initiator fields
+	 * regardless of the role for this SCB.
+	 */
+	spkt = &hscb->shared_data.istatus;
+	resid_sgptr = ahd_le32toh(spkt->residual_sgptr);
+	if ((sgptr & SG_FULL_RESID) != 0) {
+		/* Case 3 */
+		resid = ahd_get_transfer_length(scb);
+	} else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+		/* Case 4 */
+		return;
+	} else if ((resid_sgptr & SG_OVERRUN_RESID) != 0) {
+		ahd_print_path(ahd, scb);
+		printk("data overrun detected Tag == 0x%x.\n",
+		       SCB_GET_TAG(scb));
+		ahd_freeze_devq(ahd, scb);
+		ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+		ahd_freeze_scb(scb);
+		return;
+	} else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+		panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+		/* NOTREACHED */
+	} else {
+		struct ahd_dma_seg *sg;
+
+		/*
+		 * Remainder of the SG where the transfer
+		 * stopped.  
+		 */
+		resid = ahd_le32toh(spkt->residual_datacnt) & AHD_SG_LEN_MASK;
+		sg = ahd_sg_bus_to_virt(ahd, scb, resid_sgptr & SG_PTR_MASK);
+
+		/* The residual sg_ptr always points to the next sg */
+		sg--;
+
+		/*
+		 * Add up the contents of all residual
+		 * SG segments that are after the SG where
+		 * the transfer stopped.
+		 */
+		while ((ahd_le32toh(sg->len) & AHD_DMA_LAST_SEG) == 0) {
+			sg++;
+			resid += ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+		}
+	}
+	if ((scb->flags & SCB_SENSE) == 0)
+		ahd_set_residual(scb, resid);
+	else
+		ahd_set_sense_residual(scb, resid);
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_MISC) != 0) {
+		ahd_print_path(ahd, scb);
+		printk("Handled %sResidual of %d bytes\n",
+		       (scb->flags & SCB_SENSE) ? "Sense " : "", resid);
+	}
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHD_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahd_queue_lstate_event(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate,
+		       u_int initiator_id, u_int event_type, u_int event_arg)
+{
+	struct ahd_tmode_event *event;
+	int pending;
+
+	xpt_freeze_devq(lstate->path, /*count*/1);
+	if (lstate->event_w_idx >= lstate->event_r_idx)
+		pending = lstate->event_w_idx - lstate->event_r_idx;
+	else
+		pending = AHD_TMODE_EVENT_BUFFER_SIZE + 1
+			- (lstate->event_r_idx - lstate->event_w_idx);
+
+	if (event_type == EVENT_TYPE_BUS_RESET
+	 || event_type == MSG_BUS_DEV_RESET) {
+		/*
+		 * Any earlier events are irrelevant, so reset our buffer.
+		 * This has the effect of allowing us to deal with reset
+		 * floods (an external device holding down the reset line)
+		 * without losing the event that is really interesting.
+		 */
+		lstate->event_r_idx = 0;
+		lstate->event_w_idx = 0;
+		xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+	}
+
+	if (pending == AHD_TMODE_EVENT_BUFFER_SIZE) {
+		xpt_print_path(lstate->path);
+		printk("immediate event %x:%x lost\n",
+		       lstate->event_buffer[lstate->event_r_idx].event_type,
+		       lstate->event_buffer[lstate->event_r_idx].event_arg);
+		lstate->event_r_idx++;
+		if (lstate->event_r_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+			lstate->event_r_idx = 0;
+		xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+	}
+
+	event = &lstate->event_buffer[lstate->event_w_idx];
+	event->initiator_id = initiator_id;
+	event->event_type = event_type;
+	event->event_arg = event_arg;
+	lstate->event_w_idx++;
+	if (lstate->event_w_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+		lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahd_send_lstate_events(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate)
+{
+	struct ccb_hdr *ccbh;
+	struct ccb_immed_notify *inot;
+
+	while (lstate->event_r_idx != lstate->event_w_idx
+	    && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+		struct ahd_tmode_event *event;
+
+		event = &lstate->event_buffer[lstate->event_r_idx];
+		SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+		inot = (struct ccb_immed_notify *)ccbh;
+		switch (event->event_type) {
+		case EVENT_TYPE_BUS_RESET:
+			ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+			break;
+		default:
+			ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+			inot->message_args[0] = event->event_type;
+			inot->message_args[1] = event->event_arg;
+			break;
+		}
+		inot->initiator_id = event->initiator_id;
+		inot->sense_len = 0;
+		xpt_done((union ccb *)inot);
+		lstate->event_r_idx++;
+		if (lstate->event_r_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+			lstate->event_r_idx = 0;
+	}
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHD_DUMP_SEQ
+void
+ahd_dumpseq(struct ahd_softc* ahd)
+{
+	int i;
+	int max_prog;
+
+	max_prog = 2048;
+
+	ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+	ahd_outw(ahd, PRGMCNT, 0);
+	for (i = 0; i < max_prog; i++) {
+		uint8_t ins_bytes[4];
+
+		ahd_insb(ahd, SEQRAM, ins_bytes, 4);
+		printk("0x%08x\n", ins_bytes[0] << 24
+				 | ins_bytes[1] << 16
+				 | ins_bytes[2] << 8
+				 | ins_bytes[3]);
+	}
+}
+#endif
+
+static void
+ahd_loadseq(struct ahd_softc *ahd)
+{
+	struct	cs cs_table[num_critical_sections];
+	u_int	begin_set[num_critical_sections];
+	u_int	end_set[num_critical_sections];
+	const struct patch *cur_patch;
+	u_int	cs_count;
+	u_int	cur_cs;
+	u_int	i;
+	int	downloaded;
+	u_int	skip_addr;
+	u_int	sg_prefetch_cnt;
+	u_int	sg_prefetch_cnt_limit;
+	u_int	sg_prefetch_align;
+	u_int	sg_size;
+	u_int	cacheline_mask;
+	uint8_t	download_consts[DOWNLOAD_CONST_COUNT];
+
+	if (bootverbose)
+		printk("%s: Downloading Sequencer Program...",
+		       ahd_name(ahd));
+
+#if DOWNLOAD_CONST_COUNT != 8
+#error "Download Const Mismatch"
+#endif
+	/*
+	 * Start out with 0 critical sections
+	 * that apply to this firmware load.
+	 */
+	cs_count = 0;
+	cur_cs = 0;
+	memset(begin_set, 0, sizeof(begin_set));
+	memset(end_set, 0, sizeof(end_set));
+
+	/*
+	 * Setup downloadable constant table.
+	 * 
+	 * The computation for the S/G prefetch variables is
+	 * a bit complicated.  We would like to always fetch
+	 * in terms of cachelined sized increments.  However,
+	 * if the cacheline is not an even multiple of the
+	 * SG element size or is larger than our SG RAM, using
+	 * just the cache size might leave us with only a portion
+	 * of an SG element at the tail of a prefetch.  If the
+	 * cacheline is larger than our S/G prefetch buffer less
+	 * the size of an SG element, we may round down to a cacheline
+	 * that doesn't contain any or all of the S/G of interest
+	 * within the bounds of our S/G ram.  Provide variables to
+	 * the sequencer that will allow it to handle these edge
+	 * cases.
+	 */
+	/* Start by aligning to the nearest cacheline. */
+	sg_prefetch_align = ahd->pci_cachesize;
+	if (sg_prefetch_align == 0)
+		sg_prefetch_align = 8;
+	/* Round down to the nearest power of 2. */
+	while (powerof2(sg_prefetch_align) == 0)
+		sg_prefetch_align--;
+
+	cacheline_mask = sg_prefetch_align - 1;
+
+	/*
+	 * If the cacheline boundary is greater than half our prefetch RAM
+	 * we risk not being able to fetch even a single complete S/G
+	 * segment if we align to that boundary.
+	 */
+	if (sg_prefetch_align > CCSGADDR_MAX/2)
+		sg_prefetch_align = CCSGADDR_MAX/2;
+	/* Start by fetching a single cacheline. */
+	sg_prefetch_cnt = sg_prefetch_align;
+	/*
+	 * Increment the prefetch count by cachelines until
+	 * at least one S/G element will fit.
+	 */
+	sg_size = sizeof(struct ahd_dma_seg);
+	if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+		sg_size = sizeof(struct ahd_dma64_seg);
+	while (sg_prefetch_cnt < sg_size)
+		sg_prefetch_cnt += sg_prefetch_align;
+	/*
+	 * If the cacheline is not an even multiple of
+	 * the S/G size, we may only get a partial S/G when
+	 * we align. Add a cacheline if this is the case.
+	 */
+	if ((sg_prefetch_align % sg_size) != 0
+	 && (sg_prefetch_cnt < CCSGADDR_MAX))
+		sg_prefetch_cnt += sg_prefetch_align;
+	/*
+	 * Lastly, compute a value that the sequencer can use
+	 * to determine if the remainder of the CCSGRAM buffer
+	 * has a full S/G element in it.
+	 */
+	sg_prefetch_cnt_limit = -(sg_prefetch_cnt - sg_size + 1);
+	download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+	download_consts[SG_PREFETCH_CNT_LIMIT] = sg_prefetch_cnt_limit;
+	download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_align - 1);
+	download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_align - 1);
+	download_consts[SG_SIZEOF] = sg_size;
+	download_consts[PKT_OVERRUN_BUFOFFSET] =
+		(ahd->overrun_buf - (uint8_t *)ahd->qoutfifo) / 256;
+	download_consts[SCB_TRANSFER_SIZE] = SCB_TRANSFER_SIZE_1BYTE_LUN;
+	download_consts[CACHELINE_MASK] = cacheline_mask;
+	cur_patch = patches;
+	downloaded = 0;
+	skip_addr = 0;
+	ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+	ahd_outw(ahd, PRGMCNT, 0);
+
+	for (i = 0; i < sizeof(seqprog)/4; i++) {
+		if (ahd_check_patch(ahd, &cur_patch, i, &skip_addr) == 0) {
+			/*
+			 * Don't download this instruction as it
+			 * is in a patch that was removed.
+			 */
+			continue;
+		}
+		/*
+		 * Move through the CS table until we find a CS
+		 * that might apply to this instruction.
+		 */
+		for (; cur_cs < num_critical_sections; cur_cs++) {
+			if (critical_sections[cur_cs].end <= i) {
+				if (begin_set[cs_count] == TRUE
+				 && end_set[cs_count] == FALSE) {
+					cs_table[cs_count].end = downloaded;
+				 	end_set[cs_count] = TRUE;
+					cs_count++;
+				}
+				continue;
+			}
+			if (critical_sections[cur_cs].begin <= i
+			 && begin_set[cs_count] == FALSE) {
+				cs_table[cs_count].begin = downloaded;
+				begin_set[cs_count] = TRUE;
+			}
+			break;
+		}
+		ahd_download_instr(ahd, i, download_consts);
+		downloaded++;
+	}
+
+	ahd->num_critical_sections = cs_count;
+	if (cs_count != 0) {
+
+		cs_count *= sizeof(struct cs);
+		ahd->critical_sections = kmalloc(cs_count, GFP_ATOMIC);
+		if (ahd->critical_sections == NULL)
+			panic("ahd_loadseq: Could not malloc");
+		memcpy(ahd->critical_sections, cs_table, cs_count);
+	}
+	ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE);
+
+	if (bootverbose) {
+		printk(" %d instructions downloaded\n", downloaded);
+		printk("%s: Features 0x%x, Bugs 0x%x, Flags 0x%x\n",
+		       ahd_name(ahd), ahd->features, ahd->bugs, ahd->flags);
+	}
+}
+
+static int
+ahd_check_patch(struct ahd_softc *ahd, const struct patch **start_patch,
+		u_int start_instr, u_int *skip_addr)
+{
+	const struct patch *cur_patch;
+	const struct patch *last_patch;
+	u_int	num_patches;
+
+	num_patches = ARRAY_SIZE(patches);
+	last_patch = &patches[num_patches];
+	cur_patch = *start_patch;
+
+	while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+		if (cur_patch->patch_func(ahd) == 0) {
+
+			/* Start rejecting code */
+			*skip_addr = start_instr + cur_patch->skip_instr;
+			cur_patch += cur_patch->skip_patch;
+		} else {
+			/* Accepted this patch.  Advance to the next
+			 * one and wait for our intruction pointer to
+			 * hit this point.
+			 */
+			cur_patch++;
+		}
+	}
+
+	*start_patch = cur_patch;
+	if (start_instr < *skip_addr)
+		/* Still skipping */
+		return (0);
+
+	return (1);
+}
+
+static u_int
+ahd_resolve_seqaddr(struct ahd_softc *ahd, u_int address)
+{
+	const struct patch *cur_patch;
+	int address_offset;
+	u_int skip_addr;
+	u_int i;
+
+	address_offset = 0;
+	cur_patch = patches;
+	skip_addr = 0;
+
+	for (i = 0; i < address;) {
+
+		ahd_check_patch(ahd, &cur_patch, i, &skip_addr);
+
+		if (skip_addr > i) {
+			int end_addr;
+
+			end_addr = min(address, skip_addr);
+			address_offset += end_addr - i;
+			i = skip_addr;
+		} else {
+			i++;
+		}
+	}
+	return (address - address_offset);
+}
+
+static void
+ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts)
+{
+	union	ins_formats instr;
+	struct	ins_format1 *fmt1_ins;
+	struct	ins_format3 *fmt3_ins;
+	u_int	opcode;
+
+	/*
+	 * The firmware is always compiled into a little endian format.
+	 */
+	instr.integer = ahd_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+	fmt1_ins = &instr.format1;
+	fmt3_ins = NULL;
+
+	/* Pull the opcode */
+	opcode = instr.format1.opcode;
+	switch (opcode) {
+	case AIC_OP_JMP:
+	case AIC_OP_JC:
+	case AIC_OP_JNC:
+	case AIC_OP_CALL:
+	case AIC_OP_JNE:
+	case AIC_OP_JNZ:
+	case AIC_OP_JE:
+	case AIC_OP_JZ:
+	{
+		fmt3_ins = &instr.format3;
+		fmt3_ins->address = ahd_resolve_seqaddr(ahd, fmt3_ins->address);
+		/* FALLTHROUGH */
+	}
+	case AIC_OP_OR:
+	case AIC_OP_AND:
+	case AIC_OP_XOR:
+	case AIC_OP_ADD:
+	case AIC_OP_ADC:
+	case AIC_OP_BMOV:
+		if (fmt1_ins->parity != 0) {
+			fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+		}
+		fmt1_ins->parity = 0;
+		/* FALLTHROUGH */
+	case AIC_OP_ROL:
+	{
+		int i, count;
+
+		/* Calculate odd parity for the instruction */
+		for (i = 0, count = 0; i < 31; i++) {
+			uint32_t mask;
+
+			mask = 0x01 << i;
+			if ((instr.integer & mask) != 0)
+				count++;
+		}
+		if ((count & 0x01) == 0)
+			instr.format1.parity = 1;
+
+		/* The sequencer is a little endian cpu */
+		instr.integer = ahd_htole32(instr.integer);
+		ahd_outsb(ahd, SEQRAM, instr.bytes, 4);
+		break;
+	}
+	default:
+		panic("Unknown opcode encountered in seq program");
+		break;
+	}
+}
+
+static int
+ahd_probe_stack_size(struct ahd_softc *ahd)
+{
+	int last_probe;
+
+	last_probe = 0;
+	while (1) {
+		int i;
+
+		/*
+		 * We avoid using 0 as a pattern to avoid
+		 * confusion if the stack implementation
+		 * "back-fills" with zeros when "poping'
+		 * entries.
+		 */
+		for (i = 1; i <= last_probe+1; i++) {
+		       ahd_outb(ahd, STACK, i & 0xFF);
+		       ahd_outb(ahd, STACK, (i >> 8) & 0xFF);
+		}
+
+		/* Verify */
+		for (i = last_probe+1; i > 0; i--) {
+			u_int stack_entry;
+
+			stack_entry = ahd_inb(ahd, STACK)
+				    |(ahd_inb(ahd, STACK) << 8);
+			if (stack_entry != i)
+				goto sized;
+		}
+		last_probe++;
+	}
+sized:
+	return (last_probe);
+}
+
+int
+ahd_print_register(const ahd_reg_parse_entry_t *table, u_int num_entries,
+		   const char *name, u_int address, u_int value,
+		   u_int *cur_column, u_int wrap_point)
+{
+	int	printed;
+	u_int	printed_mask;
+
+	if (cur_column != NULL && *cur_column >= wrap_point) {
+		printk("\n");
+		*cur_column = 0;
+	}
+	printed = printk("%s[0x%x]", name, value);
+	if (table == NULL) {
+		printed += printk(" ");
+		*cur_column += printed;
+		return (printed);
+	}
+	printed_mask = 0;
+	while (printed_mask != 0xFF) {
+		int entry;
+
+		for (entry = 0; entry < num_entries; entry++) {
+			if (((value & table[entry].mask)
+			  != table[entry].value)
+			 || ((printed_mask & table[entry].mask)
+			  == table[entry].mask))
+				continue;
+
+			printed += printk("%s%s",
+					  printed_mask == 0 ? ":(" : "|",
+					  table[entry].name);
+			printed_mask |= table[entry].mask;
+			
+			break;
+		}
+		if (entry >= num_entries)
+			break;
+	}
+	if (printed_mask != 0)
+		printed += printk(") ");
+	else
+		printed += printk(" ");
+	if (cur_column != NULL)
+		*cur_column += printed;
+	return (printed);
+}
+
+void
+ahd_dump_card_state(struct ahd_softc *ahd)
+{
+	struct scb	*scb;
+	ahd_mode_state	 saved_modes;
+	u_int		 dffstat;
+	int		 paused;
+	u_int		 scb_index;
+	u_int		 saved_scb_index;
+	u_int		 cur_col;
+	int		 i;
+
+	if (ahd_is_paused(ahd)) {
+		paused = 1;
+	} else {
+		paused = 0;
+		ahd_pause(ahd);
+	}
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	printk(">>>>>>>>>>>>>>>>>> Dump Card State Begins <<<<<<<<<<<<<<<<<\n"
+	       "%s: Dumping Card State at program address 0x%x Mode 0x%x\n",
+	       ahd_name(ahd), 
+	       ahd_inw(ahd, CURADDR),
+	       ahd_build_mode_state(ahd, ahd->saved_src_mode,
+				    ahd->saved_dst_mode));
+	if (paused)
+		printk("Card was paused\n");
+
+	if (ahd_check_cmdcmpltqueues(ahd))
+		printk("Completions are pending\n");
+
+	/*
+	 * Mode independent registers.
+	 */
+	cur_col = 0;
+	ahd_intstat_print(ahd_inb(ahd, INTSTAT), &cur_col, 50);
+	ahd_seloid_print(ahd_inb(ahd, SELOID), &cur_col, 50);
+	ahd_selid_print(ahd_inb(ahd, SELID), &cur_col, 50);
+	ahd_hs_mailbox_print(ahd_inb(ahd, LOCAL_HS_MAILBOX), &cur_col, 50);
+	ahd_intctl_print(ahd_inb(ahd, INTCTL), &cur_col, 50);
+	ahd_seqintstat_print(ahd_inb(ahd, SEQINTSTAT), &cur_col, 50);
+	ahd_saved_mode_print(ahd_inb(ahd, SAVED_MODE), &cur_col, 50);
+	ahd_dffstat_print(ahd_inb(ahd, DFFSTAT), &cur_col, 50);
+	ahd_scsisigi_print(ahd_inb(ahd, SCSISIGI), &cur_col, 50);
+	ahd_scsiphase_print(ahd_inb(ahd, SCSIPHASE), &cur_col, 50);
+	ahd_scsibus_print(ahd_inb(ahd, SCSIBUS), &cur_col, 50);
+	ahd_lastphase_print(ahd_inb(ahd, LASTPHASE), &cur_col, 50);
+	ahd_scsiseq0_print(ahd_inb(ahd, SCSISEQ0), &cur_col, 50);
+	ahd_scsiseq1_print(ahd_inb(ahd, SCSISEQ1), &cur_col, 50);
+	ahd_seqctl0_print(ahd_inb(ahd, SEQCTL0), &cur_col, 50);
+	ahd_seqintctl_print(ahd_inb(ahd, SEQINTCTL), &cur_col, 50);
+	ahd_seq_flags_print(ahd_inb(ahd, SEQ_FLAGS), &cur_col, 50);
+	ahd_seq_flags2_print(ahd_inb(ahd, SEQ_FLAGS2), &cur_col, 50);
+	ahd_qfreeze_count_print(ahd_inw(ahd, QFREEZE_COUNT), &cur_col, 50);
+	ahd_kernel_qfreeze_count_print(ahd_inw(ahd, KERNEL_QFREEZE_COUNT),
+				       &cur_col, 50);
+	ahd_mk_message_scb_print(ahd_inw(ahd, MK_MESSAGE_SCB), &cur_col, 50);
+	ahd_mk_message_scsiid_print(ahd_inb(ahd, MK_MESSAGE_SCSIID),
+				    &cur_col, 50);
+	ahd_sstat0_print(ahd_inb(ahd, SSTAT0), &cur_col, 50);
+	ahd_sstat1_print(ahd_inb(ahd, SSTAT1), &cur_col, 50);
+	ahd_sstat2_print(ahd_inb(ahd, SSTAT2), &cur_col, 50);
+	ahd_sstat3_print(ahd_inb(ahd, SSTAT3), &cur_col, 50);
+	ahd_perrdiag_print(ahd_inb(ahd, PERRDIAG), &cur_col, 50);
+	ahd_simode1_print(ahd_inb(ahd, SIMODE1), &cur_col, 50);
+	ahd_lqistat0_print(ahd_inb(ahd, LQISTAT0), &cur_col, 50);
+	ahd_lqistat1_print(ahd_inb(ahd, LQISTAT1), &cur_col, 50);
+	ahd_lqistat2_print(ahd_inb(ahd, LQISTAT2), &cur_col, 50);
+	ahd_lqostat0_print(ahd_inb(ahd, LQOSTAT0), &cur_col, 50);
+	ahd_lqostat1_print(ahd_inb(ahd, LQOSTAT1), &cur_col, 50);
+	ahd_lqostat2_print(ahd_inb(ahd, LQOSTAT2), &cur_col, 50);
+	printk("\n");
+	printk("\nSCB Count = %d CMDS_PENDING = %d LASTSCB 0x%x "
+	       "CURRSCB 0x%x NEXTSCB 0x%x\n",
+	       ahd->scb_data.numscbs, ahd_inw(ahd, CMDS_PENDING),
+	       ahd_inw(ahd, LASTSCB), ahd_inw(ahd, CURRSCB),
+	       ahd_inw(ahd, NEXTSCB));
+	cur_col = 0;
+	/* QINFIFO */
+	ahd_search_qinfifo(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+			   CAM_LUN_WILDCARD, SCB_LIST_NULL,
+			   ROLE_UNKNOWN, /*status*/0, SEARCH_PRINT);
+	saved_scb_index = ahd_get_scbptr(ahd);
+	printk("Pending list:");
+	i = 0;
+	LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+		if (i++ > AHD_SCB_MAX)
+			break;
+		cur_col = printk("\n%3d FIFO_USE[0x%x] ", SCB_GET_TAG(scb),
+				 ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT));
+		ahd_set_scbptr(ahd, SCB_GET_TAG(scb));
+		ahd_scb_control_print(ahd_inb_scbram(ahd, SCB_CONTROL),
+				      &cur_col, 60);
+		ahd_scb_scsiid_print(ahd_inb_scbram(ahd, SCB_SCSIID),
+				     &cur_col, 60);
+	}
+	printk("\nTotal %d\n", i);
+
+	printk("Kernel Free SCB list: ");
+	i = 0;
+	TAILQ_FOREACH(scb, &ahd->scb_data.free_scbs, links.tqe) {
+		struct scb *list_scb;
+
+		list_scb = scb;
+		do {
+			printk("%d ", SCB_GET_TAG(list_scb));
+			list_scb = LIST_NEXT(list_scb, collision_links);
+		} while (list_scb && i++ < AHD_SCB_MAX);
+	}
+
+	LIST_FOREACH(scb, &ahd->scb_data.any_dev_free_scb_list, links.le) {
+		if (i++ > AHD_SCB_MAX)
+			break;
+		printk("%d ", SCB_GET_TAG(scb));
+	}
+	printk("\n");
+
+	printk("Sequencer Complete DMA-inprog list: ");
+	scb_index = ahd_inw(ahd, COMPLETE_SCB_DMAINPROG_HEAD);
+	i = 0;
+	while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+		ahd_set_scbptr(ahd, scb_index);
+		printk("%d ", scb_index);
+		scb_index = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+	}
+	printk("\n");
+
+	printk("Sequencer Complete list: ");
+	scb_index = ahd_inw(ahd, COMPLETE_SCB_HEAD);
+	i = 0;
+	while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+		ahd_set_scbptr(ahd, scb_index);
+		printk("%d ", scb_index);
+		scb_index = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+	}
+	printk("\n");
+
+	
+	printk("Sequencer DMA-Up and Complete list: ");
+	scb_index = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+	i = 0;
+	while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+		ahd_set_scbptr(ahd, scb_index);
+		printk("%d ", scb_index);
+		scb_index = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+	}
+	printk("\n");
+	printk("Sequencer On QFreeze and Complete list: ");
+	scb_index = ahd_inw(ahd, COMPLETE_ON_QFREEZE_HEAD);
+	i = 0;
+	while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+		ahd_set_scbptr(ahd, scb_index);
+		printk("%d ", scb_index);
+		scb_index = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
+	}
+	printk("\n");
+	ahd_set_scbptr(ahd, saved_scb_index);
+	dffstat = ahd_inb(ahd, DFFSTAT);
+	for (i = 0; i < 2; i++) {
+#ifdef AHD_DEBUG
+		struct scb *fifo_scb;
+#endif
+		u_int	    fifo_scbptr;
+
+		ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+		fifo_scbptr = ahd_get_scbptr(ahd);
+		printk("\n\n%s: FIFO%d %s, LONGJMP == 0x%x, SCB 0x%x\n",
+		       ahd_name(ahd), i,
+		       (dffstat & (FIFO0FREE << i)) ? "Free" : "Active",
+		       ahd_inw(ahd, LONGJMP_ADDR), fifo_scbptr);
+		cur_col = 0;
+		ahd_seqimode_print(ahd_inb(ahd, SEQIMODE), &cur_col, 50);
+		ahd_seqintsrc_print(ahd_inb(ahd, SEQINTSRC), &cur_col, 50);
+		ahd_dfcntrl_print(ahd_inb(ahd, DFCNTRL), &cur_col, 50);
+		ahd_dfstatus_print(ahd_inb(ahd, DFSTATUS), &cur_col, 50);
+		ahd_sg_cache_shadow_print(ahd_inb(ahd, SG_CACHE_SHADOW),
+					  &cur_col, 50);
+		ahd_sg_state_print(ahd_inb(ahd, SG_STATE), &cur_col, 50);
+		ahd_dffsxfrctl_print(ahd_inb(ahd, DFFSXFRCTL), &cur_col, 50);
+		ahd_soffcnt_print(ahd_inb(ahd, SOFFCNT), &cur_col, 50);
+		ahd_mdffstat_print(ahd_inb(ahd, MDFFSTAT), &cur_col, 50);
+		if (cur_col > 50) {
+			printk("\n");
+			cur_col = 0;
+		}
+		cur_col += printk("SHADDR = 0x%x%x, SHCNT = 0x%x ",
+				  ahd_inl(ahd, SHADDR+4),
+				  ahd_inl(ahd, SHADDR),
+				  (ahd_inb(ahd, SHCNT)
+				| (ahd_inb(ahd, SHCNT + 1) << 8)
+				| (ahd_inb(ahd, SHCNT + 2) << 16)));
+		if (cur_col > 50) {
+			printk("\n");
+			cur_col = 0;
+		}
+		cur_col += printk("HADDR = 0x%x%x, HCNT = 0x%x ",
+				  ahd_inl(ahd, HADDR+4),
+				  ahd_inl(ahd, HADDR),
+				  (ahd_inb(ahd, HCNT)
+				| (ahd_inb(ahd, HCNT + 1) << 8)
+				| (ahd_inb(ahd, HCNT + 2) << 16)));
+		ahd_ccsgctl_print(ahd_inb(ahd, CCSGCTL), &cur_col, 50);
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_SG) != 0) {
+			fifo_scb = ahd_lookup_scb(ahd, fifo_scbptr);
+			if (fifo_scb != NULL)
+				ahd_dump_sglist(fifo_scb);
+		}
+#endif
+	}
+	printk("\nLQIN: ");
+	for (i = 0; i < 20; i++)
+		printk("0x%x ", ahd_inb(ahd, LQIN + i));
+	printk("\n");
+	ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+	printk("%s: LQISTATE = 0x%x, LQOSTATE = 0x%x, OPTIONMODE = 0x%x\n",
+	       ahd_name(ahd), ahd_inb(ahd, LQISTATE), ahd_inb(ahd, LQOSTATE),
+	       ahd_inb(ahd, OPTIONMODE));
+	printk("%s: OS_SPACE_CNT = 0x%x MAXCMDCNT = 0x%x\n",
+	       ahd_name(ahd), ahd_inb(ahd, OS_SPACE_CNT),
+	       ahd_inb(ahd, MAXCMDCNT));
+	printk("%s: SAVED_SCSIID = 0x%x SAVED_LUN = 0x%x\n",
+	       ahd_name(ahd), ahd_inb(ahd, SAVED_SCSIID),
+	       ahd_inb(ahd, SAVED_LUN));
+	ahd_simode0_print(ahd_inb(ahd, SIMODE0), &cur_col, 50);
+	printk("\n");
+	ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+	cur_col = 0;
+	ahd_ccscbctl_print(ahd_inb(ahd, CCSCBCTL), &cur_col, 50);
+	printk("\n");
+	ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+	printk("%s: REG0 == 0x%x, SINDEX = 0x%x, DINDEX = 0x%x\n",
+	       ahd_name(ahd), ahd_inw(ahd, REG0), ahd_inw(ahd, SINDEX),
+	       ahd_inw(ahd, DINDEX));
+	printk("%s: SCBPTR == 0x%x, SCB_NEXT == 0x%x, SCB_NEXT2 == 0x%x\n",
+	       ahd_name(ahd), ahd_get_scbptr(ahd),
+	       ahd_inw_scbram(ahd, SCB_NEXT),
+	       ahd_inw_scbram(ahd, SCB_NEXT2));
+	printk("CDB %x %x %x %x %x %x\n",
+	       ahd_inb_scbram(ahd, SCB_CDB_STORE),
+	       ahd_inb_scbram(ahd, SCB_CDB_STORE+1),
+	       ahd_inb_scbram(ahd, SCB_CDB_STORE+2),
+	       ahd_inb_scbram(ahd, SCB_CDB_STORE+3),
+	       ahd_inb_scbram(ahd, SCB_CDB_STORE+4),
+	       ahd_inb_scbram(ahd, SCB_CDB_STORE+5));
+	printk("STACK:");
+	for (i = 0; i < ahd->stack_size; i++) {
+		ahd->saved_stack[i] =
+		    ahd_inb(ahd, STACK)|(ahd_inb(ahd, STACK) << 8);
+		printk(" 0x%x", ahd->saved_stack[i]);
+	}
+	for (i = ahd->stack_size-1; i >= 0; i--) {
+		ahd_outb(ahd, STACK, ahd->saved_stack[i] & 0xFF);
+		ahd_outb(ahd, STACK, (ahd->saved_stack[i] >> 8) & 0xFF);
+	}
+	printk("\n<<<<<<<<<<<<<<<<< Dump Card State Ends >>>>>>>>>>>>>>>>>>\n");
+	ahd_restore_modes(ahd, saved_modes);
+	if (paused == 0)
+		ahd_unpause(ahd);
+}
+
+#if 0
+void
+ahd_dump_scbs(struct ahd_softc *ahd)
+{
+	ahd_mode_state saved_modes;
+	u_int	       saved_scb_index;
+	int	       i;
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	saved_scb_index = ahd_get_scbptr(ahd);
+	for (i = 0; i < AHD_SCB_MAX; i++) {
+		ahd_set_scbptr(ahd, i);
+		printk("%3d", i);
+		printk("(CTRL 0x%x ID 0x%x N 0x%x N2 0x%x SG 0x%x, RSG 0x%x)\n",
+		       ahd_inb_scbram(ahd, SCB_CONTROL),
+		       ahd_inb_scbram(ahd, SCB_SCSIID),
+		       ahd_inw_scbram(ahd, SCB_NEXT),
+		       ahd_inw_scbram(ahd, SCB_NEXT2),
+		       ahd_inl_scbram(ahd, SCB_SGPTR),
+		       ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR));
+	}
+	printk("\n");
+	ahd_set_scbptr(ahd, saved_scb_index);
+	ahd_restore_modes(ahd, saved_modes);
+}
+#endif  /*  0  */
+
+/**************************** Flexport Logic **********************************/
+/*
+ * Read count 16bit words from 16bit word address start_addr from the
+ * SEEPROM attached to the controller, into buf, using the controller's
+ * SEEPROM reading state machine.  Optionally treat the data as a byte
+ * stream in terms of byte order.
+ */
+int
+ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+		 u_int start_addr, u_int count, int bytestream)
+{
+	u_int cur_addr;
+	u_int end_addr;
+	int   error;
+
+	/*
+	 * If we never make it through the loop even once,
+	 * we were passed invalid arguments.
+	 */
+	error = EINVAL;
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	end_addr = start_addr + count;
+	for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
+
+		ahd_outb(ahd, SEEADR, cur_addr);
+		ahd_outb(ahd, SEECTL, SEEOP_READ | SEESTART);
+		
+		error = ahd_wait_seeprom(ahd);
+		if (error)
+			break;
+		if (bytestream != 0) {
+			uint8_t *bytestream_ptr;
+
+			bytestream_ptr = (uint8_t *)buf;
+			*bytestream_ptr++ = ahd_inb(ahd, SEEDAT);
+			*bytestream_ptr = ahd_inb(ahd, SEEDAT+1);
+		} else {
+			/*
+			 * ahd_inw() already handles machine byte order.
+			 */
+			*buf = ahd_inw(ahd, SEEDAT);
+		}
+		buf++;
+	}
+	return (error);
+}
+
+/*
+ * Write count 16bit words from buf, into SEEPROM attache to the
+ * controller starting at 16bit word address start_addr, using the
+ * controller's SEEPROM writing state machine.
+ */
+int
+ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+		  u_int start_addr, u_int count)
+{
+	u_int cur_addr;
+	u_int end_addr;
+	int   error;
+	int   retval;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	error = ENOENT;
+
+	/* Place the chip into write-enable mode */
+	ahd_outb(ahd, SEEADR, SEEOP_EWEN_ADDR);
+	ahd_outb(ahd, SEECTL, SEEOP_EWEN | SEESTART);
+	error = ahd_wait_seeprom(ahd);
+	if (error)
+		return (error);
+
+	/*
+	 * Write the data.  If we don't get through the loop at
+	 * least once, the arguments were invalid.
+	 */
+	retval = EINVAL;
+	end_addr = start_addr + count;
+	for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
+		ahd_outw(ahd, SEEDAT, *buf++);
+		ahd_outb(ahd, SEEADR, cur_addr);
+		ahd_outb(ahd, SEECTL, SEEOP_WRITE | SEESTART);
+		
+		retval = ahd_wait_seeprom(ahd);
+		if (retval)
+			break;
+	}
+
+	/*
+	 * Disable writes.
+	 */
+	ahd_outb(ahd, SEEADR, SEEOP_EWDS_ADDR);
+	ahd_outb(ahd, SEECTL, SEEOP_EWDS | SEESTART);
+	error = ahd_wait_seeprom(ahd);
+	if (error)
+		return (error);
+	return (retval);
+}
+
+/*
+ * Wait ~100us for the serial eeprom to satisfy our request.
+ */
+static int
+ahd_wait_seeprom(struct ahd_softc *ahd)
+{
+	int cnt;
+
+	cnt = 5000;
+	while ((ahd_inb(ahd, SEESTAT) & (SEEARBACK|SEEBUSY)) != 0 && --cnt)
+		ahd_delay(5);
+
+	if (cnt == 0)
+		return (ETIMEDOUT);
+	return (0);
+}
+
+/*
+ * Validate the two checksums in the per_channel
+ * vital product data struct.
+ */
+static int
+ahd_verify_vpd_cksum(struct vpd_config *vpd)
+{
+	int i;
+	int maxaddr;
+	uint32_t checksum;
+	uint8_t *vpdarray;
+
+	vpdarray = (uint8_t *)vpd;
+	maxaddr = offsetof(struct vpd_config, vpd_checksum);
+	checksum = 0;
+	for (i = offsetof(struct vpd_config, resource_type); i < maxaddr; i++)
+		checksum = checksum + vpdarray[i];
+	if (checksum == 0
+	 || (-checksum & 0xFF) != vpd->vpd_checksum)
+		return (0);
+
+	checksum = 0;
+	maxaddr = offsetof(struct vpd_config, checksum);
+	for (i = offsetof(struct vpd_config, default_target_flags);
+	     i < maxaddr; i++)
+		checksum = checksum + vpdarray[i];
+	if (checksum == 0
+	 || (-checksum & 0xFF) != vpd->checksum)
+		return (0);
+	return (1);
+}
+
+int
+ahd_verify_cksum(struct seeprom_config *sc)
+{
+	int i;
+	int maxaddr;
+	uint32_t checksum;
+	uint16_t *scarray;
+
+	maxaddr = (sizeof(*sc)/2) - 1;
+	checksum = 0;
+	scarray = (uint16_t *)sc;
+
+	for (i = 0; i < maxaddr; i++)
+		checksum = checksum + scarray[i];
+	if (checksum == 0
+	 || (checksum & 0xFFFF) != sc->checksum) {
+		return (0);
+	} else {
+		return (1);
+	}
+}
+
+int
+ahd_acquire_seeprom(struct ahd_softc *ahd)
+{
+	/*
+	 * We should be able to determine the SEEPROM type
+	 * from the flexport logic, but unfortunately not
+	 * all implementations have this logic and there is
+	 * no programatic method for determining if the logic
+	 * is present.
+	 */
+	return (1);
+#if 0
+	uint8_t	seetype;
+	int	error;
+
+	error = ahd_read_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, &seetype);
+	if (error != 0
+         || ((seetype & FLX_ROMSTAT_SEECFG) == FLX_ROMSTAT_SEE_NONE))
+		return (0);
+	return (1);
+#endif
+}
+
+void
+ahd_release_seeprom(struct ahd_softc *ahd)
+{
+	/* Currently a no-op */
+}
+
+/*
+ * Wait at most 2 seconds for flexport arbitration to succeed.
+ */
+static int
+ahd_wait_flexport(struct ahd_softc *ahd)
+{
+	int cnt;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	cnt = 1000000 * 2 / 5;
+	while ((ahd_inb(ahd, BRDCTL) & FLXARBACK) == 0 && --cnt)
+		ahd_delay(5);
+
+	if (cnt == 0)
+		return (ETIMEDOUT);
+	return (0);
+}
+
+int
+ahd_write_flexport(struct ahd_softc *ahd, u_int addr, u_int value)
+{
+	int error;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	if (addr > 7)
+		panic("ahd_write_flexport: address out of range");
+	ahd_outb(ahd, BRDCTL, BRDEN|(addr << 3));
+	error = ahd_wait_flexport(ahd);
+	if (error != 0)
+		return (error);
+	ahd_outb(ahd, BRDDAT, value);
+	ahd_flush_device_writes(ahd);
+	ahd_outb(ahd, BRDCTL, BRDSTB|BRDEN|(addr << 3));
+	ahd_flush_device_writes(ahd);
+	ahd_outb(ahd, BRDCTL, BRDEN|(addr << 3));
+	ahd_flush_device_writes(ahd);
+	ahd_outb(ahd, BRDCTL, 0);
+	ahd_flush_device_writes(ahd);
+	return (0);
+}
+
+int
+ahd_read_flexport(struct ahd_softc *ahd, u_int addr, uint8_t *value)
+{
+	int	error;
+
+	AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+	if (addr > 7)
+		panic("ahd_read_flexport: address out of range");
+	ahd_outb(ahd, BRDCTL, BRDRW|BRDEN|(addr << 3));
+	error = ahd_wait_flexport(ahd);
+	if (error != 0)
+		return (error);
+	*value = ahd_inb(ahd, BRDDAT);
+	ahd_outb(ahd, BRDCTL, 0);
+	ahd_flush_device_writes(ahd);
+	return (0);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHD_TARGET_MODE
+cam_status
+ahd_find_tmode_devs(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb,
+		    struct ahd_tmode_tstate **tstate,
+		    struct ahd_tmode_lstate **lstate,
+		    int notfound_failure)
+{
+
+	if ((ahd->features & AHD_TARGETMODE) == 0)
+		return (CAM_REQ_INVALID);
+
+	/*
+	 * Handle the 'black hole' device that sucks up
+	 * requests to unattached luns on enabled targets.
+	 */
+	if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+	 && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+		*tstate = NULL;
+		*lstate = ahd->black_hole;
+	} else {
+		u_int max_id;
+
+		max_id = (ahd->features & AHD_WIDE) ? 16 : 8;
+		if (ccb->ccb_h.target_id >= max_id)
+			return (CAM_TID_INVALID);
+
+		if (ccb->ccb_h.target_lun >= AHD_NUM_LUNS)
+			return (CAM_LUN_INVALID);
+
+		*tstate = ahd->enabled_targets[ccb->ccb_h.target_id];
+		*lstate = NULL;
+		if (*tstate != NULL)
+			*lstate =
+			    (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+	}
+
+	if (notfound_failure != 0 && *lstate == NULL)
+		return (CAM_PATH_INVALID);
+
+	return (CAM_REQ_CMP);
+}
+
+void
+ahd_handle_en_lun(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb)
+{
+#if NOT_YET
+	struct	   ahd_tmode_tstate *tstate;
+	struct	   ahd_tmode_lstate *lstate;
+	struct	   ccb_en_lun *cel;
+	cam_status status;
+	u_int	   target;
+	u_int	   lun;
+	u_int	   target_mask;
+	u_long	   s;
+	char	   channel;
+
+	status = ahd_find_tmode_devs(ahd, sim, ccb, &tstate, &lstate,
+				     /*notfound_failure*/FALSE);
+
+	if (status != CAM_REQ_CMP) {
+		ccb->ccb_h.status = status;
+		return;
+	}
+
+	if ((ahd->features & AHD_MULTIROLE) != 0) {
+		u_int	   our_id;
+
+		our_id = ahd->our_id;
+		if (ccb->ccb_h.target_id != our_id) {
+			if ((ahd->features & AHD_MULTI_TID) != 0
+		   	 && (ahd->flags & AHD_INITIATORROLE) != 0) {
+				/*
+				 * Only allow additional targets if
+				 * the initiator role is disabled.
+				 * The hardware cannot handle a re-select-in
+				 * on the initiator id during a re-select-out
+				 * on a different target id.
+				 */
+				status = CAM_TID_INVALID;
+			} else if ((ahd->flags & AHD_INITIATORROLE) != 0
+				|| ahd->enabled_luns > 0) {
+				/*
+				 * Only allow our target id to change
+				 * if the initiator role is not configured
+				 * and there are no enabled luns which
+				 * are attached to the currently registered
+				 * scsi id.
+				 */
+				status = CAM_TID_INVALID;
+			}
+		}
+	}
+
+	if (status != CAM_REQ_CMP) {
+		ccb->ccb_h.status = status;
+		return;
+	}
+
+	/*
+	 * We now have an id that is valid.
+	 * If we aren't in target mode, switch modes.
+	 */
+	if ((ahd->flags & AHD_TARGETROLE) == 0
+	 && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+		u_long	s;
+
+		printk("Configuring Target Mode\n");
+		ahd_lock(ahd, &s);
+		if (LIST_FIRST(&ahd->pending_scbs) != NULL) {
+			ccb->ccb_h.status = CAM_BUSY;
+			ahd_unlock(ahd, &s);
+			return;
+		}
+		ahd->flags |= AHD_TARGETROLE;
+		if ((ahd->features & AHD_MULTIROLE) == 0)
+			ahd->flags &= ~AHD_INITIATORROLE;
+		ahd_pause(ahd);
+		ahd_loadseq(ahd);
+		ahd_restart(ahd);
+		ahd_unlock(ahd, &s);
+	}
+	cel = &ccb->cel;
+	target = ccb->ccb_h.target_id;
+	lun = ccb->ccb_h.target_lun;
+	channel = SIM_CHANNEL(ahd, sim);
+	target_mask = 0x01 << target;
+	if (channel == 'B')
+		target_mask <<= 8;
+
+	if (cel->enable != 0) {
+		u_int scsiseq1;
+
+		/* Are we already enabled?? */
+		if (lstate != NULL) {
+			xpt_print_path(ccb->ccb_h.path);
+			printk("Lun already enabled\n");
+			ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+			return;
+		}
+
+		if (cel->grp6_len != 0
+		 || cel->grp7_len != 0) {
+			/*
+			 * Don't (yet?) support vendor
+			 * specific commands.
+			 */
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			printk("Non-zero Group Codes\n");
+			return;
+		}
+
+		/*
+		 * Seems to be okay.
+		 * Setup our data structures.
+		 */
+		if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+			tstate = ahd_alloc_tstate(ahd, target, channel);
+			if (tstate == NULL) {
+				xpt_print_path(ccb->ccb_h.path);
+				printk("Couldn't allocate tstate\n");
+				ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+				return;
+			}
+		}
+		lstate = kzalloc(sizeof(*lstate), GFP_ATOMIC);
+		if (lstate == NULL) {
+			xpt_print_path(ccb->ccb_h.path);
+			printk("Couldn't allocate lstate\n");
+			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+			return;
+		}
+		status = xpt_create_path(&lstate->path, /*periph*/NULL,
+					 xpt_path_path_id(ccb->ccb_h.path),
+					 xpt_path_target_id(ccb->ccb_h.path),
+					 xpt_path_lun_id(ccb->ccb_h.path));
+		if (status != CAM_REQ_CMP) {
+			kfree(lstate);
+			xpt_print_path(ccb->ccb_h.path);
+			printk("Couldn't allocate path\n");
+			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+			return;
+		}
+		SLIST_INIT(&lstate->accept_tios);
+		SLIST_INIT(&lstate->immed_notifies);
+		ahd_lock(ahd, &s);
+		ahd_pause(ahd);
+		if (target != CAM_TARGET_WILDCARD) {
+			tstate->enabled_luns[lun] = lstate;
+			ahd->enabled_luns++;
+
+			if ((ahd->features & AHD_MULTI_TID) != 0) {
+				u_int targid_mask;
+
+				targid_mask = ahd_inw(ahd, TARGID);
+				targid_mask |= target_mask;
+				ahd_outw(ahd, TARGID, targid_mask);
+				ahd_update_scsiid(ahd, targid_mask);
+			} else {
+				u_int our_id;
+				char  channel;
+
+				channel = SIM_CHANNEL(ahd, sim);
+				our_id = SIM_SCSI_ID(ahd, sim);
+
+				/*
+				 * This can only happen if selections
+				 * are not enabled
+				 */
+				if (target != our_id) {
+					u_int sblkctl;
+					char  cur_channel;
+					int   swap;
+
+					sblkctl = ahd_inb(ahd, SBLKCTL);
+					cur_channel = (sblkctl & SELBUSB)
+						    ? 'B' : 'A';
+					if ((ahd->features & AHD_TWIN) == 0)
+						cur_channel = 'A';
+					swap = cur_channel != channel;
+					ahd->our_id = target;
+
+					if (swap)
+						ahd_outb(ahd, SBLKCTL,
+							 sblkctl ^ SELBUSB);
+
+					ahd_outb(ahd, SCSIID, target);
+
+					if (swap)
+						ahd_outb(ahd, SBLKCTL, sblkctl);
+				}
+			}
+		} else
+			ahd->black_hole = lstate;
+		/* Allow select-in operations */
+		if (ahd->black_hole != NULL && ahd->enabled_luns > 0) {
+			scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+			scsiseq1 |= ENSELI;
+			ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq1);
+			scsiseq1 = ahd_inb(ahd, SCSISEQ1);
+			scsiseq1 |= ENSELI;
+			ahd_outb(ahd, SCSISEQ1, scsiseq1);
+		}
+		ahd_unpause(ahd);
+		ahd_unlock(ahd, &s);
+		ccb->ccb_h.status = CAM_REQ_CMP;
+		xpt_print_path(ccb->ccb_h.path);
+		printk("Lun now enabled for target mode\n");
+	} else {
+		struct scb *scb;
+		int i, empty;
+
+		if (lstate == NULL) {
+			ccb->ccb_h.status = CAM_LUN_INVALID;
+			return;
+		}
+
+		ahd_lock(ahd, &s);
+		
+		ccb->ccb_h.status = CAM_REQ_CMP;
+		LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+			struct ccb_hdr *ccbh;
+
+			ccbh = &scb->io_ctx->ccb_h;
+			if (ccbh->func_code == XPT_CONT_TARGET_IO
+			 && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+				printk("CTIO pending\n");
+				ccb->ccb_h.status = CAM_REQ_INVALID;
+				ahd_unlock(ahd, &s);
+				return;
+			}
+		}
+
+		if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+			printk("ATIOs pending\n");
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+		}
+
+		if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+			printk("INOTs pending\n");
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+		}
+
+		if (ccb->ccb_h.status != CAM_REQ_CMP) {
+			ahd_unlock(ahd, &s);
+			return;
+		}
+
+		xpt_print_path(ccb->ccb_h.path);
+		printk("Target mode disabled\n");
+		xpt_free_path(lstate->path);
+		kfree(lstate);
+
+		ahd_pause(ahd);
+		/* Can we clean up the target too? */
+		if (target != CAM_TARGET_WILDCARD) {
+			tstate->enabled_luns[lun] = NULL;
+			ahd->enabled_luns--;
+			for (empty = 1, i = 0; i < 8; i++)
+				if (tstate->enabled_luns[i] != NULL) {
+					empty = 0;
+					break;
+				}
+
+			if (empty) {
+				ahd_free_tstate(ahd, target, channel,
+						/*force*/FALSE);
+				if (ahd->features & AHD_MULTI_TID) {
+					u_int targid_mask;
+
+					targid_mask = ahd_inw(ahd, TARGID);
+					targid_mask &= ~target_mask;
+					ahd_outw(ahd, TARGID, targid_mask);
+					ahd_update_scsiid(ahd, targid_mask);
+				}
+			}
+		} else {
+
+			ahd->black_hole = NULL;
+
+			/*
+			 * We can't allow selections without
+			 * our black hole device.
+			 */
+			empty = TRUE;
+		}
+		if (ahd->enabled_luns == 0) {
+			/* Disallow select-in */
+			u_int scsiseq1;
+
+			scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+			scsiseq1 &= ~ENSELI;
+			ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq1);
+			scsiseq1 = ahd_inb(ahd, SCSISEQ1);
+			scsiseq1 &= ~ENSELI;
+			ahd_outb(ahd, SCSISEQ1, scsiseq1);
+
+			if ((ahd->features & AHD_MULTIROLE) == 0) {
+				printk("Configuring Initiator Mode\n");
+				ahd->flags &= ~AHD_TARGETROLE;
+				ahd->flags |= AHD_INITIATORROLE;
+				ahd_pause(ahd);
+				ahd_loadseq(ahd);
+				ahd_restart(ahd);
+				/*
+				 * Unpaused.  The extra unpause
+				 * that follows is harmless.
+				 */
+			}
+		}
+		ahd_unpause(ahd);
+		ahd_unlock(ahd, &s);
+	}
+#endif
+}
+
+static void
+ahd_update_scsiid(struct ahd_softc *ahd, u_int targid_mask)
+{
+#if NOT_YET
+	u_int scsiid_mask;
+	u_int scsiid;
+
+	if ((ahd->features & AHD_MULTI_TID) == 0)
+		panic("ahd_update_scsiid called on non-multitid unit\n");
+
+	/*
+	 * Since we will rely on the TARGID mask
+	 * for selection enables, ensure that OID
+	 * in SCSIID is not set to some other ID
+	 * that we don't want to allow selections on.
+	 */
+	if ((ahd->features & AHD_ULTRA2) != 0)
+		scsiid = ahd_inb(ahd, SCSIID_ULTRA2);
+	else
+		scsiid = ahd_inb(ahd, SCSIID);
+	scsiid_mask = 0x1 << (scsiid & OID);
+	if ((targid_mask & scsiid_mask) == 0) {
+		u_int our_id;
+
+		/* ffs counts from 1 */
+		our_id = ffs(targid_mask);
+		if (our_id == 0)
+			our_id = ahd->our_id;
+		else
+			our_id--;
+		scsiid &= TID;
+		scsiid |= our_id;
+	}
+	if ((ahd->features & AHD_ULTRA2) != 0)
+		ahd_outb(ahd, SCSIID_ULTRA2, scsiid);
+	else
+		ahd_outb(ahd, SCSIID, scsiid);
+#endif
+}
+
+static void
+ahd_run_tqinfifo(struct ahd_softc *ahd, int paused)
+{
+	struct target_cmd *cmd;
+
+	ahd_sync_tqinfifo(ahd, BUS_DMASYNC_POSTREAD);
+	while ((cmd = &ahd->targetcmds[ahd->tqinfifonext])->cmd_valid != 0) {
+
+		/*
+		 * Only advance through the queue if we
+		 * have the resources to process the command.
+		 */
+		if (ahd_handle_target_cmd(ahd, cmd) != 0)
+			break;
+
+		cmd->cmd_valid = 0;
+		ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+				ahd->shared_data_map.dmamap,
+				ahd_targetcmd_offset(ahd, ahd->tqinfifonext),
+				sizeof(struct target_cmd),
+				BUS_DMASYNC_PREREAD);
+		ahd->tqinfifonext++;
+
+		/*
+		 * Lazily update our position in the target mode incoming
+		 * command queue as seen by the sequencer.
+		 */
+		if ((ahd->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+			u_int hs_mailbox;
+
+			hs_mailbox = ahd_inb(ahd, HS_MAILBOX);
+			hs_mailbox &= ~HOST_TQINPOS;
+			hs_mailbox |= ahd->tqinfifonext & HOST_TQINPOS;
+			ahd_outb(ahd, HS_MAILBOX, hs_mailbox);
+		}
+	}
+}
+
+static int
+ahd_handle_target_cmd(struct ahd_softc *ahd, struct target_cmd *cmd)
+{
+	struct	  ahd_tmode_tstate *tstate;
+	struct	  ahd_tmode_lstate *lstate;
+	struct	  ccb_accept_tio *atio;
+	uint8_t *byte;
+	int	  initiator;
+	int	  target;
+	int	  lun;
+
+	initiator = SCSIID_TARGET(ahd, cmd->scsiid);
+	target = SCSIID_OUR_ID(cmd->scsiid);
+	lun    = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+	byte = cmd->bytes;
+	tstate = ahd->enabled_targets[target];
+	lstate = NULL;
+	if (tstate != NULL)
+		lstate = tstate->enabled_luns[lun];
+
+	/*
+	 * Commands for disabled luns go to the black hole driver.
+	 */
+	if (lstate == NULL)
+		lstate = ahd->black_hole;
+
+	atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+	if (atio == NULL) {
+		ahd->flags |= AHD_TQINFIFO_BLOCKED;
+		/*
+		 * Wait for more ATIOs from the peripheral driver for this lun.
+		 */
+		return (1);
+	} else
+		ahd->flags &= ~AHD_TQINFIFO_BLOCKED;
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_TQIN) != 0)
+		printk("Incoming command from %d for %d:%d%s\n",
+		       initiator, target, lun,
+		       lstate == ahd->black_hole ? "(Black Holed)" : "");
+#endif
+	SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+	if (lstate == ahd->black_hole) {
+		/* Fill in the wildcards */
+		atio->ccb_h.target_id = target;
+		atio->ccb_h.target_lun = lun;
+	}
+
+	/*
+	 * Package it up and send it off to
+	 * whomever has this lun enabled.
+	 */
+	atio->sense_len = 0;
+	atio->init_id = initiator;
+	if (byte[0] != 0xFF) {
+		/* Tag was included */
+		atio->tag_action = *byte++;
+		atio->tag_id = *byte++;
+		atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+	} else {
+		atio->ccb_h.flags = 0;
+	}
+	byte++;
+
+	/* Okay.  Now determine the cdb size based on the command code */
+	switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+	case 0:
+		atio->cdb_len = 6;
+		break;
+	case 1:
+	case 2:
+		atio->cdb_len = 10;
+		break;
+	case 4:
+		atio->cdb_len = 16;
+		break;
+	case 5:
+		atio->cdb_len = 12;
+		break;
+	case 3:
+	default:
+		/* Only copy the opcode. */
+		atio->cdb_len = 1;
+		printk("Reserved or VU command code type encountered\n");
+		break;
+	}
+	
+	memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+	atio->ccb_h.status |= CAM_CDB_RECVD;
+
+	if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+		/*
+		 * We weren't allowed to disconnect.
+		 * We're hanging on the bus until a
+		 * continue target I/O comes in response
+		 * to this accept tio.
+		 */
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_TQIN) != 0)
+			printk("Received Immediate Command %d:%d:%d - %p\n",
+			       initiator, target, lun, ahd->pending_device);
+#endif
+		ahd->pending_device = lstate;
+		ahd_freeze_ccb((union ccb *)atio);
+		atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+	}
+	xpt_done((union ccb*)atio);
+	return (0);
+}
+
+#endif
diff --git a/drivers/scsi/aic7xxx/aic79xx_inline.h b/drivers/scsi/aic7xxx/aic79xx_inline.h
new file mode 100644
index 0000000..09335a3
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_inline.h
@@ -0,0 +1,172 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx_inline.h#59 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC79XX_INLINE_H_
+#define _AIC79XX_INLINE_H_
+
+/******************************** Debugging ***********************************/
+static inline char *ahd_name(struct ahd_softc *ahd);
+
+static inline char *ahd_name(struct ahd_softc *ahd)
+{
+	return (ahd->name);
+}
+
+/************************ Sequencer Execution Control *************************/
+static inline void ahd_known_modes(struct ahd_softc *ahd,
+				     ahd_mode src, ahd_mode dst);
+static inline ahd_mode_state ahd_build_mode_state(struct ahd_softc *ahd,
+						    ahd_mode src,
+						    ahd_mode dst);
+static inline void ahd_extract_mode_state(struct ahd_softc *ahd,
+					    ahd_mode_state state,
+					    ahd_mode *src, ahd_mode *dst);
+
+void ahd_set_modes(struct ahd_softc *ahd, ahd_mode src,
+		   ahd_mode dst);
+ahd_mode_state ahd_save_modes(struct ahd_softc *ahd);
+void ahd_restore_modes(struct ahd_softc *ahd,
+		       ahd_mode_state state);
+int  ahd_is_paused(struct ahd_softc *ahd);
+void ahd_pause(struct ahd_softc *ahd);
+void ahd_unpause(struct ahd_softc *ahd);
+
+static inline void
+ahd_known_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+	ahd->src_mode = src;
+	ahd->dst_mode = dst;
+	ahd->saved_src_mode = src;
+	ahd->saved_dst_mode = dst;
+}
+
+static inline ahd_mode_state
+ahd_build_mode_state(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+	return ((src << SRC_MODE_SHIFT) | (dst << DST_MODE_SHIFT));
+}
+
+static inline void
+ahd_extract_mode_state(struct ahd_softc *ahd, ahd_mode_state state,
+		       ahd_mode *src, ahd_mode *dst)
+{
+	*src = (state & SRC_MODE) >> SRC_MODE_SHIFT;
+	*dst = (state & DST_MODE) >> DST_MODE_SHIFT;
+}
+
+/*********************** Scatter Gather List Handling *************************/
+void	*ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
+		      void *sgptr, dma_addr_t addr,
+		      bus_size_t len, int last);
+
+/************************** Memory mapping routines ***************************/
+static inline size_t	ahd_sg_size(struct ahd_softc *ahd);
+
+void	ahd_sync_sglist(struct ahd_softc *ahd,
+			struct scb *scb, int op);
+
+static inline size_t ahd_sg_size(struct ahd_softc *ahd)
+{
+	if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+		return (sizeof(struct ahd_dma64_seg));
+	return (sizeof(struct ahd_dma_seg));
+}
+
+/*********************** Miscellaneous Support Functions ***********************/
+struct ahd_initiator_tinfo *
+	ahd_fetch_transinfo(struct ahd_softc *ahd,
+			    char channel, u_int our_id,
+			    u_int remote_id,
+			    struct ahd_tmode_tstate **tstate);
+uint16_t
+	ahd_inw(struct ahd_softc *ahd, u_int port);
+void	ahd_outw(struct ahd_softc *ahd, u_int port,
+		 u_int value);
+uint32_t
+	ahd_inl(struct ahd_softc *ahd, u_int port);
+void	ahd_outl(struct ahd_softc *ahd, u_int port,
+		 uint32_t value);
+uint64_t
+	ahd_inq(struct ahd_softc *ahd, u_int port);
+void	ahd_outq(struct ahd_softc *ahd, u_int port,
+		 uint64_t value);
+u_int	ahd_get_scbptr(struct ahd_softc *ahd);
+void	ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr);
+u_int	ahd_inb_scbram(struct ahd_softc *ahd, u_int offset);
+u_int	ahd_inw_scbram(struct ahd_softc *ahd, u_int offset);
+struct scb *
+	ahd_lookup_scb(struct ahd_softc *ahd, u_int tag);
+void	ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb);
+
+static inline uint8_t *ahd_get_sense_buf(struct ahd_softc *ahd,
+					  struct scb *scb);
+static inline uint32_t ahd_get_sense_bufaddr(struct ahd_softc *ahd,
+					      struct scb *scb);
+
+#if 0 /* unused */
+
+#define AHD_COPY_COL_IDX(dst, src)				\
+do {								\
+	dst->hscb->scsiid = src->hscb->scsiid;			\
+	dst->hscb->lun = src->hscb->lun;			\
+} while (0)
+
+#endif
+
+static inline uint8_t *
+ahd_get_sense_buf(struct ahd_softc *ahd, struct scb *scb)
+{
+	return (scb->sense_data);
+}
+
+static inline uint32_t
+ahd_get_sense_bufaddr(struct ahd_softc *ahd, struct scb *scb)
+{
+	return (scb->sense_busaddr);
+}
+
+/************************** Interrupt Processing ******************************/
+int	ahd_intr(struct ahd_softc *ahd);
+
+#endif  /* _AIC79XX_INLINE_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c
new file mode 100644
index 0000000..2588b8f
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -0,0 +1,2885 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#171 $
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-2000 Justin T. Gibbs.
+ * Copyright (c) 1997-1999 Doug Ledford
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include <scsi/scsicam.h>
+
+static struct scsi_transport_template *ahd_linux_transport_template = NULL;
+
+#include <linux/init.h>		/* __setup */
+#include <linux/mm.h>		/* For fetching system memory size */
+#include <linux/blkdev.h>		/* For block_size() */
+#include <linux/delay.h>	/* For ssleep/msleep */
+#include <linux/device.h>
+#include <linux/slab.h>
+
+/*
+ * Bucket size for counting good commands in between bad ones.
+ */
+#define AHD_LINUX_ERR_THRESH	1000
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC79XX_RESET_DELAY_MS
+#define AIC79XX_RESET_DELAY CONFIG_AIC79XX_RESET_DELAY_MS
+#else
+#define AIC79XX_RESET_DELAY 5000
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic79xx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * The tag_commands is an array of 16 to allow for wide and twin adapters.
+ * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15
+ * for channel 1.
+ */
+typedef struct {
+	uint16_t tag_commands[16];	/* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0			tagged queuing disabled
+ * 1 <= n <= 253	n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full.  For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred).  On some devices, queue full is returned for a temporary
+ * resource shortage.  These devices will return queue full at varying
+ * depths.  The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic79xx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3.  It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ *       to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic79xx_tag_info[] =
+{
+	{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+	{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC79XX_CMDS_PER_DEVICE
+#define AIC79XX_CMDS_PER_DEVICE CONFIG_AIC79XX_CMDS_PER_DEVICE
+#else
+#define AIC79XX_CMDS_PER_DEVICE AHD_MAX_QUEUE
+#endif
+
+#define AIC79XX_CONFIGED_TAG_COMMANDS {					\
+	AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,		\
+	AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,		\
+	AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,		\
+	AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,		\
+	AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,		\
+	AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,		\
+	AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,		\
+	AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE		\
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic79xx_tag_info[] =
+{
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS},
+	{AIC79XX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * The I/O cell on the chip is very configurable in respect to its analog
+ * characteristics.  Set the defaults here; they can be overriden with
+ * the proper insmod parameters.
+ */
+struct ahd_linux_iocell_opts
+{
+	uint8_t	precomp;
+	uint8_t	slewrate;
+	uint8_t amplitude;
+};
+#define AIC79XX_DEFAULT_PRECOMP		0xFF
+#define AIC79XX_DEFAULT_SLEWRATE	0xFF
+#define AIC79XX_DEFAULT_AMPLITUDE	0xFF
+#define AIC79XX_DEFAULT_IOOPTS			\
+{						\
+	AIC79XX_DEFAULT_PRECOMP,		\
+	AIC79XX_DEFAULT_SLEWRATE,		\
+	AIC79XX_DEFAULT_AMPLITUDE		\
+}
+#define AIC79XX_PRECOMP_INDEX	0
+#define AIC79XX_SLEWRATE_INDEX	1
+#define AIC79XX_AMPLITUDE_INDEX	2
+static const struct ahd_linux_iocell_opts aic79xx_iocell_info[] =
+{
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS,
+	AIC79XX_DEFAULT_IOOPTS
+};
+
+/*
+ * 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
+
+void
+ahd_print_path(struct ahd_softc *ahd, struct scb *scb)
+{
+	printk("(scsi%d:%c:%d:%d): ",
+	       ahd->platform_data->host->host_no,
+	       scb != NULL ? SCB_GET_CHANNEL(ahd, scb) : 'X',
+	       scb != NULL ? SCB_GET_TARGET(ahd, scb) : -1,
+	       scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ adapters
+ *       cards in the system.  This should be fixed.  Exceptions to this
+ *       rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic79xx_no_reset;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ *     0 == Use whatever is in the SEEPROM or default to off
+ *     1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic79xx_extended;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers.  This is somewhat
+ * dubious at best.  To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ *   0	   = Shut off PCI parity check
+ *   non-0 = Enable PCI parity check
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt.  So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number.  That will invert the 0 which will result in
+ * -1.
+ */
+static uint32_t aic79xx_pci_parity = ~0;
+
+/*
+ * There are lots of broken chipsets in the world.  Some of them will
+ * violate the PCI spec when we issue byte sized memory writes to our
+ * controller.  I/O mapped register access, if allowed by the given
+ * platform, will work in almost all cases.
+ */
+uint32_t aic79xx_allow_memio = ~0;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ *   0 - 256ms
+ *   1 - 128ms
+ *   2 - 64ms
+ *   3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static uint32_t aic79xx_seltime;
+
+/*
+ * Certain devices do not perform any aging on commands.  Should the
+ * device be saturated by commands in one portion of the disk, it is
+ * possible for transactions on far away sectors to never be serviced.
+ * To handle these devices, we can periodically send an ordered tag to
+ * force all outstanding transactions to be serviced prior to a new
+ * transaction.
+ */
+static uint32_t aic79xx_periodic_otag;
+
+/* Some storage boxes are using an LSI chip which has a bug making it
+ * impossible to use aic79xx Rev B chip in 320 speeds.  The following
+ * storage boxes have been reported to be buggy:
+ * EonStor 3U 16-Bay: U16U-G3A3
+ * EonStor 2U 12-Bay: U12U-G3A3
+ * SentinelRAID: 2500F R5 / R6
+ * SentinelRAID: 2500F R1
+ * SentinelRAID: 2500F/1500F
+ * SentinelRAID: 150F
+ * 
+ * To get around this LSI bug, you can set your board to 160 mode
+ * or you can enable the SLOWCRC bit.
+ */
+uint32_t aic79xx_slowcrc;
+
+/*
+ * Module information and settable options.
+ */
+static char *aic79xx = NULL;
+
+MODULE_AUTHOR("Maintainer: Hannes Reinecke <hare@suse.de>");
+MODULE_DESCRIPTION("Adaptec AIC790X U320 SCSI Host Bus Adapter driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(AIC79XX_DRIVER_VERSION);
+module_param(aic79xx, charp, 0444);
+MODULE_PARM_DESC(aic79xx,
+"period-delimited options string:\n"
+"	verbose			Enable verbose/diagnostic logging\n"
+"	allow_memio		Allow device registers to be memory mapped\n"
+"	debug			Bitmask of debug values to enable\n"
+"	no_reset		Suppress initial bus resets\n"
+"	extended		Enable extended geometry on all controllers\n"
+"	periodic_otag		Send an ordered tagged transaction\n"
+"				periodically to prevent tag starvation.\n"
+"				This may be required by some older disk\n"
+"				or drives/RAID arrays.\n"
+"	tag_info:<tag_str>	Set per-target tag depth\n"
+"	global_tag_depth:<int>	Global tag depth for all targets on all buses\n"
+"	slewrate:<slewrate_list>Set the signal slew rate (0-15).\n"
+"	precomp:<pcomp_list>	Set the signal precompensation (0-7).\n"
+"	amplitude:<int>		Set the signal amplitude (0-7).\n"
+"	seltime:<int>		Selection Timeout:\n"
+"				(0/256ms,1/128ms,2/64ms,3/32ms)\n"
+"	slowcrc			Turn on the SLOWCRC bit (Rev B only)\n"		 
+"\n"
+"	Sample modprobe configuration file:\n"
+"	#	Enable verbose logging\n"
+"	#	Set tag depth on Controller 2/Target 2 to 10 tags\n"
+"	#	Shorten the selection timeout to 128ms\n"
+"\n"
+"	options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n"
+);
+
+static void ahd_linux_handle_scsi_status(struct ahd_softc *,
+					 struct scsi_device *,
+					 struct scb *);
+static void ahd_linux_queue_cmd_complete(struct ahd_softc *ahd,
+					 struct scsi_cmnd *cmd);
+static int ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd);
+static void ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd);
+static u_int ahd_linux_user_tagdepth(struct ahd_softc *ahd,
+				     struct ahd_devinfo *devinfo);
+static void ahd_linux_device_queue_depth(struct scsi_device *);
+static int ahd_linux_run_command(struct ahd_softc*,
+				 struct ahd_linux_device *,
+				 struct scsi_cmnd *);
+static void ahd_linux_setup_tag_info_global(char *p);
+static int  aic79xx_setup(char *c);
+static void ahd_freeze_simq(struct ahd_softc *ahd);
+static void ahd_release_simq(struct ahd_softc *ahd);
+
+static int ahd_linux_unit;
+
+
+/************************** OS Utility Wrappers *******************************/
+void ahd_delay(long);
+void
+ahd_delay(long usec)
+{
+	/*
+	 * udelay on Linux can have problems for
+	 * multi-millisecond waits.  Wait at most
+	 * 1024us per call.
+	 */
+	while (usec > 0) {
+		udelay(usec % 1024);
+		usec -= 1024;
+	}
+}
+
+
+/***************************** Low Level I/O **********************************/
+uint8_t ahd_inb(struct ahd_softc * ahd, long port);
+void ahd_outb(struct ahd_softc * ahd, long port, uint8_t val);
+void ahd_outw_atomic(struct ahd_softc * ahd,
+				     long port, uint16_t val);
+void ahd_outsb(struct ahd_softc * ahd, long port,
+			       uint8_t *, int count);
+void ahd_insb(struct ahd_softc * ahd, long port,
+			       uint8_t *, int count);
+
+uint8_t
+ahd_inb(struct ahd_softc * ahd, long port)
+{
+	uint8_t x;
+
+	if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+		x = readb(ahd->bshs[0].maddr + port);
+	} else {
+		x = inb(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+	}
+	mb();
+	return (x);
+}
+
+#if 0 /* unused */
+static uint16_t
+ahd_inw_atomic(struct ahd_softc * ahd, long port)
+{
+	uint8_t x;
+
+	if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+		x = readw(ahd->bshs[0].maddr + port);
+	} else {
+		x = inw(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+	}
+	mb();
+	return (x);
+}
+#endif
+
+void
+ahd_outb(struct ahd_softc * ahd, long port, uint8_t val)
+{
+	if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+		writeb(val, ahd->bshs[0].maddr + port);
+	} else {
+		outb(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+	}
+	mb();
+}
+
+void
+ahd_outw_atomic(struct ahd_softc * ahd, long port, uint16_t val)
+{
+	if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+		writew(val, ahd->bshs[0].maddr + port);
+	} else {
+		outw(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+	}
+	mb();
+}
+
+void
+ahd_outsb(struct ahd_softc * ahd, long port, uint8_t *array, int count)
+{
+	int i;
+
+	/*
+	 * There is probably a more efficient way to do this on Linux
+	 * but we don't use this for anything speed critical and this
+	 * should work.
+	 */
+	for (i = 0; i < count; i++)
+		ahd_outb(ahd, port, *array++);
+}
+
+void
+ahd_insb(struct ahd_softc * ahd, long port, uint8_t *array, int count)
+{
+	int i;
+
+	/*
+	 * There is probably a more efficient way to do this on Linux
+	 * but we don't use this for anything speed critical and this
+	 * should work.
+	 */
+	for (i = 0; i < count; i++)
+		*array++ = ahd_inb(ahd, port);
+}
+
+/******************************* PCI Routines *********************************/
+uint32_t
+ahd_pci_read_config(ahd_dev_softc_t pci, int reg, int width)
+{
+	switch (width) {
+	case 1:
+	{
+		uint8_t retval;
+
+		pci_read_config_byte(pci, reg, &retval);
+		return (retval);
+	}
+	case 2:
+	{
+		uint16_t retval;
+		pci_read_config_word(pci, reg, &retval);
+		return (retval);
+	}
+	case 4:
+	{
+		uint32_t retval;
+		pci_read_config_dword(pci, reg, &retval);
+		return (retval);
+	}
+	default:
+		panic("ahd_pci_read_config: Read size too big");
+		/* NOTREACHED */
+		return (0);
+	}
+}
+
+void
+ahd_pci_write_config(ahd_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+	switch (width) {
+	case 1:
+		pci_write_config_byte(pci, reg, value);
+		break;
+	case 2:
+		pci_write_config_word(pci, reg, value);
+		break;
+	case 4:
+		pci_write_config_dword(pci, reg, value);
+		break;
+	default:
+		panic("ahd_pci_write_config: Write size too big");
+		/* NOTREACHED */
+	}
+}
+
+/****************************** Inlines ***************************************/
+static void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*);
+
+static void
+ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+	struct scsi_cmnd *cmd;
+
+	cmd = scb->io_ctx;
+	ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE);
+	scsi_dma_unmap(cmd);
+}
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahd, cmd)						\
+	(((scmd_id(cmd) << TID_SHIFT) & TID) | (ahd)->our_id)
+
+/*
+ * Return a string describing the driver.
+ */
+static const char *
+ahd_linux_info(struct Scsi_Host *host)
+{
+	static char buffer[512];
+	char	ahd_info[256];
+	char   *bp;
+	struct ahd_softc *ahd;
+
+	bp = &buffer[0];
+	ahd = *(struct ahd_softc **)host->hostdata;
+	memset(bp, 0, sizeof(buffer));
+	strcpy(bp, "Adaptec AIC79XX PCI-X SCSI HBA DRIVER, Rev " AIC79XX_DRIVER_VERSION "\n"
+			"        <");
+	strcat(bp, ahd->description);
+	strcat(bp, ">\n"
+			"        ");
+	ahd_controller_info(ahd, ahd_info);
+	strcat(bp, ahd_info);
+
+	return (bp);
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+static int
+ahd_linux_queue_lck(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd *))
+{
+	struct	 ahd_softc *ahd;
+	struct	 ahd_linux_device *dev = scsi_transport_device_data(cmd->device);
+	int rtn = SCSI_MLQUEUE_HOST_BUSY;
+
+	ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
+
+	cmd->scsi_done = scsi_done;
+	cmd->result = CAM_REQ_INPROG << 16;
+	rtn = ahd_linux_run_command(ahd, dev, cmd);
+
+	return rtn;
+}
+
+static DEF_SCSI_QCMD(ahd_linux_queue)
+
+static struct scsi_target **
+ahd_linux_target_in_softc(struct scsi_target *starget)
+{
+	struct	ahd_softc *ahd =
+		*((struct ahd_softc **)dev_to_shost(&starget->dev)->hostdata);
+	unsigned int target_offset;
+
+	target_offset = starget->id;
+	if (starget->channel != 0)
+		target_offset += 8;
+
+	return &ahd->platform_data->starget[target_offset];
+}
+
+static int
+ahd_linux_target_alloc(struct scsi_target *starget)
+{
+	struct	ahd_softc *ahd =
+		*((struct ahd_softc **)dev_to_shost(&starget->dev)->hostdata);
+	struct seeprom_config *sc = ahd->seep_config;
+	unsigned long flags;
+	struct scsi_target **ahd_targp = ahd_linux_target_in_softc(starget);
+	struct ahd_devinfo devinfo;
+	struct ahd_initiator_tinfo *tinfo;
+	struct ahd_tmode_tstate *tstate;
+	char channel = starget->channel + 'A';
+
+	ahd_lock(ahd, &flags);
+
+	BUG_ON(*ahd_targp != NULL);
+
+	*ahd_targp = starget;
+
+	if (sc) {
+		int flags = sc->device_flags[starget->id];
+
+		tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+					    starget->id, &tstate);
+
+		if ((flags  & CFPACKETIZED) == 0) {
+			/* don't negotiate packetized (IU) transfers */
+			spi_max_iu(starget) = 0;
+		} else {
+			if ((ahd->features & AHD_RTI) == 0)
+				spi_rti(starget) = 0;
+		}
+
+		if ((flags & CFQAS) == 0)
+			spi_max_qas(starget) = 0;
+
+		/* Transinfo values have been set to BIOS settings */
+		spi_max_width(starget) = (flags & CFWIDEB) ? 1 : 0;
+		spi_min_period(starget) = tinfo->user.period;
+		spi_max_offset(starget) = tinfo->user.offset;
+	}
+
+	tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id,
+				    starget->id, &tstate);
+	ahd_compile_devinfo(&devinfo, ahd->our_id, starget->id,
+			    CAM_LUN_WILDCARD, channel,
+			    ROLE_INITIATOR);
+	ahd_set_syncrate(ahd, &devinfo, 0, 0, 0,
+			 AHD_TRANS_GOAL, /*paused*/FALSE);
+	ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+		      AHD_TRANS_GOAL, /*paused*/FALSE);
+	ahd_unlock(ahd, &flags);
+
+	return 0;
+}
+
+static void
+ahd_linux_target_destroy(struct scsi_target *starget)
+{
+	struct scsi_target **ahd_targp = ahd_linux_target_in_softc(starget);
+
+	*ahd_targp = NULL;
+}
+
+static int
+ahd_linux_slave_alloc(struct scsi_device *sdev)
+{
+	struct	ahd_softc *ahd =
+		*((struct ahd_softc **)sdev->host->hostdata);
+	struct ahd_linux_device *dev;
+
+	if (bootverbose)
+		printk("%s: Slave Alloc %d\n", ahd_name(ahd), sdev->id);
+
+	dev = scsi_transport_device_data(sdev);
+	memset(dev, 0, sizeof(*dev));
+
+	/*
+	 * We start out life using untagged
+	 * transactions of which we allow one.
+	 */
+	dev->openings = 1;
+
+	/*
+	 * Set maxtags to 0.  This will be changed if we
+	 * later determine that we are dealing with
+	 * a tagged queuing capable device.
+	 */
+	dev->maxtags = 0;
+	
+	return (0);
+}
+
+static int
+ahd_linux_slave_configure(struct scsi_device *sdev)
+{
+	struct	ahd_softc *ahd;
+
+	ahd = *((struct ahd_softc **)sdev->host->hostdata);
+	if (bootverbose)
+		sdev_printk(KERN_INFO, sdev, "Slave Configure\n");
+
+	ahd_linux_device_queue_depth(sdev);
+
+	/* Initial Domain Validation */
+	if (!spi_initial_dv(sdev->sdev_target))
+		spi_dv_device(sdev);
+
+	return 0;
+}
+
+#if defined(__i386__)
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+static int
+ahd_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+		    sector_t capacity, int geom[])
+{
+	uint8_t *bh;
+	int	 heads;
+	int	 sectors;
+	int	 cylinders;
+	int	 ret;
+	int	 extended;
+	struct	 ahd_softc *ahd;
+
+	ahd = *((struct ahd_softc **)sdev->host->hostdata);
+
+	bh = scsi_bios_ptable(bdev);
+	if (bh) {
+		ret = scsi_partsize(bh, capacity,
+				    &geom[2], &geom[0], &geom[1]);
+		kfree(bh);
+		if (ret != -1)
+			return (ret);
+	}
+	heads = 64;
+	sectors = 32;
+	cylinders = aic_sector_div(capacity, heads, sectors);
+
+	if (aic79xx_extended != 0)
+		extended = 1;
+	else
+		extended = (ahd->flags & AHD_EXTENDED_TRANS_A) != 0;
+	if (extended && cylinders >= 1024) {
+		heads = 255;
+		sectors = 63;
+		cylinders = aic_sector_div(capacity, heads, sectors);
+	}
+	geom[0] = heads;
+	geom[1] = sectors;
+	geom[2] = cylinders;
+	return (0);
+}
+#endif
+
+/*
+ * Abort the current SCSI command(s).
+ */
+static int
+ahd_linux_abort(struct scsi_cmnd *cmd)
+{
+	int error;
+	
+	error = ahd_linux_queue_abort_cmd(cmd);
+
+	return error;
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+static int
+ahd_linux_dev_reset(struct scsi_cmnd *cmd)
+{
+	struct ahd_softc *ahd;
+	struct ahd_linux_device *dev;
+	struct scb *reset_scb;
+	u_int  cdb_byte;
+	int    retval = SUCCESS;
+	int    paused;
+	int    wait;
+	struct	ahd_initiator_tinfo *tinfo;
+	struct	ahd_tmode_tstate *tstate;
+	unsigned long flags;
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	reset_scb = NULL;
+	paused = FALSE;
+	wait = FALSE;
+	ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
+
+	scmd_printk(KERN_INFO, cmd,
+		    "Attempting to queue a TARGET RESET message:");
+
+	printk("CDB:");
+	for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++)
+		printk(" 0x%x", cmd->cmnd[cdb_byte]);
+	printk("\n");
+
+	/*
+	 * Determine if we currently own this command.
+	 */
+	dev = scsi_transport_device_data(cmd->device);
+
+	if (dev == NULL) {
+		/*
+		 * No target device for this command exists,
+		 * so we must not still own the command.
+		 */
+		scmd_printk(KERN_INFO, cmd, "Is not an active device\n");
+		return SUCCESS;
+	}
+
+	/*
+	 * Generate us a new SCB
+	 */
+	reset_scb = ahd_get_scb(ahd, AHD_NEVER_COL_IDX);
+	if (!reset_scb) {
+		scmd_printk(KERN_INFO, cmd, "No SCB available\n");
+		return FAILED;
+	}
+
+	tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+				    cmd->device->id, &tstate);
+	reset_scb->io_ctx = cmd;
+	reset_scb->platform_data->dev = dev;
+	reset_scb->sg_count = 0;
+	ahd_set_residual(reset_scb, 0);
+	ahd_set_sense_residual(reset_scb, 0);
+	reset_scb->platform_data->xfer_len = 0;
+	reset_scb->hscb->control = 0;
+	reset_scb->hscb->scsiid = BUILD_SCSIID(ahd,cmd);
+	reset_scb->hscb->lun = cmd->device->lun;
+	reset_scb->hscb->cdb_len = 0;
+	reset_scb->hscb->task_management = SIU_TASKMGMT_LUN_RESET;
+	reset_scb->flags |= SCB_DEVICE_RESET|SCB_RECOVERY_SCB|SCB_ACTIVE;
+	if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+		reset_scb->flags |= SCB_PACKETIZED;
+	} else {
+		reset_scb->hscb->control |= MK_MESSAGE;
+	}
+	dev->openings--;
+	dev->active++;
+	dev->commands_issued++;
+
+	ahd_lock(ahd, &flags);
+
+	LIST_INSERT_HEAD(&ahd->pending_scbs, reset_scb, pending_links);
+	ahd_queue_scb(ahd, reset_scb);
+
+	ahd->platform_data->eh_done = &done;
+	ahd_unlock(ahd, &flags);
+
+	printk("%s: Device reset code sleeping\n", ahd_name(ahd));
+	if (!wait_for_completion_timeout(&done, 5 * HZ)) {
+		ahd_lock(ahd, &flags);
+		ahd->platform_data->eh_done = NULL;
+		ahd_unlock(ahd, &flags);
+		printk("%s: Device reset timer expired (active %d)\n",
+		       ahd_name(ahd), dev->active);
+		retval = FAILED;
+	}
+	printk("%s: Device reset returning 0x%x\n", ahd_name(ahd), retval);
+
+	return (retval);
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+static int
+ahd_linux_bus_reset(struct scsi_cmnd *cmd)
+{
+	struct ahd_softc *ahd;
+	int    found;
+	unsigned long flags;
+
+	ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
+		printk("%s: Bus reset called for cmd %p\n",
+		       ahd_name(ahd), cmd);
+#endif
+	ahd_lock(ahd, &flags);
+
+	found = ahd_reset_channel(ahd, scmd_channel(cmd) + 'A',
+				  /*initiate reset*/TRUE);
+	ahd_unlock(ahd, &flags);
+
+	if (bootverbose)
+		printk("%s: SCSI bus reset delivered. "
+		       "%d SCBs aborted.\n", ahd_name(ahd), found);
+
+	return (SUCCESS);
+}
+
+struct scsi_host_template aic79xx_driver_template = {
+	.module			= THIS_MODULE,
+	.name			= "aic79xx",
+	.proc_name		= "aic79xx",
+	.show_info		= ahd_linux_show_info,
+	.write_info	 	= ahd_proc_write_seeprom,
+	.info			= ahd_linux_info,
+	.queuecommand		= ahd_linux_queue,
+	.eh_abort_handler	= ahd_linux_abort,
+	.eh_device_reset_handler = ahd_linux_dev_reset,
+	.eh_bus_reset_handler	= ahd_linux_bus_reset,
+#if defined(__i386__)
+	.bios_param		= ahd_linux_biosparam,
+#endif
+	.can_queue		= AHD_MAX_QUEUE,
+	.this_id		= -1,
+	.max_sectors		= 8192,
+	.cmd_per_lun		= 2,
+	.use_clustering		= ENABLE_CLUSTERING,
+	.slave_alloc		= ahd_linux_slave_alloc,
+	.slave_configure	= ahd_linux_slave_configure,
+	.target_alloc		= ahd_linux_target_alloc,
+	.target_destroy		= ahd_linux_target_destroy,
+};
+
+/******************************** Bus DMA *************************************/
+int
+ahd_dma_tag_create(struct ahd_softc *ahd, bus_dma_tag_t parent,
+		   bus_size_t alignment, bus_size_t boundary,
+		   dma_addr_t lowaddr, dma_addr_t highaddr,
+		   bus_dma_filter_t *filter, void *filterarg,
+		   bus_size_t maxsize, int nsegments,
+		   bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+	bus_dma_tag_t dmat;
+
+	dmat = kmalloc(sizeof(*dmat), GFP_ATOMIC);
+	if (dmat == NULL)
+		return (ENOMEM);
+
+	/*
+	 * Linux is very simplistic about DMA memory.  For now don't
+	 * maintain all specification information.  Once Linux supplies
+	 * better facilities for doing these operations, or the
+	 * needs of this particular driver change, we might need to do
+	 * more here.
+	 */
+	dmat->alignment = alignment;
+	dmat->boundary = boundary;
+	dmat->maxsize = maxsize;
+	*ret_tag = dmat;
+	return (0);
+}
+
+void
+ahd_dma_tag_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat)
+{
+	kfree(dmat);
+}
+
+int
+ahd_dmamem_alloc(struct ahd_softc *ahd, bus_dma_tag_t dmat, void** vaddr,
+		 int flags, bus_dmamap_t *mapp)
+{
+	*vaddr = pci_alloc_consistent(ahd->dev_softc,
+				      dmat->maxsize, mapp);
+	if (*vaddr == NULL)
+		return (ENOMEM);
+	return(0);
+}
+
+void
+ahd_dmamem_free(struct ahd_softc *ahd, bus_dma_tag_t dmat,
+		void* vaddr, bus_dmamap_t map)
+{
+	pci_free_consistent(ahd->dev_softc, dmat->maxsize,
+			    vaddr, map);
+}
+
+int
+ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map,
+		void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+		void *cb_arg, int flags)
+{
+	/*
+	 * Assume for now that this will only be used during
+	 * initialization and not for per-transaction buffer mapping.
+	 */
+	bus_dma_segment_t stack_sg;
+
+	stack_sg.ds_addr = map;
+	stack_sg.ds_len = dmat->maxsize;
+	cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+	return (0);
+}
+
+void
+ahd_dmamap_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+}
+
+int
+ahd_dmamap_unload(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+	/* Nothing to do */
+	return (0);
+}
+
+/********************* Platform Dependent Functions ***************************/
+static void
+ahd_linux_setup_iocell_info(u_long index, int instance, int targ, int32_t value)
+{
+
+	if ((instance >= 0)
+	 && (instance < ARRAY_SIZE(aic79xx_iocell_info))) {
+		uint8_t *iocell_info;
+
+		iocell_info = (uint8_t*)&aic79xx_iocell_info[instance];
+		iocell_info[index] = value & 0xFFFF;
+		if (bootverbose)
+			printk("iocell[%d:%ld] = %d\n", instance, index, value);
+	}
+}
+
+static void
+ahd_linux_setup_tag_info_global(char *p)
+{
+	int tags, i, j;
+
+	tags = simple_strtoul(p + 1, NULL, 0) & 0xff;
+	printk("Setting Global Tags= %d\n", tags);
+
+	for (i = 0; i < ARRAY_SIZE(aic79xx_tag_info); i++) {
+		for (j = 0; j < AHD_NUM_TARGETS; j++) {
+			aic79xx_tag_info[i].tag_commands[j] = tags;
+		}
+	}
+}
+
+static void
+ahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value)
+{
+
+	if ((instance >= 0) && (targ >= 0)
+	 && (instance < ARRAY_SIZE(aic79xx_tag_info))
+	 && (targ < AHD_NUM_TARGETS)) {
+		aic79xx_tag_info[instance].tag_commands[targ] = value & 0x1FF;
+		if (bootverbose)
+			printk("tag_info[%d:%d] = %d\n", instance, targ, value);
+	}
+}
+
+static char *
+ahd_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth,
+		       void (*callback)(u_long, int, int, int32_t),
+		       u_long callback_arg)
+{
+	char	*tok_end;
+	char	*tok_end2;
+	int      i;
+	int      instance;
+	int	 targ;
+	int	 done;
+	char	 tok_list[] = {'.', ',', '{', '}', '\0'};
+
+	/* All options use a ':' name/arg separator */
+	if (*opt_arg != ':')
+		return (opt_arg);
+	opt_arg++;
+	instance = -1;
+	targ = -1;
+	done = FALSE;
+	/*
+	 * Restore separator that may be in
+	 * the middle of our option argument.
+	 */
+	tok_end = strchr(opt_arg, '\0');
+	if (tok_end < end)
+		*tok_end = ',';
+	while (!done) {
+		switch (*opt_arg) {
+		case '{':
+			if (instance == -1) {
+				instance = 0;
+			} else {
+				if (depth > 1) {
+					if (targ == -1)
+						targ = 0;
+				} else {
+					printk("Malformed Option %s\n",
+					       opt_name);
+					done = TRUE;
+				}
+			}
+			opt_arg++;
+			break;
+		case '}':
+			if (targ != -1)
+				targ = -1;
+			else if (instance != -1)
+				instance = -1;
+			opt_arg++;
+			break;
+		case ',':
+		case '.':
+			if (instance == -1)
+				done = TRUE;
+			else if (targ >= 0)
+				targ++;
+			else if (instance >= 0)
+				instance++;
+			opt_arg++;
+			break;
+		case '\0':
+			done = TRUE;
+			break;
+		default:
+			tok_end = end;
+			for (i = 0; tok_list[i]; i++) {
+				tok_end2 = strchr(opt_arg, tok_list[i]);
+				if ((tok_end2) && (tok_end2 < tok_end))
+					tok_end = tok_end2;
+			}
+			callback(callback_arg, instance, targ,
+				 simple_strtol(opt_arg, NULL, 0));
+			opt_arg = tok_end;
+			break;
+		}
+	}
+	return (opt_arg);
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic79xx=stpwlev:1,extended
+ */
+static int
+aic79xx_setup(char *s)
+{
+	int	i, n;
+	char   *p;
+	char   *end;
+
+	static const struct {
+		const char *name;
+		uint32_t *flag;
+	} options[] = {
+		{ "extended", &aic79xx_extended },
+		{ "no_reset", &aic79xx_no_reset },
+		{ "verbose", &aic79xx_verbose },
+		{ "allow_memio", &aic79xx_allow_memio},
+#ifdef AHD_DEBUG
+		{ "debug", &ahd_debug },
+#endif
+		{ "periodic_otag", &aic79xx_periodic_otag },
+		{ "pci_parity", &aic79xx_pci_parity },
+		{ "seltime", &aic79xx_seltime },
+		{ "tag_info", NULL },
+		{ "global_tag_depth", NULL},
+		{ "slewrate", NULL },
+		{ "precomp", NULL },
+		{ "amplitude", NULL },
+		{ "slowcrc", &aic79xx_slowcrc },
+	};
+
+	end = strchr(s, '\0');
+
+	/*
+	 * XXX ia64 gcc isn't smart enough to know that ARRAY_SIZE
+	 * will never be 0 in this case.
+	 */
+	n = 0;
+
+	while ((p = strsep(&s, ",.")) != NULL) {
+		if (*p == '\0')
+			continue;
+		for (i = 0; i < ARRAY_SIZE(options); i++) {
+
+			n = strlen(options[i].name);
+			if (strncmp(options[i].name, p, n) == 0)
+				break;
+		}
+		if (i == ARRAY_SIZE(options))
+			continue;
+
+		if (strncmp(p, "global_tag_depth", n) == 0) {
+			ahd_linux_setup_tag_info_global(p + n);
+		} else if (strncmp(p, "tag_info", n) == 0) {
+			s = ahd_parse_brace_option("tag_info", p + n, end,
+			    2, ahd_linux_setup_tag_info, 0);
+		} else if (strncmp(p, "slewrate", n) == 0) {
+			s = ahd_parse_brace_option("slewrate",
+			    p + n, end, 1, ahd_linux_setup_iocell_info,
+			    AIC79XX_SLEWRATE_INDEX);
+		} else if (strncmp(p, "precomp", n) == 0) {
+			s = ahd_parse_brace_option("precomp",
+			    p + n, end, 1, ahd_linux_setup_iocell_info,
+			    AIC79XX_PRECOMP_INDEX);
+		} else if (strncmp(p, "amplitude", n) == 0) {
+			s = ahd_parse_brace_option("amplitude",
+			    p + n, end, 1, ahd_linux_setup_iocell_info,
+			    AIC79XX_AMPLITUDE_INDEX);
+		} else if (p[n] == ':') {
+			*(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
+		} else if (!strncmp(p, "verbose", n)) {
+			*(options[i].flag) = 1;
+		} else {
+			*(options[i].flag) ^= 0xFFFFFFFF;
+		}
+	}
+	return 1;
+}
+
+__setup("aic79xx=", aic79xx_setup);
+
+uint32_t aic79xx_verbose;
+
+int
+ahd_linux_register_host(struct ahd_softc *ahd, struct scsi_host_template *template)
+{
+	char	buf[80];
+	struct	Scsi_Host *host;
+	char	*new_name;
+	u_long	s;
+	int	retval;
+
+	template->name = ahd->description;
+	host = scsi_host_alloc(template, sizeof(struct ahd_softc *));
+	if (host == NULL)
+		return (ENOMEM);
+
+	*((struct ahd_softc **)host->hostdata) = ahd;
+	ahd->platform_data->host = host;
+	host->can_queue = AHD_MAX_QUEUE;
+	host->cmd_per_lun = 2;
+	host->sg_tablesize = AHD_NSEG;
+	host->this_id = ahd->our_id;
+	host->irq = ahd->platform_data->irq;
+	host->max_id = (ahd->features & AHD_WIDE) ? 16 : 8;
+	host->max_lun = AHD_NUM_LUNS;
+	host->max_channel = 0;
+	host->sg_tablesize = AHD_NSEG;
+	ahd_lock(ahd, &s);
+	ahd_set_unit(ahd, ahd_linux_unit++);
+	ahd_unlock(ahd, &s);
+	sprintf(buf, "scsi%d", host->host_no);
+	new_name = kmalloc(strlen(buf) + 1, GFP_ATOMIC);
+	if (new_name != NULL) {
+		strcpy(new_name, buf);
+		ahd_set_name(ahd, new_name);
+	}
+	host->unique_id = ahd->unit;
+	ahd_linux_initialize_scsi_bus(ahd);
+	ahd_intr_enable(ahd, TRUE);
+
+	host->transportt = ahd_linux_transport_template;
+
+	retval = scsi_add_host(host, &ahd->dev_softc->dev);
+	if (retval) {
+		printk(KERN_WARNING "aic79xx: scsi_add_host failed\n");
+		scsi_host_put(host);
+		return retval;
+	}
+
+	scsi_scan_host(host);
+	return 0;
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+static void
+ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd)
+{
+	u_int target_id;
+	u_int numtarg;
+	unsigned long s;
+
+	target_id = 0;
+	numtarg = 0;
+
+	if (aic79xx_no_reset != 0)
+		ahd->flags &= ~AHD_RESET_BUS_A;
+
+	if ((ahd->flags & AHD_RESET_BUS_A) != 0)
+		ahd_reset_channel(ahd, 'A', /*initiate_reset*/TRUE);
+	else
+		numtarg = (ahd->features & AHD_WIDE) ? 16 : 8;
+
+	ahd_lock(ahd, &s);
+
+	/*
+	 * Force negotiation to async for all targets that
+	 * will not see an initial bus reset.
+	 */
+	for (; target_id < numtarg; target_id++) {
+		struct ahd_devinfo devinfo;
+		struct ahd_initiator_tinfo *tinfo;
+		struct ahd_tmode_tstate *tstate;
+
+		tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+					    target_id, &tstate);
+		ahd_compile_devinfo(&devinfo, ahd->our_id, target_id,
+				    CAM_LUN_WILDCARD, 'A', ROLE_INITIATOR);
+		ahd_update_neg_request(ahd, &devinfo, tstate,
+				       tinfo, AHD_NEG_ALWAYS);
+	}
+	ahd_unlock(ahd, &s);
+	/* Give the bus some time to recover */
+	if ((ahd->flags & AHD_RESET_BUS_A) != 0) {
+		ahd_freeze_simq(ahd);
+		msleep(AIC79XX_RESET_DELAY);
+		ahd_release_simq(ahd);
+	}
+}
+
+int
+ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg)
+{
+	ahd->platform_data =
+	    kzalloc(sizeof(struct ahd_platform_data), GFP_ATOMIC);
+	if (ahd->platform_data == NULL)
+		return (ENOMEM);
+	ahd->platform_data->irq = AHD_LINUX_NOIRQ;
+	ahd_lockinit(ahd);
+	ahd->seltime = (aic79xx_seltime & 0x3) << 4;
+	return (0);
+}
+
+void
+ahd_platform_free(struct ahd_softc *ahd)
+{
+	struct scsi_target *starget;
+	int i;
+
+	if (ahd->platform_data != NULL) {
+		/* destroy all of the device and target objects */
+		for (i = 0; i < AHD_NUM_TARGETS; i++) {
+			starget = ahd->platform_data->starget[i];
+			if (starget != NULL) {
+				ahd->platform_data->starget[i] = NULL;
+			}
+		}
+
+		if (ahd->platform_data->irq != AHD_LINUX_NOIRQ)
+			free_irq(ahd->platform_data->irq, ahd);
+		if (ahd->tags[0] == BUS_SPACE_PIO
+		 && ahd->bshs[0].ioport != 0)
+			release_region(ahd->bshs[0].ioport, 256);
+		if (ahd->tags[1] == BUS_SPACE_PIO
+		 && ahd->bshs[1].ioport != 0)
+			release_region(ahd->bshs[1].ioport, 256);
+		if (ahd->tags[0] == BUS_SPACE_MEMIO
+		 && ahd->bshs[0].maddr != NULL) {
+			iounmap(ahd->bshs[0].maddr);
+			release_mem_region(ahd->platform_data->mem_busaddr,
+					   0x1000);
+		}
+		if (ahd->platform_data->host)
+			scsi_host_put(ahd->platform_data->host);
+
+		kfree(ahd->platform_data);
+	}
+}
+
+void
+ahd_platform_init(struct ahd_softc *ahd)
+{
+	/*
+	 * Lookup and commit any modified IO Cell options.
+	 */
+	if (ahd->unit < ARRAY_SIZE(aic79xx_iocell_info)) {
+		const struct ahd_linux_iocell_opts *iocell_opts;
+
+		iocell_opts = &aic79xx_iocell_info[ahd->unit];
+		if (iocell_opts->precomp != AIC79XX_DEFAULT_PRECOMP)
+			AHD_SET_PRECOMP(ahd, iocell_opts->precomp);
+		if (iocell_opts->slewrate != AIC79XX_DEFAULT_SLEWRATE)
+			AHD_SET_SLEWRATE(ahd, iocell_opts->slewrate);
+		if (iocell_opts->amplitude != AIC79XX_DEFAULT_AMPLITUDE)
+			AHD_SET_AMPLITUDE(ahd, iocell_opts->amplitude);
+	}
+
+}
+
+void
+ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
+{
+	ahd_platform_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+				SCB_GET_CHANNEL(ahd, scb),
+				SCB_GET_LUN(scb), SCB_LIST_NULL,
+				ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev,
+		      struct ahd_devinfo *devinfo, ahd_queue_alg alg)
+{
+	struct ahd_linux_device *dev;
+	int was_queuing;
+	int now_queuing;
+
+	if (sdev == NULL)
+		return;
+
+	dev = scsi_transport_device_data(sdev);
+
+	if (dev == NULL)
+		return;
+	was_queuing = dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED);
+	switch (alg) {
+	default:
+	case AHD_QUEUE_NONE:
+		now_queuing = 0;
+		break; 
+	case AHD_QUEUE_BASIC:
+		now_queuing = AHD_DEV_Q_BASIC;
+		break;
+	case AHD_QUEUE_TAGGED:
+		now_queuing = AHD_DEV_Q_TAGGED;
+		break;
+	}
+	if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) == 0
+	 && (was_queuing != now_queuing)
+	 && (dev->active != 0)) {
+		dev->flags |= AHD_DEV_FREEZE_TIL_EMPTY;
+		dev->qfrozen++;
+	}
+
+	dev->flags &= ~(AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED|AHD_DEV_PERIODIC_OTAG);
+	if (now_queuing) {
+		u_int usertags;
+
+		usertags = ahd_linux_user_tagdepth(ahd, devinfo);
+		if (!was_queuing) {
+			/*
+			 * Start out aggressively and allow our
+			 * dynamic queue depth algorithm to take
+			 * care of the rest.
+			 */
+			dev->maxtags = usertags;
+			dev->openings = dev->maxtags - dev->active;
+		}
+		if (dev->maxtags == 0) {
+			/*
+			 * Queueing is disabled by the user.
+			 */
+			dev->openings = 1;
+		} else if (alg == AHD_QUEUE_TAGGED) {
+			dev->flags |= AHD_DEV_Q_TAGGED;
+			if (aic79xx_periodic_otag != 0)
+				dev->flags |= AHD_DEV_PERIODIC_OTAG;
+		} else
+			dev->flags |= AHD_DEV_Q_BASIC;
+	} else {
+		/* We can only have one opening. */
+		dev->maxtags = 0;
+		dev->openings =  1 - dev->active;
+	}
+
+	switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) {
+	case AHD_DEV_Q_BASIC:
+	case AHD_DEV_Q_TAGGED:
+		scsi_change_queue_depth(sdev,
+				dev->openings + dev->active);
+		break;
+	default:
+		/*
+		 * We allow the OS to queue 2 untagged transactions to
+		 * us at any time even though we can only execute them
+		 * serially on the controller/device.  This should
+		 * remove some latency.
+		 */
+		scsi_change_queue_depth(sdev, 1);
+		break;
+	}
+}
+
+int
+ahd_platform_abort_scbs(struct ahd_softc *ahd, int target, char channel,
+			int lun, u_int tag, role_t role, uint32_t status)
+{
+	return 0;
+}
+
+static u_int
+ahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+	static int warned_user;
+	u_int tags;
+
+	tags = 0;
+	if ((ahd->user_discenable & devinfo->target_mask) != 0) {
+		if (ahd->unit >= ARRAY_SIZE(aic79xx_tag_info)) {
+
+			if (warned_user == 0) {
+				printk(KERN_WARNING
+"aic79xx: WARNING: Insufficient tag_info instances\n"
+"aic79xx: for installed controllers.  Using defaults\n"
+"aic79xx: Please update the aic79xx_tag_info array in\n"
+"aic79xx: the aic79xx_osm.c source file.\n");
+				warned_user++;
+			}
+			tags = AHD_MAX_QUEUE;
+		} else {
+			adapter_tag_info_t *tag_info;
+
+			tag_info = &aic79xx_tag_info[ahd->unit];
+			tags = tag_info->tag_commands[devinfo->target_offset];
+			if (tags > AHD_MAX_QUEUE)
+				tags = AHD_MAX_QUEUE;
+		}
+	}
+	return (tags);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahd_linux_device_queue_depth(struct scsi_device *sdev)
+{
+	struct	ahd_devinfo devinfo;
+	u_int	tags;
+	struct ahd_softc *ahd = *((struct ahd_softc **)sdev->host->hostdata);
+
+	ahd_compile_devinfo(&devinfo,
+			    ahd->our_id,
+			    sdev->sdev_target->id, sdev->lun,
+			    sdev->sdev_target->channel == 0 ? 'A' : 'B',
+			    ROLE_INITIATOR);
+	tags = ahd_linux_user_tagdepth(ahd, &devinfo);
+	if (tags != 0 && sdev->tagged_supported != 0) {
+
+		ahd_platform_set_tags(ahd, sdev, &devinfo, AHD_QUEUE_TAGGED);
+		ahd_send_async(ahd, devinfo.channel, devinfo.target,
+			       devinfo.lun, AC_TRANSFER_NEG);
+		ahd_print_devinfo(ahd, &devinfo);
+		printk("Tagged Queuing enabled.  Depth %d\n", tags);
+	} else {
+		ahd_platform_set_tags(ahd, sdev, &devinfo, AHD_QUEUE_NONE);
+		ahd_send_async(ahd, devinfo.channel, devinfo.target,
+			       devinfo.lun, AC_TRANSFER_NEG);
+	}
+}
+
+static int
+ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev,
+		      struct scsi_cmnd *cmd)
+{
+	struct	 scb *scb;
+	struct	 hardware_scb *hscb;
+	struct	 ahd_initiator_tinfo *tinfo;
+	struct	 ahd_tmode_tstate *tstate;
+	u_int	 col_idx;
+	uint16_t mask;
+	unsigned long flags;
+	int nseg;
+
+	nseg = scsi_dma_map(cmd);
+	if (nseg < 0)
+		return SCSI_MLQUEUE_HOST_BUSY;
+
+	ahd_lock(ahd, &flags);
+
+	/*
+	 * Get an scb to use.
+	 */
+	tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+				    cmd->device->id, &tstate);
+	if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) == 0
+	 || (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+		col_idx = AHD_NEVER_COL_IDX;
+	} else {
+		col_idx = AHD_BUILD_COL_IDX(cmd->device->id,
+					    cmd->device->lun);
+	}
+	if ((scb = ahd_get_scb(ahd, col_idx)) == NULL) {
+		ahd->flags |= AHD_RESOURCE_SHORTAGE;
+		ahd_unlock(ahd, &flags);
+		scsi_dma_unmap(cmd);
+		return SCSI_MLQUEUE_HOST_BUSY;
+	}
+
+	scb->io_ctx = cmd;
+	scb->platform_data->dev = dev;
+	hscb = scb->hscb;
+	cmd->host_scribble = (char *)scb;
+
+	/*
+	 * Fill out basics of the HSCB.
+	 */
+	hscb->control = 0;
+	hscb->scsiid = BUILD_SCSIID(ahd, cmd);
+	hscb->lun = cmd->device->lun;
+	scb->hscb->task_management = 0;
+	mask = SCB_GET_TARGET_MASK(ahd, scb);
+
+	if ((ahd->user_discenable & mask) != 0)
+		hscb->control |= DISCENB;
+
+	if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0)
+		scb->flags |= SCB_PACKETIZED;
+
+	if ((tstate->auto_negotiate & mask) != 0) {
+		scb->flags |= SCB_AUTO_NEGOTIATE;
+		scb->hscb->control |= MK_MESSAGE;
+	}
+
+	if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
+		if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH
+		 && (dev->flags & AHD_DEV_Q_TAGGED) != 0) {
+			hscb->control |= MSG_ORDERED_TASK;
+			dev->commands_since_idle_or_otag = 0;
+		} else {
+			hscb->control |= MSG_SIMPLE_TASK;
+		}
+	}
+
+	hscb->cdb_len = cmd->cmd_len;
+	memcpy(hscb->shared_data.idata.cdb, cmd->cmnd, hscb->cdb_len);
+
+	scb->platform_data->xfer_len = 0;
+	ahd_set_residual(scb, 0);
+	ahd_set_sense_residual(scb, 0);
+	scb->sg_count = 0;
+
+	if (nseg > 0) {
+		void *sg = scb->sg_list;
+		struct scatterlist *cur_seg;
+		int i;
+
+		scb->platform_data->xfer_len = 0;
+
+		scsi_for_each_sg(cmd, cur_seg, nseg, i) {
+			dma_addr_t addr;
+			bus_size_t len;
+
+			addr = sg_dma_address(cur_seg);
+			len = sg_dma_len(cur_seg);
+			scb->platform_data->xfer_len += len;
+			sg = ahd_sg_setup(ahd, scb, sg, addr, len,
+					  i == (nseg - 1));
+		}
+	}
+
+	LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links);
+	dev->openings--;
+	dev->active++;
+	dev->commands_issued++;
+
+	if ((dev->flags & AHD_DEV_PERIODIC_OTAG) != 0)
+		dev->commands_since_idle_or_otag++;
+	scb->flags |= SCB_ACTIVE;
+	ahd_queue_scb(ahd, scb);
+
+	ahd_unlock(ahd, &flags);
+
+	return 0;
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+irqreturn_t
+ahd_linux_isr(int irq, void *dev_id)
+{
+	struct	ahd_softc *ahd;
+	u_long	flags;
+	int	ours;
+
+	ahd = (struct ahd_softc *) dev_id;
+	ahd_lock(ahd, &flags); 
+	ours = ahd_intr(ahd);
+	ahd_unlock(ahd, &flags);
+	return IRQ_RETVAL(ours);
+}
+
+void
+ahd_send_async(struct ahd_softc *ahd, char channel,
+	       u_int target, u_int lun, ac_code code)
+{
+	switch (code) {
+	case AC_TRANSFER_NEG:
+	{
+		struct  scsi_target *starget;
+		struct	ahd_initiator_tinfo *tinfo;
+		struct	ahd_tmode_tstate *tstate;
+		unsigned int target_ppr_options;
+
+		BUG_ON(target == CAM_TARGET_WILDCARD);
+
+		tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id,
+					    target, &tstate);
+
+		/*
+		 * Don't bother reporting results while
+		 * negotiations are still pending.
+		 */
+		if (tinfo->curr.period != tinfo->goal.period
+		 || tinfo->curr.width != tinfo->goal.width
+		 || tinfo->curr.offset != tinfo->goal.offset
+		 || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+			if (bootverbose == 0)
+				break;
+
+		/*
+		 * Don't bother reporting results that
+		 * are identical to those last reported.
+		 */
+		starget = ahd->platform_data->starget[target];
+		if (starget == NULL)
+			break;
+
+		target_ppr_options =
+			(spi_dt(starget) ? MSG_EXT_PPR_DT_REQ : 0)
+			+ (spi_qas(starget) ? MSG_EXT_PPR_QAS_REQ : 0)
+			+ (spi_iu(starget) ?  MSG_EXT_PPR_IU_REQ : 0)
+			+ (spi_rd_strm(starget) ? MSG_EXT_PPR_RD_STRM : 0)
+			+ (spi_pcomp_en(starget) ? MSG_EXT_PPR_PCOMP_EN : 0)
+			+ (spi_rti(starget) ? MSG_EXT_PPR_RTI : 0)
+			+ (spi_wr_flow(starget) ? MSG_EXT_PPR_WR_FLOW : 0)
+			+ (spi_hold_mcs(starget) ? MSG_EXT_PPR_HOLD_MCS : 0);
+
+		if (tinfo->curr.period == spi_period(starget)
+		    && tinfo->curr.width == spi_width(starget)
+		    && tinfo->curr.offset == spi_offset(starget)
+		 && tinfo->curr.ppr_options == target_ppr_options)
+			if (bootverbose == 0)
+				break;
+
+		spi_period(starget) = tinfo->curr.period;
+		spi_width(starget) = tinfo->curr.width;
+		spi_offset(starget) = tinfo->curr.offset;
+		spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_DT_REQ ? 1 : 0;
+		spi_qas(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_QAS_REQ ? 1 : 0;
+		spi_iu(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ ? 1 : 0;
+		spi_rd_strm(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_RD_STRM ? 1 : 0;
+		spi_pcomp_en(starget) =  tinfo->curr.ppr_options & MSG_EXT_PPR_PCOMP_EN ? 1 : 0;
+		spi_rti(starget) =  tinfo->curr.ppr_options &  MSG_EXT_PPR_RTI ? 1 : 0;
+		spi_wr_flow(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_WR_FLOW ? 1 : 0;
+		spi_hold_mcs(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_HOLD_MCS ? 1 : 0;
+		spi_display_xfer_agreement(starget);
+		break;
+	}
+        case AC_SENT_BDR:
+	{
+		WARN_ON(lun != CAM_LUN_WILDCARD);
+		scsi_report_device_reset(ahd->platform_data->host,
+					 channel - 'A', target);
+		break;
+	}
+        case AC_BUS_RESET:
+		if (ahd->platform_data->host != NULL) {
+			scsi_report_bus_reset(ahd->platform_data->host,
+					      channel - 'A');
+		}
+                break;
+        default:
+                panic("ahd_send_async: Unexpected async event");
+        }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahd_done(struct ahd_softc *ahd, struct scb *scb)
+{
+	struct scsi_cmnd *cmd;
+	struct	  ahd_linux_device *dev;
+
+	if ((scb->flags & SCB_ACTIVE) == 0) {
+		printk("SCB %d done'd twice\n", SCB_GET_TAG(scb));
+		ahd_dump_card_state(ahd);
+		panic("Stopping for safety");
+	}
+	LIST_REMOVE(scb, pending_links);
+	cmd = scb->io_ctx;
+	dev = scb->platform_data->dev;
+	dev->active--;
+	dev->openings++;
+	if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) {
+		cmd->result &= ~(CAM_DEV_QFRZN << 16);
+		dev->qfrozen--;
+	}
+	ahd_linux_unmap_scb(ahd, scb);
+
+	/*
+	 * Guard against stale sense data.
+	 * The Linux mid-layer assumes that sense
+	 * was retrieved anytime the first byte of
+	 * the sense buffer looks "sane".
+	 */
+	cmd->sense_buffer[0] = 0;
+	if (ahd_get_transaction_status(scb) == CAM_REQ_INPROG) {
+		uint32_t amount_xferred;
+
+		amount_xferred =
+		    ahd_get_transfer_length(scb) - ahd_get_residual(scb);
+		if ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) {
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_MISC) != 0) {
+				ahd_print_path(ahd, scb);
+				printk("Set CAM_UNCOR_PARITY\n");
+			}
+#endif
+			ahd_set_transaction_status(scb, CAM_UNCOR_PARITY);
+#ifdef AHD_REPORT_UNDERFLOWS
+		/*
+		 * This code is disabled by default as some
+		 * clients of the SCSI system do not properly
+		 * initialize the underflow parameter.  This
+		 * results in spurious termination of commands
+		 * that complete as expected (e.g. underflow is
+		 * allowed as command can return variable amounts
+		 * of data.
+		 */
+		} else if (amount_xferred < scb->io_ctx->underflow) {
+			u_int i;
+
+			ahd_print_path(ahd, scb);
+			printk("CDB:");
+			for (i = 0; i < scb->io_ctx->cmd_len; i++)
+				printk(" 0x%x", scb->io_ctx->cmnd[i]);
+			printk("\n");
+			ahd_print_path(ahd, scb);
+			printk("Saw underflow (%ld of %ld bytes). "
+			       "Treated as error\n",
+				ahd_get_residual(scb),
+				ahd_get_transfer_length(scb));
+			ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+#endif
+		} else {
+			ahd_set_transaction_status(scb, CAM_REQ_CMP);
+		}
+	} else if (ahd_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
+		ahd_linux_handle_scsi_status(ahd, cmd->device, scb);
+	}
+
+	if (dev->openings == 1
+	 && ahd_get_transaction_status(scb) == CAM_REQ_CMP
+	 && ahd_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+		dev->tag_success_count++;
+	/*
+	 * Some devices deal with temporary internal resource
+	 * shortages by returning queue full.  When the queue
+	 * full occurrs, we throttle back.  Slowly try to get
+	 * back to our previous queue depth.
+	 */
+	if ((dev->openings + dev->active) < dev->maxtags
+	 && dev->tag_success_count > AHD_TAG_SUCCESS_INTERVAL) {
+		dev->tag_success_count = 0;
+		dev->openings++;
+	}
+
+	if (dev->active == 0)
+		dev->commands_since_idle_or_otag = 0;
+
+	if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+		printk("Recovery SCB completes\n");
+		if (ahd_get_transaction_status(scb) == CAM_BDR_SENT
+		 || ahd_get_transaction_status(scb) == CAM_REQ_ABORTED)
+			ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+
+		if (ahd->platform_data->eh_done)
+			complete(ahd->platform_data->eh_done);
+	}
+
+	ahd_free_scb(ahd, scb);
+	ahd_linux_queue_cmd_complete(ahd, cmd);
+}
+
+static void
+ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
+			     struct scsi_device *sdev, struct scb *scb)
+{
+	struct	ahd_devinfo devinfo;
+	struct ahd_linux_device *dev = scsi_transport_device_data(sdev);
+
+	ahd_compile_devinfo(&devinfo,
+			    ahd->our_id,
+			    sdev->sdev_target->id, sdev->lun,
+			    sdev->sdev_target->channel == 0 ? 'A' : 'B',
+			    ROLE_INITIATOR);
+	
+	/*
+	 * We don't currently trust the mid-layer to
+	 * properly deal with queue full or busy.  So,
+	 * when one occurs, we tell the mid-layer to
+	 * unconditionally requeue the command to us
+	 * so that we can retry it ourselves.  We also
+	 * implement our own throttling mechanism so
+	 * we don't clobber the device with too many
+	 * commands.
+	 */
+	switch (ahd_get_scsi_status(scb)) {
+	default:
+		break;
+	case SCSI_STATUS_CHECK_COND:
+	case SCSI_STATUS_CMD_TERMINATED:
+	{
+		struct scsi_cmnd *cmd;
+
+		/*
+		 * Copy sense information to the OS's cmd
+		 * structure if it is available.
+		 */
+		cmd = scb->io_ctx;
+		if ((scb->flags & (SCB_SENSE|SCB_PKT_SENSE)) != 0) {
+			struct scsi_status_iu_header *siu;
+			u_int sense_size;
+			u_int sense_offset;
+
+			if (scb->flags & SCB_SENSE) {
+				sense_size = min(sizeof(struct scsi_sense_data)
+					       - ahd_get_sense_residual(scb),
+						 (u_long)SCSI_SENSE_BUFFERSIZE);
+				sense_offset = 0;
+			} else {
+				/*
+				 * Copy only the sense data into the provided
+				 * buffer.
+				 */
+				siu = (struct scsi_status_iu_header *)
+				    scb->sense_data;
+				sense_size = min_t(size_t,
+						scsi_4btoul(siu->sense_length),
+						SCSI_SENSE_BUFFERSIZE);
+				sense_offset = SIU_SENSE_OFFSET(siu);
+			}
+
+			memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+			memcpy(cmd->sense_buffer,
+			       ahd_get_sense_buf(ahd, scb)
+			       + sense_offset, sense_size);
+			cmd->result |= (DRIVER_SENSE << 24);
+
+#ifdef AHD_DEBUG
+			if (ahd_debug & AHD_SHOW_SENSE) {
+				int i;
+
+				printk("Copied %d bytes of sense data at %d:",
+				       sense_size, sense_offset);
+				for (i = 0; i < sense_size; i++) {
+					if ((i & 0xF) == 0)
+						printk("\n");
+					printk("0x%x ", cmd->sense_buffer[i]);
+				}
+				printk("\n");
+			}
+#endif
+		}
+		break;
+	}
+	case SCSI_STATUS_QUEUE_FULL:
+		/*
+		 * By the time the core driver has returned this
+		 * command, all other commands that were queued
+		 * to us but not the device have been returned.
+		 * This ensures that dev->active is equal to
+		 * the number of commands actually queued to
+		 * the device.
+		 */
+		dev->tag_success_count = 0;
+		if (dev->active != 0) {
+			/*
+			 * Drop our opening count to the number
+			 * of commands currently outstanding.
+			 */
+			dev->openings = 0;
+#ifdef AHD_DEBUG
+			if ((ahd_debug & AHD_SHOW_QFULL) != 0) {
+				ahd_print_path(ahd, scb);
+				printk("Dropping tag count to %d\n",
+				       dev->active);
+			}
+#endif
+			if (dev->active == dev->tags_on_last_queuefull) {
+
+				dev->last_queuefull_same_count++;
+				/*
+				 * If we repeatedly see a queue full
+				 * at the same queue depth, this
+				 * device has a fixed number of tag
+				 * slots.  Lock in this tag depth
+				 * so we stop seeing queue fulls from
+				 * this device.
+				 */
+				if (dev->last_queuefull_same_count
+				 == AHD_LOCK_TAGS_COUNT) {
+					dev->maxtags = dev->active;
+					ahd_print_path(ahd, scb);
+					printk("Locking max tag count at %d\n",
+					       dev->active);
+				}
+			} else {
+				dev->tags_on_last_queuefull = dev->active;
+				dev->last_queuefull_same_count = 0;
+			}
+			ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+			ahd_set_scsi_status(scb, SCSI_STATUS_OK);
+			ahd_platform_set_tags(ahd, sdev, &devinfo,
+				     (dev->flags & AHD_DEV_Q_BASIC)
+				   ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED);
+			break;
+		}
+		/*
+		 * Drop down to a single opening, and treat this
+		 * as if the target returned BUSY SCSI status.
+		 */
+		dev->openings = 1;
+		ahd_platform_set_tags(ahd, sdev, &devinfo,
+			     (dev->flags & AHD_DEV_Q_BASIC)
+			   ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED);
+		ahd_set_scsi_status(scb, SCSI_STATUS_BUSY);
+	}
+}
+
+static void
+ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, struct scsi_cmnd *cmd)
+{
+	int status;
+	int new_status = DID_OK;
+	int do_fallback = 0;
+	int scsi_status;
+
+	/*
+	 * Map CAM error codes into Linux Error codes.  We
+	 * avoid the conversion so that the DV code has the
+	 * full error information available when making
+	 * state change decisions.
+	 */
+
+	status = ahd_cmd_get_transaction_status(cmd);
+	switch (status) {
+	case CAM_REQ_INPROG:
+	case CAM_REQ_CMP:
+		new_status = DID_OK;
+		break;
+	case CAM_AUTOSENSE_FAIL:
+		new_status = DID_ERROR;
+		/* Fallthrough */
+	case CAM_SCSI_STATUS_ERROR:
+		scsi_status = ahd_cmd_get_scsi_status(cmd);
+
+		switch(scsi_status) {
+		case SCSI_STATUS_CMD_TERMINATED:
+		case SCSI_STATUS_CHECK_COND:
+			if ((cmd->result >> 24) != DRIVER_SENSE) {
+				do_fallback = 1;
+			} else {
+				struct scsi_sense_data *sense;
+				
+				sense = (struct scsi_sense_data *)
+					cmd->sense_buffer;
+				if (sense->extra_len >= 5 &&
+				    (sense->add_sense_code == 0x47
+				     || sense->add_sense_code == 0x48))
+					do_fallback = 1;
+			}
+			break;
+		default:
+			break;
+		}
+		break;
+	case CAM_REQ_ABORTED:
+		new_status = DID_ABORT;
+		break;
+	case CAM_BUSY:
+		new_status = DID_BUS_BUSY;
+		break;
+	case CAM_REQ_INVALID:
+	case CAM_PATH_INVALID:
+		new_status = DID_BAD_TARGET;
+		break;
+	case CAM_SEL_TIMEOUT:
+		new_status = DID_NO_CONNECT;
+		break;
+	case CAM_SCSI_BUS_RESET:
+	case CAM_BDR_SENT:
+		new_status = DID_RESET;
+		break;
+	case CAM_UNCOR_PARITY:
+		new_status = DID_PARITY;
+		do_fallback = 1;
+		break;
+	case CAM_CMD_TIMEOUT:
+		new_status = DID_TIME_OUT;
+		do_fallback = 1;
+		break;
+	case CAM_REQ_CMP_ERR:
+	case CAM_UNEXP_BUSFREE:
+	case CAM_DATA_RUN_ERR:
+		new_status = DID_ERROR;
+		do_fallback = 1;
+		break;
+	case CAM_UA_ABORT:
+	case CAM_NO_HBA:
+	case CAM_SEQUENCE_FAIL:
+	case CAM_CCB_LEN_ERR:
+	case CAM_PROVIDE_FAIL:
+	case CAM_REQ_TERMIO:
+	case CAM_UNREC_HBA_ERROR:
+	case CAM_REQ_TOO_BIG:
+		new_status = DID_ERROR;
+		break;
+	case CAM_REQUEUE_REQ:
+		new_status = DID_REQUEUE;
+		break;
+	default:
+		/* We should never get here */
+		new_status = DID_ERROR;
+		break;
+	}
+
+	if (do_fallback) {
+		printk("%s: device overrun (status %x) on %d:%d:%d\n",
+		       ahd_name(ahd), status, cmd->device->channel,
+		       cmd->device->id, (u8)cmd->device->lun);
+	}
+
+	ahd_cmd_set_transaction_status(cmd, new_status);
+
+	cmd->scsi_done(cmd);
+}
+
+static void
+ahd_freeze_simq(struct ahd_softc *ahd)
+{
+	scsi_block_requests(ahd->platform_data->host);
+}
+
+static void
+ahd_release_simq(struct ahd_softc *ahd)
+{
+	scsi_unblock_requests(ahd->platform_data->host);
+}
+
+static int
+ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
+{
+	struct ahd_softc *ahd;
+	struct ahd_linux_device *dev;
+	struct scb *pending_scb;
+	u_int  saved_scbptr;
+	u_int  active_scbptr;
+	u_int  last_phase;
+	u_int  saved_scsiid;
+	u_int  cdb_byte;
+	int    retval;
+	int    was_paused;
+	int    paused;
+	int    wait;
+	int    disconnected;
+	ahd_mode_state saved_modes;
+	unsigned long flags;
+
+	pending_scb = NULL;
+	paused = FALSE;
+	wait = FALSE;
+	ahd = *(struct ahd_softc **)cmd->device->host->hostdata;
+
+	scmd_printk(KERN_INFO, cmd,
+		    "Attempting to queue an ABORT message:");
+
+	printk("CDB:");
+	for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++)
+		printk(" 0x%x", cmd->cmnd[cdb_byte]);
+	printk("\n");
+
+	ahd_lock(ahd, &flags);
+
+	/*
+	 * First determine if we currently own this command.
+	 * Start by searching the device queue.  If not found
+	 * there, check the pending_scb list.  If not found
+	 * at all, and the system wanted us to just abort the
+	 * command, return success.
+	 */
+	dev = scsi_transport_device_data(cmd->device);
+
+	if (dev == NULL) {
+		/*
+		 * No target device for this command exists,
+		 * so we must not still own the command.
+		 */
+		scmd_printk(KERN_INFO, cmd, "Is not an active device\n");
+		retval = SUCCESS;
+		goto no_cmd;
+	}
+
+	/*
+	 * See if we can find a matching cmd in the pending list.
+	 */
+	LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+		if (pending_scb->io_ctx == cmd)
+			break;
+	}
+
+	if (pending_scb == NULL) {
+		scmd_printk(KERN_INFO, cmd, "Command not found\n");
+		goto no_cmd;
+	}
+
+	if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+		/*
+		 * We can't queue two recovery actions using the same SCB
+		 */
+		retval = FAILED;
+		goto  done;
+	}
+
+	/*
+	 * Ensure that the card doesn't do anything
+	 * behind our back.  Also make sure that we
+	 * didn't "just" miss an interrupt that would
+	 * affect this cmd.
+	 */
+	was_paused = ahd_is_paused(ahd);
+	ahd_pause_and_flushwork(ahd);
+	paused = TRUE;
+
+	if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+		scmd_printk(KERN_INFO, cmd, "Command already completed\n");
+		goto no_cmd;
+	}
+
+	printk("%s: At time of recovery, card was %spaused\n",
+	       ahd_name(ahd), was_paused ? "" : "not ");
+	ahd_dump_card_state(ahd);
+
+	disconnected = TRUE;
+	if (ahd_search_qinfifo(ahd, cmd->device->id, 
+			       cmd->device->channel + 'A',
+			       cmd->device->lun,
+			       pending_scb->hscb->tag,
+			       ROLE_INITIATOR, CAM_REQ_ABORTED,
+			       SEARCH_COMPLETE) > 0) {
+		printk("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+		       ahd_name(ahd), cmd->device->channel, 
+		       cmd->device->id, (u8)cmd->device->lun);
+		retval = SUCCESS;
+		goto done;
+	}
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	last_phase = ahd_inb(ahd, LASTPHASE);
+	saved_scbptr = ahd_get_scbptr(ahd);
+	active_scbptr = saved_scbptr;
+	if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) {
+		struct scb *bus_scb;
+
+		bus_scb = ahd_lookup_scb(ahd, active_scbptr);
+		if (bus_scb == pending_scb)
+			disconnected = FALSE;
+	}
+
+	/*
+	 * At this point, pending_scb is the scb associated with the
+	 * passed in command.  That command is currently active on the
+	 * bus or is in the disconnected state.
+	 */
+	saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+	if (last_phase != P_BUSFREE
+	    && SCB_GET_TAG(pending_scb) == active_scbptr) {
+
+		/*
+		 * We're active on the bus, so assert ATN
+		 * and hope that the target responds.
+		 */
+		pending_scb = ahd_lookup_scb(ahd, active_scbptr);
+		pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT;
+		ahd_outb(ahd, MSG_OUT, HOST_MSG);
+		ahd_outb(ahd, SCSISIGO, last_phase|ATNO);
+		scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n");
+		wait = TRUE;
+	} else if (disconnected) {
+
+		/*
+		 * Actually re-queue this SCB in an attempt
+		 * to select the device before it reconnects.
+		 */
+		pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT;
+		ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb));
+		pending_scb->hscb->cdb_len = 0;
+		pending_scb->hscb->task_attribute = 0;
+		pending_scb->hscb->task_management = SIU_TASKMGMT_ABORT_TASK;
+
+		if ((pending_scb->flags & SCB_PACKETIZED) != 0) {
+			/*
+			 * Mark the SCB has having an outstanding
+			 * task management function.  Should the command
+			 * complete normally before the task management
+			 * function can be sent, the host will be notified
+			 * to abort our requeued SCB.
+			 */
+			ahd_outb(ahd, SCB_TASK_MANAGEMENT,
+				 pending_scb->hscb->task_management);
+		} else {
+			/*
+			 * If non-packetized, set the MK_MESSAGE control
+			 * bit indicating that we desire to send a message.
+			 * We also set the disconnected flag since there is
+			 * no guarantee that our SCB control byte matches
+			 * the version on the card.  We don't want the
+			 * sequencer to abort the command thinking an
+			 * unsolicited reselection occurred.
+			 */
+			pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+
+			/*
+			 * The sequencer will never re-reference the
+			 * in-core SCB.  To make sure we are notified
+			 * during reselection, set the MK_MESSAGE flag in
+			 * the card's copy of the SCB.
+			 */
+			ahd_outb(ahd, SCB_CONTROL,
+				 ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE);
+		}
+
+		/*
+		 * Clear out any entries in the QINFIFO first
+		 * so we are the next SCB for this target
+		 * to run.
+		 */
+		ahd_search_qinfifo(ahd, cmd->device->id,
+				   cmd->device->channel + 'A', cmd->device->lun,
+				   SCB_LIST_NULL, ROLE_INITIATOR,
+				   CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+		ahd_qinfifo_requeue_tail(ahd, pending_scb);
+		ahd_set_scbptr(ahd, saved_scbptr);
+		ahd_print_path(ahd, pending_scb);
+		printk("Device is disconnected, re-queuing SCB\n");
+		wait = TRUE;
+	} else {
+		scmd_printk(KERN_INFO, cmd, "Unable to deliver message\n");
+		retval = FAILED;
+		goto done;
+	}
+
+no_cmd:
+	/*
+	 * Our assumption is that if we don't have the command, no
+	 * recovery action was required, so we return success.  Again,
+	 * the semantics of the mid-layer recovery engine are not
+	 * well defined, so this may change in time.
+	 */
+	retval = SUCCESS;
+done:
+	if (paused)
+		ahd_unpause(ahd);
+	if (wait) {
+		DECLARE_COMPLETION_ONSTACK(done);
+
+		ahd->platform_data->eh_done = &done;
+		ahd_unlock(ahd, &flags);
+
+		printk("%s: Recovery code sleeping\n", ahd_name(ahd));
+		if (!wait_for_completion_timeout(&done, 5 * HZ)) {
+			ahd_lock(ahd, &flags);
+			ahd->platform_data->eh_done = NULL;
+			ahd_unlock(ahd, &flags);
+			printk("%s: Timer Expired (active %d)\n",
+			       ahd_name(ahd), dev->active);
+			retval = FAILED;
+		}
+		printk("Recovery code awake\n");
+	} else
+		ahd_unlock(ahd, &flags);
+
+	if (retval != SUCCESS)
+		printk("%s: Command abort returning 0x%x\n",
+		       ahd_name(ahd), retval);
+
+	return retval;
+}
+
+static void ahd_linux_set_width(struct scsi_target *starget, int width)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_devinfo devinfo;
+	unsigned long flags;
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_lock(ahd, &flags);
+	ahd_set_width(ahd, &devinfo, width, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_period(struct scsi_target *starget, int period)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options;
+	unsigned int dt;
+	unsigned long flags;
+	unsigned long offset = tinfo->goal.offset;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: set period to %d\n", ahd_name(ahd), period);
+#endif
+	if (offset == 0)
+		offset = MAX_OFFSET;
+
+	if (period < 8)
+		period = 8;
+	if (period < 10) {
+		if (spi_max_width(starget)) {
+			ppr_options |= MSG_EXT_PPR_DT_REQ;
+			if (period == 8)
+				ppr_options |= MSG_EXT_PPR_IU_REQ;
+		} else
+			period = 10;
+	}
+
+	dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+
+	/* all PPR requests apart from QAS require wide transfers */
+	if (ppr_options & ~MSG_EXT_PPR_QAS_REQ) {
+		if (spi_width(starget) == 0)
+			ppr_options &= MSG_EXT_PPR_QAS_REQ;
+	}
+
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_offset(struct scsi_target *starget, int offset)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = 0;
+	unsigned int period = 0;
+	unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+	unsigned long flags;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: set offset to %d\n", ahd_name(ahd), offset);
+#endif
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	if (offset != 0) {
+		period = tinfo->goal.period;
+		ppr_options = tinfo->goal.ppr_options;
+		ahd_find_syncrate(ahd, &period, &ppr_options, 
+				  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+	}
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, offset, ppr_options,
+			 AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_dt(struct scsi_target *starget, int dt)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_DT_REQ;
+	unsigned int period = tinfo->goal.period;
+	unsigned int width = tinfo->goal.width;
+	unsigned long flags;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: %s DT\n", ahd_name(ahd),
+		       dt ? "enabling" : "disabling");
+#endif
+	if (dt && spi_max_width(starget)) {
+		ppr_options |= MSG_EXT_PPR_DT_REQ;
+		if (!width)
+			ahd_linux_set_width(starget, 1);
+	} else {
+		if (period <= 9)
+			period = 10; /* If resetting DT, period must be >= 25ns */
+		/* IU is invalid without DT set */
+		ppr_options &= ~MSG_EXT_PPR_IU_REQ;
+	}
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_qas(struct scsi_target *starget, int qas)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_QAS_REQ;
+	unsigned int period = tinfo->goal.period;
+	unsigned int dt;
+	unsigned long flags;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: %s QAS\n", ahd_name(ahd),
+		       qas ? "enabling" : "disabling");
+#endif
+
+	if (qas) {
+		ppr_options |= MSG_EXT_PPR_QAS_REQ; 
+	}
+
+	dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_iu(struct scsi_target *starget, int iu)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_IU_REQ;
+	unsigned int period = tinfo->goal.period;
+	unsigned int dt;
+	unsigned long flags;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: %s IU\n", ahd_name(ahd),
+		       iu ? "enabling" : "disabling");
+#endif
+
+	if (iu && spi_max_width(starget)) {
+		ppr_options |= MSG_EXT_PPR_IU_REQ;
+		ppr_options |= MSG_EXT_PPR_DT_REQ; /* IU requires DT */
+	}
+
+	dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_rd_strm(struct scsi_target *starget, int rdstrm)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_RD_STRM;
+	unsigned int period = tinfo->goal.period;
+	unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+	unsigned long flags;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: %s Read Streaming\n", ahd_name(ahd),
+		       rdstrm  ? "enabling" : "disabling");
+#endif
+
+	if (rdstrm && spi_max_width(starget))
+		ppr_options |= MSG_EXT_PPR_RD_STRM;
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_wr_flow(struct scsi_target *starget, int wrflow)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_WR_FLOW;
+	unsigned int period = tinfo->goal.period;
+	unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+	unsigned long flags;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: %s Write Flow Control\n", ahd_name(ahd),
+		       wrflow ? "enabling" : "disabling");
+#endif
+
+	if (wrflow && spi_max_width(starget))
+		ppr_options |= MSG_EXT_PPR_WR_FLOW;
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_rti(struct scsi_target *starget, int rti)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_RTI;
+	unsigned int period = tinfo->goal.period;
+	unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+	unsigned long flags;
+
+	if ((ahd->features & AHD_RTI) == 0) {
+#ifdef AHD_DEBUG
+		if ((ahd_debug & AHD_SHOW_DV) != 0)
+			printk("%s: RTI not available\n", ahd_name(ahd));
+#endif
+		return;
+	}
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: %s RTI\n", ahd_name(ahd),
+		       rti ? "enabling" : "disabling");
+#endif
+
+	if (rti && spi_max_width(starget))
+		ppr_options |= MSG_EXT_PPR_RTI;
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_pcomp_en(struct scsi_target *starget, int pcomp)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_PCOMP_EN;
+	unsigned int period = tinfo->goal.period;
+	unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+	unsigned long flags;
+
+#ifdef AHD_DEBUG
+	if ((ahd_debug & AHD_SHOW_DV) != 0)
+		printk("%s: %s Precompensation\n", ahd_name(ahd),
+		       pcomp ? "Enable" : "Disable");
+#endif
+
+	if (pcomp && spi_max_width(starget)) {
+		uint8_t precomp;
+
+		if (ahd->unit < ARRAY_SIZE(aic79xx_iocell_info)) {
+			const struct ahd_linux_iocell_opts *iocell_opts;
+
+			iocell_opts = &aic79xx_iocell_info[ahd->unit];
+			precomp = iocell_opts->precomp;
+		} else {
+			precomp = AIC79XX_DEFAULT_PRECOMP;
+		}
+		ppr_options |= MSG_EXT_PPR_PCOMP_EN;
+		AHD_SET_PRECOMP(ahd, precomp);
+	} else {
+		AHD_SET_PRECOMP(ahd, 0);
+	}
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_set_hold_mcs(struct scsi_target *starget, int hold)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata);
+	struct ahd_tmode_tstate *tstate;
+	struct ahd_initiator_tinfo *tinfo 
+		= ahd_fetch_transinfo(ahd,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahd_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_HOLD_MCS;
+	unsigned int period = tinfo->goal.period;
+	unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ;
+	unsigned long flags;
+
+	if (hold && spi_max_width(starget))
+		ppr_options |= MSG_EXT_PPR_HOLD_MCS;
+
+	ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahd_find_syncrate(ahd, &period, &ppr_options,
+			  dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2);
+
+	ahd_lock(ahd, &flags);
+	ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset,
+			 ppr_options, AHD_TRANS_GOAL, FALSE);
+	ahd_unlock(ahd, &flags);
+}
+
+static void ahd_linux_get_signalling(struct Scsi_Host *shost)
+{
+	struct ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata;
+	unsigned long flags;
+	u8 mode;
+
+	ahd_lock(ahd, &flags);
+	ahd_pause(ahd);
+	mode = ahd_inb(ahd, SBLKCTL);
+	ahd_unpause(ahd);
+	ahd_unlock(ahd, &flags);
+
+	if (mode & ENAB40)
+		spi_signalling(shost) = SPI_SIGNAL_LVD;
+	else if (mode & ENAB20)
+		spi_signalling(shost) = SPI_SIGNAL_SE;
+	else
+		spi_signalling(shost) = SPI_SIGNAL_UNKNOWN;
+}
+
+static struct spi_function_template ahd_linux_transport_functions = {
+	.set_offset	= ahd_linux_set_offset,
+	.show_offset	= 1,
+	.set_period	= ahd_linux_set_period,
+	.show_period	= 1,
+	.set_width	= ahd_linux_set_width,
+	.show_width	= 1,
+	.set_dt		= ahd_linux_set_dt,
+	.show_dt	= 1,
+	.set_iu		= ahd_linux_set_iu,
+	.show_iu	= 1,
+	.set_qas	= ahd_linux_set_qas,
+	.show_qas	= 1,
+	.set_rd_strm	= ahd_linux_set_rd_strm,
+	.show_rd_strm	= 1,
+	.set_wr_flow	= ahd_linux_set_wr_flow,
+	.show_wr_flow	= 1,
+	.set_rti	= ahd_linux_set_rti,
+	.show_rti	= 1,
+	.set_pcomp_en	= ahd_linux_set_pcomp_en,
+	.show_pcomp_en	= 1,
+	.set_hold_mcs	= ahd_linux_set_hold_mcs,
+	.show_hold_mcs	= 1,
+	.get_signalling = ahd_linux_get_signalling,
+};
+
+static int __init
+ahd_linux_init(void)
+{
+	int	error = 0;
+
+	/*
+	 * If we've been passed any parameters, process them now.
+	 */
+	if (aic79xx)
+		aic79xx_setup(aic79xx);
+
+	ahd_linux_transport_template =
+		spi_attach_transport(&ahd_linux_transport_functions);
+	if (!ahd_linux_transport_template)
+		return -ENODEV;
+
+	scsi_transport_reserve_device(ahd_linux_transport_template,
+				      sizeof(struct ahd_linux_device));
+
+	error = ahd_linux_pci_init();
+	if (error)
+		spi_release_transport(ahd_linux_transport_template);
+	return error;
+}
+
+static void __exit
+ahd_linux_exit(void)
+{
+	ahd_linux_pci_exit();
+	spi_release_transport(ahd_linux_transport_template);
+}
+
+module_init(ahd_linux_init);
+module_exit(ahd_linux_exit);
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h
new file mode 100644
index 0000000..c58fa33
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.h
@@ -0,0 +1,695 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.h#166 $
+ *
+ */
+#ifndef _AIC79XX_LINUX_H_
+#define _AIC79XX_LINUX_H_
+
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+/* Core SCSI definitions */
+#define AIC_LIB_PREFIX ahd
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+#include "scsi_iu.h"
+#include "aiclib.h"
+
+/*********************************** Debugging ********************************/
+#ifdef CONFIG_AIC79XX_DEBUG_ENABLE
+#ifdef CONFIG_AIC79XX_DEBUG_MASK
+#define AHD_DEBUG 1
+#define AHD_DEBUG_OPTS CONFIG_AIC79XX_DEBUG_MASK
+#else
+/*
+ * Compile in debugging code, but do not enable any printfs.
+ */
+#define AHD_DEBUG 1
+#define AHD_DEBUG_OPTS 0
+#endif
+/* No debugging code. */
+#endif
+
+/********************************** Misc Macros *******************************/
+#define	powerof2(x)	((((x)-1)&(x))==0)
+
+/************************* Forward Declarations *******************************/
+struct ahd_softc;
+typedef struct pci_dev *ahd_dev_softc_t;
+typedef struct scsi_cmnd      *ahd_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahd_htobe16(x)	cpu_to_be16(x)
+#define ahd_htobe32(x)	cpu_to_be32(x)
+#define ahd_htobe64(x)	cpu_to_be64(x)
+#define ahd_htole16(x)	cpu_to_le16(x)
+#define ahd_htole32(x)	cpu_to_le32(x)
+#define ahd_htole64(x)	cpu_to_le64(x)
+
+#define ahd_be16toh(x)	be16_to_cpu(x)
+#define ahd_be32toh(x)	be32_to_cpu(x)
+#define ahd_be64toh(x)	be64_to_cpu(x)
+#define ahd_le16toh(x)	le16_to_cpu(x)
+#define ahd_le32toh(x)	le32_to_cpu(x)
+#define ahd_le64toh(x)	le64_to_cpu(x)
+
+/************************* Configuration Data *********************************/
+extern uint32_t aic79xx_allow_memio;
+extern struct scsi_host_template aic79xx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+typedef uint32_t bus_size_t;
+
+typedef enum {
+	BUS_SPACE_MEMIO,
+	BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+	u_long		  ioport;
+	volatile uint8_t __iomem *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+	dma_addr_t	ds_addr;
+	bus_size_t	ds_len;
+} bus_dma_segment_t;
+
+struct ahd_linux_dma_tag
+{
+	bus_size_t	alignment;
+	bus_size_t	boundary;
+	bus_size_t	maxsize;
+};
+typedef struct ahd_linux_dma_tag* bus_dma_tag_t;
+
+typedef dma_addr_t bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, dma_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK		0x0
+#define BUS_DMA_NOWAIT		0x1
+#define BUS_DMA_ALLOCNOW	0x2
+#define BUS_DMA_LOAD_SEGS	0x4	/*
+					 * Argument is an S/G list not
+					 * a single buffer.
+					 */
+
+#define BUS_SPACE_MAXADDR	0xFFFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT	0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT	0xFFFFFFFF
+
+int	ahd_dma_tag_create(struct ahd_softc *, bus_dma_tag_t /*parent*/,
+			   bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+			   dma_addr_t /*lowaddr*/, dma_addr_t /*highaddr*/,
+			   bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+			   bus_size_t /*maxsize*/, int /*nsegments*/,
+			   bus_size_t /*maxsegsz*/, int /*flags*/,
+			   bus_dma_tag_t */*dma_tagp*/);
+
+void	ahd_dma_tag_destroy(struct ahd_softc *, bus_dma_tag_t /*tag*/);
+
+int	ahd_dmamem_alloc(struct ahd_softc *, bus_dma_tag_t /*dmat*/,
+			 void** /*vaddr*/, int /*flags*/,
+			 bus_dmamap_t* /*mapp*/);
+
+void	ahd_dmamem_free(struct ahd_softc *, bus_dma_tag_t /*dmat*/,
+			void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void	ahd_dmamap_destroy(struct ahd_softc *, bus_dma_tag_t /*tag*/,
+			   bus_dmamap_t /*map*/);
+
+int	ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t /*dmat*/,
+			bus_dmamap_t /*map*/, void * /*buf*/,
+			bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+			void */*callback_arg*/, int /*flags*/);
+
+int	ahd_dmamap_unload(struct ahd_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Operations performed by ahd_dmamap_sync().
+ */
+#define BUS_DMASYNC_PREREAD	0x01	/* pre-read synchronization */
+#define BUS_DMASYNC_POSTREAD	0x02	/* post-read synchronization */
+#define BUS_DMASYNC_PREWRITE	0x04	/* pre-write synchronization */
+#define BUS_DMASYNC_POSTWRITE	0x08	/* post-write synchronization */
+
+/*
+ * XXX
+ * ahd_dmamap_sync is only used on buffers allocated with
+ * the pci_alloc_consistent() API.  Although I'm not sure how
+ * this works on architectures with a write buffer, Linux does
+ * not have an API to sync "coherent" memory.  Perhaps we need
+ * to do an mb()?
+ */
+#define ahd_dmamap_sync(ahd, dma_tag, dmamap, offset, len, op)
+
+/************************** Timer DataStructures ******************************/
+typedef struct timer_list ahd_timer_t;
+
+/********************************** Includes **********************************/
+#ifdef CONFIG_AIC79XX_REG_PRETTY_PRINT
+#define AIC_DEBUG_REGISTERS 1
+#else
+#define AIC_DEBUG_REGISTERS 0
+#endif
+#include "aic79xx.h"
+
+/***************************** Timer Facilities *******************************/
+#define ahd_timer_init init_timer
+#define ahd_timer_stop del_timer_sync
+
+/***************************** SMP support ************************************/
+#include <linux/spinlock.h>
+
+#define AIC79XX_DRIVER_VERSION "3.0"
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle.  The structure allocated for a device only becomes persistent
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+
+typedef enum {
+	AHD_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+	AHD_DEV_Q_BASIC		 = 0x10, /* Allow basic device queuing */
+	AHD_DEV_Q_TAGGED	 = 0x20, /* Allow full SCSI2 command queueing */
+	AHD_DEV_PERIODIC_OTAG	 = 0x40, /* Send OTAG to prevent starvation */
+} ahd_linux_dev_flags;
+
+struct ahd_linux_device {
+	TAILQ_ENTRY(ahd_linux_device) links;
+
+	/*
+	 * The number of transactions currently
+	 * queued to the device.
+	 */
+	int			active;
+
+	/*
+	 * The currently allowed number of 
+	 * transactions that can be queued to
+	 * the device.  Must be signed for
+	 * conversion from tagged to untagged
+	 * mode where the device may have more
+	 * than one outstanding active transaction.
+	 */
+	int			openings;
+
+	/*
+	 * A positive count indicates that this
+	 * device's queue is halted.
+	 */
+	u_int			qfrozen;
+	
+	/*
+	 * Cumulative command counter.
+	 */
+	u_long			commands_issued;
+
+	/*
+	 * The number of tagged transactions when
+	 * running at our current opening level
+	 * that have been successfully received by
+	 * this device since the last QUEUE FULL.
+	 */
+	u_int			tag_success_count;
+#define AHD_TAG_SUCCESS_INTERVAL 50
+
+	ahd_linux_dev_flags	flags;
+
+	/*
+	 * Per device timer.
+	 */
+	struct timer_list	timer;
+
+	/*
+	 * The high limit for the tags variable.
+	 */
+	u_int			maxtags;
+
+	/*
+	 * The computed number of tags outstanding
+	 * at the time of the last QUEUE FULL event.
+	 */
+	u_int			tags_on_last_queuefull;
+
+	/*
+	 * How many times we have seen a queue full
+	 * with the same number of tags.  This is used
+	 * to stop our adaptive queue depth algorithm
+	 * on devices with a fixed number of tags.
+	 */
+	u_int			last_queuefull_same_count;
+#define AHD_LOCK_TAGS_COUNT 50
+
+	/*
+	 * How many transactions have been queued
+	 * without the device going idle.  We use
+	 * this statistic to determine when to issue
+	 * an ordered tag to prevent transaction
+	 * starvation.  This statistic is only updated
+	 * if the AHD_DEV_PERIODIC_OTAG flag is set
+	 * on this device.
+	 */
+	u_int			commands_since_idle_or_otag;
+#define AHD_OTAG_THRESH	500
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require.  So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#define	AHD_NSEG 128
+
+/*
+ * Per-SCB OSM storage.
+ */
+struct scb_platform_data {
+	struct ahd_linux_device	*dev;
+	dma_addr_t		 buf_busaddr;
+	uint32_t		 xfer_len;
+	uint32_t		 sense_resid;	/* Auto-Sense residual */
+};
+
+/*
+ * Define a structure used for each host adapter.  All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+struct ahd_platform_data {
+	/*
+	 * Fields accessed from interrupt context.
+	 */
+	struct scsi_target *starget[AHD_NUM_TARGETS]; 
+
+	spinlock_t		 spin_lock;
+	struct completion	*eh_done;
+	struct Scsi_Host        *host;		/* pointer to scsi host */
+#define AHD_LINUX_NOIRQ	((uint32_t)~0)
+	uint32_t		 irq;		/* IRQ for this adapter */
+	uint32_t		 bios_address;
+	resource_size_t		 mem_busaddr;	/* Mem Base Addr */
+};
+
+void ahd_delay(long);
+
+/***************************** Low Level I/O **********************************/
+uint8_t ahd_inb(struct ahd_softc * ahd, long port);
+void ahd_outb(struct ahd_softc * ahd, long port, uint8_t val);
+void ahd_outw_atomic(struct ahd_softc * ahd,
+				     long port, uint16_t val);
+void ahd_outsb(struct ahd_softc * ahd, long port,
+			       uint8_t *, int count);
+void ahd_insb(struct ahd_softc * ahd, long port,
+			       uint8_t *, int count);
+
+/**************************** Initialization **********************************/
+int		ahd_linux_register_host(struct ahd_softc *,
+					struct scsi_host_template *);
+
+/******************************** Locking *************************************/
+static inline void
+ahd_lockinit(struct ahd_softc *ahd)
+{
+	spin_lock_init(&ahd->platform_data->spin_lock);
+}
+
+static inline void
+ahd_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+	spin_lock_irqsave(&ahd->platform_data->spin_lock, *flags);
+}
+
+static inline void
+ahd_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+	spin_unlock_irqrestore(&ahd->platform_data->spin_lock, *flags);
+}
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR		0x00
+#define PCIR_VENDOR		0x00
+#define PCIR_DEVICE		0x02
+#define PCIR_COMMAND		0x04
+#define PCIM_CMD_PORTEN		0x0001
+#define PCIM_CMD_MEMEN		0x0002
+#define PCIM_CMD_BUSMASTEREN	0x0004
+#define PCIM_CMD_MWRICEN	0x0010
+#define PCIM_CMD_PERRESPEN	0x0040
+#define	PCIM_CMD_SERRESPEN	0x0100
+#define PCIR_STATUS		0x06
+#define PCIR_REVID		0x08
+#define PCIR_PROGIF		0x09
+#define PCIR_SUBCLASS		0x0a
+#define PCIR_CLASS		0x0b
+#define PCIR_CACHELNSZ		0x0c
+#define PCIR_LATTIMER		0x0d
+#define PCIR_HEADERTYPE		0x0e
+#define PCIM_MFDEV		0x80
+#define PCIR_BIST		0x0f
+#define PCIR_CAP_PTR		0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS	0x10
+#define PCIR_SUBVEND_0	0x2c
+#define PCIR_SUBDEV_0	0x2e
+
+/****************************** PCI-X definitions *****************************/
+#define PCIXR_COMMAND	0x96
+#define PCIXR_DEVADDR	0x98
+#define PCIXM_DEVADDR_FNUM	0x0003	/* Function Number */
+#define PCIXM_DEVADDR_DNUM	0x00F8	/* Device Number */
+#define PCIXM_DEVADDR_BNUM	0xFF00	/* Bus Number */
+#define PCIXR_STATUS	0x9A
+#define PCIXM_STATUS_64BIT	0x0001	/* Active 64bit connection to device. */
+#define PCIXM_STATUS_133CAP	0x0002	/* Device is 133MHz capable */
+#define PCIXM_STATUS_SCDISC	0x0004	/* Split Completion Discarded */
+#define PCIXM_STATUS_UNEXPSC	0x0008	/* Unexpected Split Completion */
+#define PCIXM_STATUS_CMPLEXDEV	0x0010	/* Device Complexity (set == bridge) */
+#define PCIXM_STATUS_MAXMRDBC	0x0060	/* Maximum Burst Read Count */
+#define PCIXM_STATUS_MAXSPLITS	0x0380	/* Maximum Split Transactions */
+#define PCIXM_STATUS_MAXCRDS	0x1C00	/* Maximum Cumulative Read Size */
+#define PCIXM_STATUS_RCVDSCEM	0x2000	/* Received a Split Comp w/Error msg */
+
+typedef enum
+{
+	AHD_POWER_STATE_D0,
+	AHD_POWER_STATE_D1,
+	AHD_POWER_STATE_D2,
+	AHD_POWER_STATE_D3
+} ahd_power_state;
+
+void ahd_power_state_change(struct ahd_softc *ahd,
+			    ahd_power_state new_state);
+
+/******************************* PCI Routines *********************************/
+int			 ahd_linux_pci_init(void);
+void			 ahd_linux_pci_exit(void);
+int			 ahd_pci_map_registers(struct ahd_softc *ahd);
+int			 ahd_pci_map_int(struct ahd_softc *ahd);
+
+uint32_t		 ahd_pci_read_config(ahd_dev_softc_t pci,
+					     int reg, int width);
+void			 ahd_pci_write_config(ahd_dev_softc_t pci,
+					  int reg, uint32_t value,
+					  int width);
+
+static inline int ahd_get_pci_function(ahd_dev_softc_t);
+static inline int
+ahd_get_pci_function(ahd_dev_softc_t pci)
+{
+	return (PCI_FUNC(pci->devfn));
+}
+
+static inline int ahd_get_pci_slot(ahd_dev_softc_t);
+static inline int
+ahd_get_pci_slot(ahd_dev_softc_t pci)
+{
+	return (PCI_SLOT(pci->devfn));
+}
+
+static inline int ahd_get_pci_bus(ahd_dev_softc_t);
+static inline int
+ahd_get_pci_bus(ahd_dev_softc_t pci)
+{
+	return (pci->bus->number);
+}
+
+static inline void ahd_flush_device_writes(struct ahd_softc *);
+static inline void
+ahd_flush_device_writes(struct ahd_softc *ahd)
+{
+	/* XXX Is this sufficient for all architectures??? */
+	ahd_inb(ahd, INTSTAT);
+}
+
+/**************************** Proc FS Support *********************************/
+int	ahd_proc_write_seeprom(struct Scsi_Host *, char *, int);
+int	ahd_linux_show_info(struct seq_file *,struct Scsi_Host *);
+
+/*********************** Transaction Access Wrappers **************************/
+static inline void ahd_cmd_set_transaction_status(struct scsi_cmnd *, uint32_t);
+static inline void ahd_set_transaction_status(struct scb *, uint32_t);
+static inline void ahd_cmd_set_scsi_status(struct scsi_cmnd *, uint32_t);
+static inline void ahd_set_scsi_status(struct scb *, uint32_t);
+static inline uint32_t ahd_cmd_get_transaction_status(struct scsi_cmnd *cmd);
+static inline uint32_t ahd_get_transaction_status(struct scb *);
+static inline uint32_t ahd_cmd_get_scsi_status(struct scsi_cmnd *cmd);
+static inline uint32_t ahd_get_scsi_status(struct scb *);
+static inline void ahd_set_transaction_tag(struct scb *, int, u_int);
+static inline u_long ahd_get_transfer_length(struct scb *);
+static inline int ahd_get_transfer_dir(struct scb *);
+static inline void ahd_set_residual(struct scb *, u_long);
+static inline void ahd_set_sense_residual(struct scb *scb, u_long resid);
+static inline u_long ahd_get_residual(struct scb *);
+static inline u_long ahd_get_sense_residual(struct scb *);
+static inline int ahd_perform_autosense(struct scb *);
+static inline uint32_t ahd_get_sense_bufsize(struct ahd_softc *,
+					       struct scb *);
+static inline void ahd_notify_xfer_settings_change(struct ahd_softc *,
+						     struct ahd_devinfo *);
+static inline void ahd_platform_scb_free(struct ahd_softc *ahd,
+					   struct scb *scb);
+static inline void ahd_freeze_scb(struct scb *scb);
+
+static inline
+void ahd_cmd_set_transaction_status(struct scsi_cmnd *cmd, uint32_t status)
+{
+	cmd->result &= ~(CAM_STATUS_MASK << 16);
+	cmd->result |= status << 16;
+}
+
+static inline
+void ahd_set_transaction_status(struct scb *scb, uint32_t status)
+{
+	ahd_cmd_set_transaction_status(scb->io_ctx,status);
+}
+
+static inline
+void ahd_cmd_set_scsi_status(struct scsi_cmnd *cmd, uint32_t status)
+{
+	cmd->result &= ~0xFFFF;
+	cmd->result |= status;
+}
+
+static inline
+void ahd_set_scsi_status(struct scb *scb, uint32_t status)
+{
+	ahd_cmd_set_scsi_status(scb->io_ctx, status);
+}
+
+static inline
+uint32_t ahd_cmd_get_transaction_status(struct scsi_cmnd *cmd)
+{
+	return ((cmd->result >> 16) & CAM_STATUS_MASK);
+}
+
+static inline
+uint32_t ahd_get_transaction_status(struct scb *scb)
+{
+	return (ahd_cmd_get_transaction_status(scb->io_ctx));
+}
+
+static inline
+uint32_t ahd_cmd_get_scsi_status(struct scsi_cmnd *cmd)
+{
+	return (cmd->result & 0xFFFF);
+}
+
+static inline
+uint32_t ahd_get_scsi_status(struct scb *scb)
+{
+	return (ahd_cmd_get_scsi_status(scb->io_ctx));
+}
+
+static inline
+void ahd_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+	/*
+	 * Nothing to do for linux as the incoming transaction
+	 * has no concept of tag/non tagged, etc.
+	 */
+}
+
+static inline
+u_long ahd_get_transfer_length(struct scb *scb)
+{
+	return (scb->platform_data->xfer_len);
+}
+
+static inline
+int ahd_get_transfer_dir(struct scb *scb)
+{
+	return (scb->io_ctx->sc_data_direction);
+}
+
+static inline
+void ahd_set_residual(struct scb *scb, u_long resid)
+{
+	scsi_set_resid(scb->io_ctx, resid);
+}
+
+static inline
+void ahd_set_sense_residual(struct scb *scb, u_long resid)
+{
+	scb->platform_data->sense_resid = resid;
+}
+
+static inline
+u_long ahd_get_residual(struct scb *scb)
+{
+	return scsi_get_resid(scb->io_ctx);
+}
+
+static inline
+u_long ahd_get_sense_residual(struct scb *scb)
+{
+	return (scb->platform_data->sense_resid);
+}
+
+static inline
+int ahd_perform_autosense(struct scb *scb)
+{
+	/*
+	 * We always perform autosense in Linux.
+	 * On other platforms this is set on a
+	 * per-transaction basis.
+	 */
+	return (1);
+}
+
+static inline uint32_t
+ahd_get_sense_bufsize(struct ahd_softc *ahd, struct scb *scb)
+{
+	return (sizeof(struct scsi_sense_data));
+}
+
+static inline void
+ahd_notify_xfer_settings_change(struct ahd_softc *ahd,
+				struct ahd_devinfo *devinfo)
+{
+	/* Nothing to do here for linux */
+}
+
+static inline void
+ahd_platform_scb_free(struct ahd_softc *ahd, struct scb *scb)
+{
+	ahd->flags &= ~AHD_RESOURCE_SHORTAGE;
+}
+
+int	ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg);
+void	ahd_platform_free(struct ahd_softc *ahd);
+void	ahd_platform_init(struct ahd_softc *ahd);
+void	ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb);
+
+static inline void
+ahd_freeze_scb(struct scb *scb)
+{
+	if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) {
+                scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
+                scb->platform_data->dev->qfrozen++;
+        }
+}
+
+void	ahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev,
+			      struct ahd_devinfo *devinfo, ahd_queue_alg);
+int	ahd_platform_abort_scbs(struct ahd_softc *ahd, int target,
+				char channel, int lun, u_int tag,
+				role_t role, uint32_t status);
+irqreturn_t
+	ahd_linux_isr(int irq, void *dev_id);
+void	ahd_done(struct ahd_softc*, struct scb*);
+void	ahd_send_async(struct ahd_softc *, char channel,
+		       u_int target, u_int lun, ac_code);
+void	ahd_print_path(struct ahd_softc *, struct scb *);
+
+#ifdef CONFIG_PCI
+#define AHD_PCI_CONFIG 1
+#else
+#define AHD_PCI_CONFIG 0
+#endif
+#define bootverbose aic79xx_verbose
+extern uint32_t aic79xx_verbose;
+
+#endif /* _AIC79XX_LINUX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
new file mode 100644
index 0000000..8466aa7
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
@@ -0,0 +1,397 @@
+/*
+ * Linux driver attachment glue for PCI based U320 controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm_pci.c#25 $
+ */
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include "aic79xx_pci.h"
+
+/* Define the macro locally since it's different for different class of chips.
+ */
+#define ID(x)            \
+	ID2C(x),         \
+	ID2C(IDIROC(x))
+
+static const struct pci_device_id ahd_linux_pci_id_table[] = {
+	/* aic7901 based controllers */
+	ID(ID_AHA_29320A),
+	ID(ID_AHA_29320ALP),
+	ID(ID_AHA_29320LPE),
+	/* aic7902 based controllers */
+	ID(ID_AHA_29320),
+	ID(ID_AHA_29320B),
+	ID(ID_AHA_29320LP),
+	ID(ID_AHA_39320),
+	ID(ID_AHA_39320_B),
+	ID(ID_AHA_39320A),
+	ID(ID_AHA_39320D),
+	ID(ID_AHA_39320D_HP),
+	ID(ID_AHA_39320D_B),
+	ID(ID_AHA_39320D_B_HP),
+	/* Generic chip probes for devices we don't know exactly. */
+	ID16(ID_AIC7901 & ID_9005_GENERIC_MASK),
+	ID(ID_AIC7901A & ID_DEV_VENDOR_MASK),
+	ID16(ID_AIC7902 & ID_9005_GENERIC_MASK),
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ahd_linux_pci_id_table);
+
+#ifdef CONFIG_PM
+static int
+ahd_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
+{
+	struct ahd_softc *ahd = pci_get_drvdata(pdev);
+	int rc;
+
+	if ((rc = ahd_suspend(ahd)))
+		return rc;
+
+	ahd_pci_suspend(ahd);
+
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+
+	if (mesg.event & PM_EVENT_SLEEP)
+		pci_set_power_state(pdev, PCI_D3hot);
+
+	return rc;
+}
+
+static int
+ahd_linux_pci_dev_resume(struct pci_dev *pdev)
+{
+	struct ahd_softc *ahd = pci_get_drvdata(pdev);
+	int rc;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	if ((rc = pci_enable_device(pdev))) {
+		dev_printk(KERN_ERR, &pdev->dev,
+			   "failed to enable device after resume (%d)\n", rc);
+		return rc;
+	}
+
+	pci_set_master(pdev);
+
+	ahd_pci_resume(ahd);
+
+	ahd_resume(ahd);
+
+	return rc;
+}
+#endif
+
+static void
+ahd_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+	struct ahd_softc *ahd = pci_get_drvdata(pdev);
+	u_long s;
+
+	if (ahd->platform_data && ahd->platform_data->host)
+			scsi_remove_host(ahd->platform_data->host);
+
+	ahd_lock(ahd, &s);
+	ahd_intr_enable(ahd, FALSE);
+	ahd_unlock(ahd, &s);
+	ahd_free(ahd);
+}
+
+static void
+ahd_linux_pci_inherit_flags(struct ahd_softc *ahd)
+{
+	struct pci_dev *pdev = ahd->dev_softc, *master_pdev;
+	unsigned int master_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
+
+	master_pdev = pci_get_slot(pdev->bus, master_devfn);
+	if (master_pdev) {
+		struct ahd_softc *master = pci_get_drvdata(master_pdev);
+		if (master) {
+			ahd->flags &= ~AHD_BIOS_ENABLED;
+			ahd->flags |= master->flags & AHD_BIOS_ENABLED;
+		} else
+			printk(KERN_ERR "aic79xx: no multichannel peer found!\n");
+		pci_dev_put(master_pdev);
+	}
+}
+
+static int
+ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	char		 buf[80];
+	struct		 ahd_softc *ahd;
+	ahd_dev_softc_t	 pci;
+	const struct ahd_pci_identity *entry;
+	char		*name;
+	int		 error;
+	struct device	*dev = &pdev->dev;
+
+	pci = pdev;
+	entry = ahd_find_pci_device(pci);
+	if (entry == NULL)
+		return (-ENODEV);
+
+	/*
+	 * Allocate a softc for this card and
+	 * set it up for attachment by our
+	 * common detect routine.
+	 */
+	sprintf(buf, "ahd_pci:%d:%d:%d",
+		ahd_get_pci_bus(pci),
+		ahd_get_pci_slot(pci),
+		ahd_get_pci_function(pci));
+	name = kstrdup(buf, GFP_ATOMIC);
+	if (name == NULL)
+		return (-ENOMEM);
+	ahd = ahd_alloc(NULL, name);
+	if (ahd == NULL)
+		return (-ENOMEM);
+	if (pci_enable_device(pdev)) {
+		ahd_free(ahd);
+		return (-ENODEV);
+	}
+	pci_set_master(pdev);
+
+	if (sizeof(dma_addr_t) > 4) {
+		const u64 required_mask = dma_get_required_mask(dev);
+
+		if (required_mask > DMA_BIT_MASK(39) &&
+		    dma_set_mask(dev, DMA_BIT_MASK(64)) == 0)
+			ahd->flags |= AHD_64BIT_ADDRESSING;
+		else if (required_mask > DMA_BIT_MASK(32) &&
+			 dma_set_mask(dev, DMA_BIT_MASK(39)) == 0)
+			ahd->flags |= AHD_39BIT_ADDRESSING;
+		else
+			dma_set_mask(dev, DMA_BIT_MASK(32));
+	} else {
+		dma_set_mask(dev, DMA_BIT_MASK(32));
+	}
+	ahd->dev_softc = pci;
+	error = ahd_pci_config(ahd, entry);
+	if (error != 0) {
+		ahd_free(ahd);
+		return (-error);
+	}
+
+	/*
+	 * Second Function PCI devices need to inherit some
+	 * * settings from function 0.
+	 */
+	if ((ahd->features & AHD_MULTI_FUNC) && PCI_FUNC(pdev->devfn) != 0)
+		ahd_linux_pci_inherit_flags(ahd);
+
+	pci_set_drvdata(pdev, ahd);
+
+	ahd_linux_register_host(ahd, &aic79xx_driver_template);
+	return (0);
+}
+
+static struct pci_driver aic79xx_pci_driver = {
+	.name		= "aic79xx",
+	.probe		= ahd_linux_pci_dev_probe,
+#ifdef CONFIG_PM
+	.suspend	= ahd_linux_pci_dev_suspend,
+	.resume		= ahd_linux_pci_dev_resume,
+#endif
+	.remove		= ahd_linux_pci_dev_remove,
+	.id_table	= ahd_linux_pci_id_table
+};
+
+int
+ahd_linux_pci_init(void)
+{
+	return pci_register_driver(&aic79xx_pci_driver);
+}
+
+void
+ahd_linux_pci_exit(void)
+{
+	pci_unregister_driver(&aic79xx_pci_driver);
+}
+
+static int
+ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, resource_size_t *base,
+				 resource_size_t *base2)
+{
+	*base = pci_resource_start(ahd->dev_softc, 0);
+	/*
+	 * This is really the 3rd bar and should be at index 2,
+	 * but the Linux PCI code doesn't know how to "count" 64bit
+	 * bars.
+	 */
+	*base2 = pci_resource_start(ahd->dev_softc, 3);
+	if (*base == 0 || *base2 == 0)
+		return (ENOMEM);
+	if (!request_region(*base, 256, "aic79xx"))
+		return (ENOMEM);
+	if (!request_region(*base2, 256, "aic79xx")) {
+		release_region(*base, 256);
+		return (ENOMEM);
+	}
+	return (0);
+}
+
+static int
+ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
+				 resource_size_t *bus_addr,
+				 uint8_t __iomem **maddr)
+{
+	resource_size_t	start;
+	resource_size_t	base_page;
+	u_long	base_offset;
+	int	error = 0;
+
+	if (aic79xx_allow_memio == 0)
+		return (ENOMEM);
+
+	if ((ahd->bugs & AHD_PCIX_MMAPIO_BUG) != 0)
+		return (ENOMEM);
+
+	start = pci_resource_start(ahd->dev_softc, 1);
+	base_page = start & PAGE_MASK;
+	base_offset = start - base_page;
+	if (start != 0) {
+		*bus_addr = start;
+		if (!request_mem_region(start, 0x1000, "aic79xx"))
+			error = ENOMEM;
+		if (!error) {
+			*maddr = ioremap_nocache(base_page, base_offset + 512);
+			if (*maddr == NULL) {
+				error = ENOMEM;
+				release_mem_region(start, 0x1000);
+			} else
+				*maddr += base_offset;
+		}
+	} else
+		error = ENOMEM;
+	return (error);
+}
+
+int
+ahd_pci_map_registers(struct ahd_softc *ahd)
+{
+	uint32_t command;
+	resource_size_t base;
+	uint8_t	__iomem *maddr;
+	int	 error;
+
+	/*
+	 * If its allowed, we prefer memory mapped access.
+	 */
+	command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, 4);
+	command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
+	base = 0;
+	maddr = NULL;
+	error = ahd_linux_pci_reserve_mem_region(ahd, &base, &maddr);
+	if (error == 0) {
+		ahd->platform_data->mem_busaddr = base;
+		ahd->tags[0] = BUS_SPACE_MEMIO;
+		ahd->bshs[0].maddr = maddr;
+		ahd->tags[1] = BUS_SPACE_MEMIO;
+		ahd->bshs[1].maddr = maddr + 0x100;
+		ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+				     command | PCIM_CMD_MEMEN, 4);
+
+		if (ahd_pci_test_register_access(ahd) != 0) {
+
+			printk("aic79xx: PCI Device %d:%d:%d "
+			       "failed memory mapped test.  Using PIO.\n",
+			       ahd_get_pci_bus(ahd->dev_softc),
+			       ahd_get_pci_slot(ahd->dev_softc),
+			       ahd_get_pci_function(ahd->dev_softc));
+			iounmap(maddr);
+			release_mem_region(ahd->platform_data->mem_busaddr,
+					   0x1000);
+			ahd->bshs[0].maddr = NULL;
+			maddr = NULL;
+		} else
+			command |= PCIM_CMD_MEMEN;
+	} else if (bootverbose) {
+		printk("aic79xx: PCI%d:%d:%d MEM region 0x%llx "
+		       "unavailable. Cannot memory map device.\n",
+		       ahd_get_pci_bus(ahd->dev_softc),
+		       ahd_get_pci_slot(ahd->dev_softc),
+		       ahd_get_pci_function(ahd->dev_softc),
+		       (unsigned long long)base);
+	}
+
+	if (maddr == NULL) {
+		resource_size_t base2;
+
+		error = ahd_linux_pci_reserve_io_regions(ahd, &base, &base2);
+		if (error == 0) {
+			ahd->tags[0] = BUS_SPACE_PIO;
+			ahd->tags[1] = BUS_SPACE_PIO;
+			ahd->bshs[0].ioport = (u_long)base;
+			ahd->bshs[1].ioport = (u_long)base2;
+			command |= PCIM_CMD_PORTEN;
+		} else {
+			printk("aic79xx: PCI%d:%d:%d IO regions 0x%llx and "
+			       "0x%llx unavailable. Cannot map device.\n",
+			       ahd_get_pci_bus(ahd->dev_softc),
+			       ahd_get_pci_slot(ahd->dev_softc),
+			       ahd_get_pci_function(ahd->dev_softc),
+			       (unsigned long long)base,
+			       (unsigned long long)base2);
+		}
+	}
+	ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, 4);
+	return (error);
+}
+
+int
+ahd_pci_map_int(struct ahd_softc *ahd)
+{
+	int error;
+
+	error = request_irq(ahd->dev_softc->irq, ahd_linux_isr,
+			    IRQF_SHARED, "aic79xx", ahd);
+	if (!error)
+		ahd->platform_data->irq = ahd->dev_softc->irq;
+	
+	return (-error);
+}
+
+void
+ahd_power_state_change(struct ahd_softc *ahd, ahd_power_state new_state)
+{
+	pci_set_power_state(ahd->dev_softc, new_state);
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c
new file mode 100644
index 0000000..cc9bd26
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_pci.c
@@ -0,0 +1,1014 @@
+/*
+ * Product specific probe and attach routines for:
+ *	aic7901 and aic7902 SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx_pci.c#92 $
+ */
+
+#ifdef __linux__
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#else
+#include <dev/aic7xxx/aic79xx_osm.h>
+#include <dev/aic7xxx/aic79xx_inline.h>
+#endif
+
+#include "aic79xx_pci.h"
+
+static inline uint64_t
+ahd_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+	uint64_t id;
+
+	id = subvendor
+	   | (subdevice << 16)
+	   | ((uint64_t)vendor << 32)
+	   | ((uint64_t)device << 48);
+
+	return (id);
+}
+
+#define ID_AIC7902_PCI_REV_A4		0x3
+#define ID_AIC7902_PCI_REV_B0		0x10
+#define SUBID_HP			0x0E11
+
+#define DEVID_9005_HOSTRAID(id) ((id) & 0x80)
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define		DEVID_9005_TYPE_HBA		0x0	/* Standard Card */
+#define		DEVID_9005_TYPE_HBA_2EXT	0x1	/* 2 External Ports */
+#define		DEVID_9005_TYPE_IROC		0x8	/* Raid(0,1,10) Card */
+#define		DEVID_9005_TYPE_MB		0xF	/* On Motherboard */
+
+#define DEVID_9005_MFUNC(id) ((id) & 0x10)
+
+#define DEVID_9005_PACKETIZED(id) ((id) & 0x8000)
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define		SUBID_9005_TYPE_HBA		0x0	/* Standard Card */
+#define		SUBID_9005_TYPE_MB		0xF	/* On Motherboard */
+
+#define SUBID_9005_AUTOTERM(id)	(((id) & 0x10) == 0)
+
+#define SUBID_9005_LEGACYCONN_FUNC(id) ((id) & 0x20)
+
+#define SUBID_9005_SEEPTYPE(id) (((id) & 0x0C0) >> 6)
+#define		SUBID_9005_SEEPTYPE_NONE	0x0
+#define		SUBID_9005_SEEPTYPE_4K		0x1
+
+static ahd_device_setup_t ahd_aic7901_setup;
+static ahd_device_setup_t ahd_aic7901A_setup;
+static ahd_device_setup_t ahd_aic7902_setup;
+static ahd_device_setup_t ahd_aic790X_setup;
+
+static const struct ahd_pci_identity ahd_pci_ident_table[] =
+{
+	/* aic7901 based controllers */
+	{
+		ID_AHA_29320A,
+		ID_ALL_MASK,
+		"Adaptec 29320A Ultra320 SCSI adapter",
+		ahd_aic7901_setup
+	},
+	{
+		ID_AHA_29320ALP,
+		ID_ALL_MASK,
+		"Adaptec 29320ALP PCIx Ultra320 SCSI adapter",
+		ahd_aic7901_setup
+	},
+	{
+		ID_AHA_29320LPE,
+		ID_ALL_MASK,
+		"Adaptec 29320LPE PCIe Ultra320 SCSI adapter",
+		ahd_aic7901_setup
+	},
+	/* aic7901A based controllers */
+	{
+		ID_AHA_29320LP,
+		ID_ALL_MASK,
+		"Adaptec 29320LP Ultra320 SCSI adapter",
+		ahd_aic7901A_setup
+	},
+	/* aic7902 based controllers */	
+	{
+		ID_AHA_29320,
+		ID_ALL_MASK,
+		"Adaptec 29320 Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_29320B,
+		ID_ALL_MASK,
+		"Adaptec 29320B Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_39320,
+		ID_ALL_MASK,
+		"Adaptec 39320 Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_39320_B,
+		ID_ALL_MASK,
+		"Adaptec 39320 Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_39320_B_DELL,
+		ID_ALL_MASK,
+		"Adaptec (Dell OEM) 39320 Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_39320A,
+		ID_ALL_MASK,
+		"Adaptec 39320A Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_39320D,
+		ID_ALL_MASK,
+		"Adaptec 39320D Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_39320D_HP,
+		ID_ALL_MASK,
+		"Adaptec (HP OEM) 39320D Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_39320D_B,
+		ID_ALL_MASK,
+		"Adaptec 39320D Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	{
+		ID_AHA_39320D_B_HP,
+		ID_ALL_MASK,
+		"Adaptec (HP OEM) 39320D Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	},
+	/* Generic chip probes for devices we don't know 'exactly' */
+	{
+		ID_AIC7901 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec AIC7901 Ultra320 SCSI adapter",
+		ahd_aic7901_setup
+	},
+	{
+		ID_AIC7901A & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec AIC7901A Ultra320 SCSI adapter",
+		ahd_aic7901A_setup
+	},
+	{
+		ID_AIC7902 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec AIC7902 Ultra320 SCSI adapter",
+		ahd_aic7902_setup
+	}
+};
+
+static const u_int ahd_num_pci_devs = ARRAY_SIZE(ahd_pci_ident_table);
+		
+#define	DEVCONFIG		0x40
+#define		PCIXINITPAT	0x0000E000ul
+#define			PCIXINIT_PCI33_66	0x0000E000ul
+#define			PCIXINIT_PCIX50_66	0x0000C000ul
+#define			PCIXINIT_PCIX66_100	0x0000A000ul
+#define			PCIXINIT_PCIX100_133	0x00008000ul
+#define	PCI_BUS_MODES_INDEX(devconfig)	\
+	(((devconfig) & PCIXINITPAT) >> 13)
+static const char *pci_bus_modes[] =
+{
+	"PCI bus mode unknown",
+	"PCI bus mode unknown",
+	"PCI bus mode unknown",
+	"PCI bus mode unknown",
+	"PCI-X 101-133MHz",
+	"PCI-X 67-100MHz",
+	"PCI-X 50-66MHz",
+	"PCI 33 or 66MHz"
+};
+
+#define		TESTMODE	0x00000800ul
+#define		IRDY_RST	0x00000200ul
+#define		FRAME_RST	0x00000100ul
+#define		PCI64BIT	0x00000080ul
+#define		MRDCEN		0x00000040ul
+#define		ENDIANSEL	0x00000020ul
+#define		MIXQWENDIANEN	0x00000008ul
+#define		DACEN		0x00000004ul
+#define		STPWLEVEL	0x00000002ul
+#define		QWENDIANSEL	0x00000001ul
+
+#define	DEVCONFIG1		0x44
+#define		PREQDIS		0x01
+
+#define	CSIZE_LATTIME		0x0c
+#define		CACHESIZE	0x000000fful
+#define		LATTIME		0x0000ff00ul
+
+static int	ahd_check_extport(struct ahd_softc *ahd);
+static void	ahd_configure_termination(struct ahd_softc *ahd,
+					  u_int adapter_control);
+static void	ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat);
+static void	ahd_pci_intr(struct ahd_softc *ahd);
+
+const struct ahd_pci_identity *
+ahd_find_pci_device(ahd_dev_softc_t pci)
+{
+	uint64_t  full_id;
+	uint16_t  device;
+	uint16_t  vendor;
+	uint16_t  subdevice;
+	uint16_t  subvendor;
+	const struct ahd_pci_identity *entry;
+	u_int	  i;
+
+	vendor = ahd_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+	device = ahd_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+	subvendor = ahd_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+	subdevice = ahd_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+	full_id = ahd_compose_id(device,
+				 vendor,
+				 subdevice,
+				 subvendor);
+
+	/*
+	 * Controllers, mask out the IROC/HostRAID bit
+	 */
+	
+	full_id &= ID_ALL_IROC_MASK;
+
+	for (i = 0; i < ahd_num_pci_devs; i++) {
+		entry = &ahd_pci_ident_table[i];
+		if (entry->full_id == (full_id & entry->id_mask)) {
+			/* Honor exclusion entries. */
+			if (entry->name == NULL)
+				return (NULL);
+			return (entry);
+		}
+	}
+	return (NULL);
+}
+
+int
+ahd_pci_config(struct ahd_softc *ahd, const struct ahd_pci_identity *entry)
+{
+	struct scb_data *shared_scb_data;
+	u_int		 command;
+	uint32_t	 devconfig;
+	uint16_t	 subvendor; 
+	int		 error;
+
+	shared_scb_data = NULL;
+	ahd->description = entry->name;
+	/*
+	 * Record if this is an HP board.
+	 */
+	subvendor = ahd_pci_read_config(ahd->dev_softc,
+					PCIR_SUBVEND_0, /*bytes*/2);
+	if (subvendor == SUBID_HP)
+		ahd->flags |= AHD_HP_BOARD;
+
+	error = entry->setup(ahd);
+	if (error != 0)
+		return (error);
+	
+	devconfig = ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+	if ((devconfig & PCIXINITPAT) == PCIXINIT_PCI33_66) {
+		ahd->chip |= AHD_PCI;
+		/* Disable PCIX workarounds when running in PCI mode. */
+		ahd->bugs &= ~AHD_PCIX_BUG_MASK;
+	} else {
+		ahd->chip |= AHD_PCIX;
+	}
+	ahd->bus_description = pci_bus_modes[PCI_BUS_MODES_INDEX(devconfig)];
+
+	ahd_power_state_change(ahd, AHD_POWER_STATE_D0);
+
+	error = ahd_pci_map_registers(ahd);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * If we need to support high memory, enable dual
+	 * address cycles.  This bit must be set to enable
+	 * high address bit generation even if we are on a
+	 * 64bit bus (PCI64BIT set in devconfig).
+	 */
+	if ((ahd->flags & (AHD_39BIT_ADDRESSING|AHD_64BIT_ADDRESSING)) != 0) {
+		if (bootverbose)
+			printk("%s: Enabling 39Bit Addressing\n",
+			       ahd_name(ahd));
+		devconfig = ahd_pci_read_config(ahd->dev_softc,
+						DEVCONFIG, /*bytes*/4);
+		devconfig |= DACEN;
+		ahd_pci_write_config(ahd->dev_softc, DEVCONFIG,
+				     devconfig, /*bytes*/4);
+	}
+	
+	/* Ensure busmastering is enabled */
+	command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
+	command |= PCIM_CMD_BUSMASTEREN;
+	ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, /*bytes*/2);
+
+	error = ahd_softc_init(ahd);
+	if (error != 0)
+		return (error);
+
+	ahd->bus_intr = ahd_pci_intr;
+
+	error = ahd_reset(ahd, /*reinit*/FALSE);
+	if (error != 0)
+		return (ENXIO);
+
+	ahd->pci_cachesize =
+	    ahd_pci_read_config(ahd->dev_softc, CSIZE_LATTIME,
+				/*bytes*/1) & CACHESIZE;
+	ahd->pci_cachesize *= 4;
+
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	/* See if we have a SEEPROM and perform auto-term */
+	error = ahd_check_extport(ahd);
+	if (error != 0)
+		return (error);
+
+	/* Core initialization */
+	error = ahd_init(ahd);
+	if (error != 0)
+		return (error);
+	ahd->init_level++;
+
+	/*
+	 * Allow interrupts now that we are completely setup.
+	 */
+	return ahd_pci_map_int(ahd);
+}
+
+#ifdef CONFIG_PM
+void
+ahd_pci_suspend(struct ahd_softc *ahd)
+{
+	/*
+	 * Save chip register configuration data for chip resets
+	 * that occur during runtime and resume events.
+	 */
+	ahd->suspend_state.pci_state.devconfig =
+	    ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+	ahd->suspend_state.pci_state.command =
+	    ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/1);
+	ahd->suspend_state.pci_state.csize_lattime =
+	    ahd_pci_read_config(ahd->dev_softc, CSIZE_LATTIME, /*bytes*/1);
+
+}
+
+void
+ahd_pci_resume(struct ahd_softc *ahd)
+{
+	ahd_pci_write_config(ahd->dev_softc, DEVCONFIG,
+			     ahd->suspend_state.pci_state.devconfig, /*bytes*/4);
+	ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+			     ahd->suspend_state.pci_state.command, /*bytes*/1);
+	ahd_pci_write_config(ahd->dev_softc, CSIZE_LATTIME,
+			     ahd->suspend_state.pci_state.csize_lattime, /*bytes*/1);
+}
+#endif
+
+/*
+ * Perform some simple tests that should catch situations where
+ * our registers are invalidly mapped.
+ */
+int
+ahd_pci_test_register_access(struct ahd_softc *ahd)
+{
+	uint32_t cmd;
+	u_int	 targpcistat;
+	u_int	 pci_status1;
+	int	 error;
+	uint8_t	 hcntrl;
+
+	error = EIO;
+
+	/*
+	 * Enable PCI error interrupt status, but suppress NMIs
+	 * generated by SERR raised due to target aborts.
+	 */
+	cmd = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
+	ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+			     cmd & ~PCIM_CMD_SERRESPEN, /*bytes*/2);
+
+	/*
+	 * First a simple test to see if any
+	 * registers can be read.  Reading
+	 * HCNTRL has no side effects and has
+	 * at least one bit that is guaranteed to
+	 * be zero so it is a good register to
+	 * use for this test.
+	 */
+	hcntrl = ahd_inb(ahd, HCNTRL);
+	if (hcntrl == 0xFF)
+		goto fail;
+
+	/*
+	 * Next create a situation where write combining
+	 * or read prefetching could be initiated by the
+	 * CPU or host bridge.  Our device does not support
+	 * either, so look for data corruption and/or flaged
+	 * PCI errors.  First pause without causing another
+	 * chip reset.
+	 */
+	hcntrl &= ~CHIPRST;
+	ahd_outb(ahd, HCNTRL, hcntrl|PAUSE);
+	while (ahd_is_paused(ahd) == 0)
+		;
+
+	/* Clear any PCI errors that occurred before our driver attached. */
+	ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+	targpcistat = ahd_inb(ahd, TARGPCISTAT);
+	ahd_outb(ahd, TARGPCISTAT, targpcistat);
+	pci_status1 = ahd_pci_read_config(ahd->dev_softc,
+					  PCIR_STATUS + 1, /*bytes*/1);
+	ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+			     pci_status1, /*bytes*/1);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	ahd_outb(ahd, CLRINT, CLRPCIINT);
+
+	ahd_outb(ahd, SEQCTL0, PERRORDIS);
+	ahd_outl(ahd, SRAM_BASE, 0x5aa555aa);
+	if (ahd_inl(ahd, SRAM_BASE) != 0x5aa555aa)
+		goto fail;
+
+	if ((ahd_inb(ahd, INTSTAT) & PCIINT) != 0) {
+		ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+		targpcistat = ahd_inb(ahd, TARGPCISTAT);
+		if ((targpcistat & STA) != 0)
+			goto fail;
+	}
+
+	error = 0;
+
+fail:
+	if ((ahd_inb(ahd, INTSTAT) & PCIINT) != 0) {
+
+		ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+		targpcistat = ahd_inb(ahd, TARGPCISTAT);
+
+		/* Silently clear any latched errors. */
+		ahd_outb(ahd, TARGPCISTAT, targpcistat);
+		pci_status1 = ahd_pci_read_config(ahd->dev_softc,
+						  PCIR_STATUS + 1, /*bytes*/1);
+		ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+				     pci_status1, /*bytes*/1);
+		ahd_outb(ahd, CLRINT, CLRPCIINT);
+	}
+	ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS);
+	ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, cmd, /*bytes*/2);
+	return (error);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static int
+ahd_check_extport(struct ahd_softc *ahd)
+{
+	struct	vpd_config vpd;
+	struct	seeprom_config *sc;
+	u_int	adapter_control;
+	int	have_seeprom;
+	int	error;
+
+	sc = ahd->seep_config;
+	have_seeprom = ahd_acquire_seeprom(ahd);
+	if (have_seeprom) {
+		u_int start_addr;
+
+		/*
+		 * Fetch VPD for this function and parse it.
+		 */
+		if (bootverbose) 
+			printk("%s: Reading VPD from SEEPROM...",
+			       ahd_name(ahd));
+
+		/* Address is always in units of 16bit words */
+		start_addr = ((2 * sizeof(*sc))
+			    + (sizeof(vpd) * (ahd->channel - 'A'))) / 2;
+
+		error = ahd_read_seeprom(ahd, (uint16_t *)&vpd,
+					 start_addr, sizeof(vpd)/2,
+					 /*bytestream*/TRUE);
+		if (error == 0)
+			error = ahd_parse_vpddata(ahd, &vpd);
+		if (bootverbose) 
+			printk("%s: VPD parsing %s\n",
+			       ahd_name(ahd),
+			       error == 0 ? "successful" : "failed");
+
+		if (bootverbose) 
+			printk("%s: Reading SEEPROM...", ahd_name(ahd));
+
+		/* Address is always in units of 16bit words */
+		start_addr = (sizeof(*sc) / 2) * (ahd->channel - 'A');
+
+		error = ahd_read_seeprom(ahd, (uint16_t *)sc,
+					 start_addr, sizeof(*sc)/2,
+					 /*bytestream*/FALSE);
+
+		if (error != 0) {
+			printk("Unable to read SEEPROM\n");
+			have_seeprom = 0;
+		} else {
+			have_seeprom = ahd_verify_cksum(sc);
+
+			if (bootverbose) {
+				if (have_seeprom == 0)
+					printk ("checksum error\n");
+				else
+					printk ("done.\n");
+			}
+		}
+		ahd_release_seeprom(ahd);
+	}
+
+	if (!have_seeprom) {
+		u_int	  nvram_scb;
+
+		/*
+		 * Pull scratch ram settings and treat them as
+		 * if they are the contents of an seeprom if
+		 * the 'ADPT', 'BIOS', or 'ASPI' signature is found
+		 * in SCB 0xFF.  We manually compose the data as 16bit
+		 * values to avoid endian issues.
+		 */
+		ahd_set_scbptr(ahd, 0xFF);
+		nvram_scb = ahd_inb_scbram(ahd, SCB_BASE + NVRAM_SCB_OFFSET);
+		if (nvram_scb != 0xFF
+		 && ((ahd_inb_scbram(ahd, SCB_BASE + 0) == 'A'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'D'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'P'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'T')
+		  || (ahd_inb_scbram(ahd, SCB_BASE + 0) == 'B'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'I'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'O'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'S')
+		  || (ahd_inb_scbram(ahd, SCB_BASE + 0) == 'A'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'S'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'P'
+		   && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'I'))) {
+			uint16_t *sc_data;
+			int	  i;
+
+			ahd_set_scbptr(ahd, nvram_scb);
+			sc_data = (uint16_t *)sc;
+			for (i = 0; i < 64; i += 2)
+				*sc_data++ = ahd_inw_scbram(ahd, SCB_BASE+i);
+			have_seeprom = ahd_verify_cksum(sc);
+			if (have_seeprom)
+				ahd->flags |= AHD_SCB_CONFIG_USED;
+		}
+	}
+
+#ifdef AHD_DEBUG
+	if (have_seeprom != 0
+	 && (ahd_debug & AHD_DUMP_SEEPROM) != 0) {
+		uint16_t *sc_data;
+		int	  i;
+
+		printk("%s: Seeprom Contents:", ahd_name(ahd));
+		sc_data = (uint16_t *)sc;
+		for (i = 0; i < (sizeof(*sc)); i += 2)
+			printk("\n\t0x%.4x", sc_data[i]);
+		printk("\n");
+	}
+#endif
+
+	if (!have_seeprom) {
+		if (bootverbose)
+			printk("%s: No SEEPROM available.\n", ahd_name(ahd));
+		ahd->flags |= AHD_USEDEFAULTS;
+		error = ahd_default_config(ahd);
+		adapter_control = CFAUTOTERM|CFSEAUTOTERM;
+		kfree(ahd->seep_config);
+		ahd->seep_config = NULL;
+	} else {
+		error = ahd_parse_cfgdata(ahd, sc);
+		adapter_control = sc->adapter_control;
+	}
+	if (error != 0)
+		return (error);
+
+	ahd_configure_termination(ahd, adapter_control);
+
+	return (0);
+}
+
+static void
+ahd_configure_termination(struct ahd_softc *ahd, u_int adapter_control)
+{
+	int	 error;
+	u_int	 sxfrctl1;
+	uint8_t	 termctl;
+	uint32_t devconfig;
+
+	devconfig = ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+	devconfig &= ~STPWLEVEL;
+	if ((ahd->flags & AHD_STPWLEVEL_A) != 0)
+		devconfig |= STPWLEVEL;
+	if (bootverbose)
+		printk("%s: STPWLEVEL is %s\n",
+		       ahd_name(ahd), (devconfig & STPWLEVEL) ? "on" : "off");
+	ahd_pci_write_config(ahd->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+ 
+	/* Make sure current sensing is off. */
+	if ((ahd->flags & AHD_CURRENT_SENSING) != 0) {
+		(void)ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, 0);
+	}
+
+	/*
+	 * Read to sense.  Write to set.
+	 */
+	error = ahd_read_flexport(ahd, FLXADDR_TERMCTL, &termctl);
+	if ((adapter_control & CFAUTOTERM) == 0) {
+		if (bootverbose)
+			printk("%s: Manual Primary Termination\n",
+			       ahd_name(ahd));
+		termctl &= ~(FLX_TERMCTL_ENPRILOW|FLX_TERMCTL_ENPRIHIGH);
+		if ((adapter_control & CFSTERM) != 0)
+			termctl |= FLX_TERMCTL_ENPRILOW;
+		if ((adapter_control & CFWSTERM) != 0)
+			termctl |= FLX_TERMCTL_ENPRIHIGH;
+	} else if (error != 0) {
+		printk("%s: Primary Auto-Term Sensing failed! "
+		       "Using Defaults.\n", ahd_name(ahd));
+		termctl = FLX_TERMCTL_ENPRILOW|FLX_TERMCTL_ENPRIHIGH;
+	}
+
+	if ((adapter_control & CFSEAUTOTERM) == 0) {
+		if (bootverbose)
+			printk("%s: Manual Secondary Termination\n",
+			       ahd_name(ahd));
+		termctl &= ~(FLX_TERMCTL_ENSECLOW|FLX_TERMCTL_ENSECHIGH);
+		if ((adapter_control & CFSELOWTERM) != 0)
+			termctl |= FLX_TERMCTL_ENSECLOW;
+		if ((adapter_control & CFSEHIGHTERM) != 0)
+			termctl |= FLX_TERMCTL_ENSECHIGH;
+	} else if (error != 0) {
+		printk("%s: Secondary Auto-Term Sensing failed! "
+		       "Using Defaults.\n", ahd_name(ahd));
+		termctl |= FLX_TERMCTL_ENSECLOW|FLX_TERMCTL_ENSECHIGH;
+	}
+
+	/*
+	 * Now set the termination based on what we found.
+	 */
+	sxfrctl1 = ahd_inb(ahd, SXFRCTL1) & ~STPWEN;
+	ahd->flags &= ~AHD_TERM_ENB_A;
+	if ((termctl & FLX_TERMCTL_ENPRILOW) != 0) {
+		ahd->flags |= AHD_TERM_ENB_A;
+		sxfrctl1 |= STPWEN;
+	}
+	/* Must set the latch once in order to be effective. */
+	ahd_outb(ahd, SXFRCTL1, sxfrctl1|STPWEN);
+	ahd_outb(ahd, SXFRCTL1, sxfrctl1);
+
+	error = ahd_write_flexport(ahd, FLXADDR_TERMCTL, termctl);
+	if (error != 0) {
+		printk("%s: Unable to set termination settings!\n",
+		       ahd_name(ahd));
+	} else if (bootverbose) {
+		printk("%s: Primary High byte termination %sabled\n",
+		       ahd_name(ahd),
+		       (termctl & FLX_TERMCTL_ENPRIHIGH) ? "En" : "Dis");
+
+		printk("%s: Primary Low byte termination %sabled\n",
+		       ahd_name(ahd),
+		       (termctl & FLX_TERMCTL_ENPRILOW) ? "En" : "Dis");
+
+		printk("%s: Secondary High byte termination %sabled\n",
+		       ahd_name(ahd),
+		       (termctl & FLX_TERMCTL_ENSECHIGH) ? "En" : "Dis");
+
+		printk("%s: Secondary Low byte termination %sabled\n",
+		       ahd_name(ahd),
+		       (termctl & FLX_TERMCTL_ENSECLOW) ? "En" : "Dis");
+	}
+	return;
+}
+
+#define	DPE	0x80
+#define SSE	0x40
+#define	RMA	0x20
+#define	RTA	0x10
+#define STA	0x08
+#define DPR	0x01
+
+static const char *split_status_source[] =
+{
+	"DFF0",
+	"DFF1",
+	"OVLY",
+	"CMC",
+};
+
+static const char *pci_status_source[] =
+{
+	"DFF0",
+	"DFF1",
+	"SG",
+	"CMC",
+	"OVLY",
+	"NONE",
+	"MSI",
+	"TARG"
+};
+
+static const char *split_status_strings[] =
+{
+	"%s: Received split response in %s.\n",
+	"%s: Received split completion error message in %s\n",
+	"%s: Receive overrun in %s\n",
+	"%s: Count not complete in %s\n",
+	"%s: Split completion data bucket in %s\n",
+	"%s: Split completion address error in %s\n",
+	"%s: Split completion byte count error in %s\n",
+	"%s: Signaled Target-abort to early terminate a split in %s\n"
+};
+
+static const char *pci_status_strings[] =
+{
+	"%s: Data Parity Error has been reported via PERR# in %s\n",
+	"%s: Target initial wait state error in %s\n",
+	"%s: Split completion read data parity error in %s\n",
+	"%s: Split completion address attribute parity error in %s\n",
+	"%s: Received a Target Abort in %s\n",
+	"%s: Received a Master Abort in %s\n",
+	"%s: Signal System Error Detected in %s\n",
+	"%s: Address or Write Phase Parity Error Detected in %s.\n"
+};
+
+static void
+ahd_pci_intr(struct ahd_softc *ahd)
+{
+	uint8_t		pci_status[8];
+	ahd_mode_state	saved_modes;
+	u_int		pci_status1;
+	u_int		intstat;
+	u_int		i;
+	u_int		reg;
+	
+	intstat = ahd_inb(ahd, INTSTAT);
+
+	if ((intstat & SPLTINT) != 0)
+		ahd_pci_split_intr(ahd, intstat);
+
+	if ((intstat & PCIINT) == 0)
+		return;
+
+	printk("%s: PCI error Interrupt\n", ahd_name(ahd));
+	saved_modes = ahd_save_modes(ahd);
+	ahd_dump_card_state(ahd);
+	ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+	for (i = 0, reg = DF0PCISTAT; i < 8; i++, reg++) {
+
+		if (i == 5)
+			continue;
+		pci_status[i] = ahd_inb(ahd, reg);
+		/* Clear latched errors.  So our interrupt deasserts. */
+		ahd_outb(ahd, reg, pci_status[i]);
+	}
+
+	for (i = 0; i < 8; i++) {
+		u_int bit;
+	
+		if (i == 5)
+			continue;
+
+		for (bit = 0; bit < 8; bit++) {
+
+			if ((pci_status[i] & (0x1 << bit)) != 0) {
+				const char *s;
+
+				s = pci_status_strings[bit];
+				if (i == 7/*TARG*/ && bit == 3)
+					s = "%s: Signaled Target Abort\n";
+				printk(s, ahd_name(ahd), pci_status_source[i]);
+			}
+		}	
+	}
+	pci_status1 = ahd_pci_read_config(ahd->dev_softc,
+					  PCIR_STATUS + 1, /*bytes*/1);
+	ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+			     pci_status1, /*bytes*/1);
+	ahd_restore_modes(ahd, saved_modes);
+	ahd_outb(ahd, CLRINT, CLRPCIINT);
+	ahd_unpause(ahd);
+}
+
+static void
+ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat)
+{
+	uint8_t		split_status[4];
+	uint8_t		split_status1[4];
+	uint8_t		sg_split_status[2];
+	uint8_t		sg_split_status1[2];
+	ahd_mode_state	saved_modes;
+	u_int		i;
+	uint16_t	pcix_status;
+
+	/*
+	 * Check for splits in all modes.  Modes 0 and 1
+	 * additionally have SG engine splits to look at.
+	 */
+	pcix_status = ahd_pci_read_config(ahd->dev_softc, PCIXR_STATUS,
+					  /*bytes*/2);
+	printk("%s: PCI Split Interrupt - PCI-X status = 0x%x\n",
+	       ahd_name(ahd), pcix_status);
+	saved_modes = ahd_save_modes(ahd);
+	for (i = 0; i < 4; i++) {
+		ahd_set_modes(ahd, i, i);
+
+		split_status[i] = ahd_inb(ahd, DCHSPLTSTAT0);
+		split_status1[i] = ahd_inb(ahd, DCHSPLTSTAT1);
+		/* Clear latched errors.  So our interrupt deasserts. */
+		ahd_outb(ahd, DCHSPLTSTAT0, split_status[i]);
+		ahd_outb(ahd, DCHSPLTSTAT1, split_status1[i]);
+		if (i > 1)
+			continue;
+		sg_split_status[i] = ahd_inb(ahd, SGSPLTSTAT0);
+		sg_split_status1[i] = ahd_inb(ahd, SGSPLTSTAT1);
+		/* Clear latched errors.  So our interrupt deasserts. */
+		ahd_outb(ahd, SGSPLTSTAT0, sg_split_status[i]);
+		ahd_outb(ahd, SGSPLTSTAT1, sg_split_status1[i]);
+	}
+
+	for (i = 0; i < 4; i++) {
+		u_int bit;
+
+		for (bit = 0; bit < 8; bit++) {
+
+			if ((split_status[i] & (0x1 << bit)) != 0)
+				printk(split_status_strings[bit], ahd_name(ahd),
+				       split_status_source[i]);
+
+			if (i > 1)
+				continue;
+
+			if ((sg_split_status[i] & (0x1 << bit)) != 0)
+				printk(split_status_strings[bit], ahd_name(ahd), "SG");
+		}
+	}
+	/*
+	 * Clear PCI-X status bits.
+	 */
+	ahd_pci_write_config(ahd->dev_softc, PCIXR_STATUS,
+			     pcix_status, /*bytes*/2);
+	ahd_outb(ahd, CLRINT, CLRSPLTINT);
+	ahd_restore_modes(ahd, saved_modes);
+}
+
+static int
+ahd_aic7901_setup(struct ahd_softc *ahd)
+{
+
+	ahd->chip = AHD_AIC7901;
+	ahd->features = AHD_AIC7901_FE;
+	return (ahd_aic790X_setup(ahd));
+}
+
+static int
+ahd_aic7901A_setup(struct ahd_softc *ahd)
+{
+
+	ahd->chip = AHD_AIC7901A;
+	ahd->features = AHD_AIC7901A_FE;
+	return (ahd_aic790X_setup(ahd));
+}
+
+static int
+ahd_aic7902_setup(struct ahd_softc *ahd)
+{
+	ahd->chip = AHD_AIC7902;
+	ahd->features = AHD_AIC7902_FE;
+	return (ahd_aic790X_setup(ahd));
+}
+
+static int
+ahd_aic790X_setup(struct ahd_softc *ahd)
+{
+	ahd_dev_softc_t pci;
+	u_int rev;
+
+	pci = ahd->dev_softc;
+	rev = ahd_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev < ID_AIC7902_PCI_REV_A4) {
+		printk("%s: Unable to attach to unsupported chip revision %d\n",
+		       ahd_name(ahd), rev);
+		ahd_pci_write_config(pci, PCIR_COMMAND, 0, /*bytes*/2);
+		return (ENXIO);
+	}
+	ahd->channel = ahd_get_pci_function(pci) + 'A';
+	if (rev < ID_AIC7902_PCI_REV_B0) {
+		/*
+		 * Enable A series workarounds.
+		 */
+		ahd->bugs |= AHD_SENT_SCB_UPDATE_BUG|AHD_ABORT_LQI_BUG
+			  |  AHD_PKT_BITBUCKET_BUG|AHD_LONG_SETIMO_BUG
+			  |  AHD_NLQICRC_DELAYED_BUG|AHD_SCSIRST_BUG
+			  |  AHD_LQO_ATNO_BUG|AHD_AUTOFLUSH_BUG
+			  |  AHD_CLRLQO_AUTOCLR_BUG|AHD_PCIX_MMAPIO_BUG
+			  |  AHD_PCIX_CHIPRST_BUG|AHD_PCIX_SCBRAM_RD_BUG
+			  |  AHD_PKTIZED_STATUS_BUG|AHD_PKT_LUN_BUG
+			  |  AHD_MDFF_WSCBPTR_BUG|AHD_REG_SLOW_SETTLE_BUG
+			  |  AHD_SET_MODE_BUG|AHD_BUSFREEREV_BUG
+			  |  AHD_NONPACKFIFO_BUG|AHD_PACED_NEGTABLE_BUG
+			  |  AHD_FAINT_LED_BUG;
+
+		/*
+		 * IO Cell parameter setup.
+		 */
+		AHD_SET_PRECOMP(ahd, AHD_PRECOMP_CUTBACK_29);
+
+		if ((ahd->flags & AHD_HP_BOARD) == 0)
+			AHD_SET_SLEWRATE(ahd, AHD_SLEWRATE_DEF_REVA);
+	} else {
+		/* This is revision B and newer. */
+		extern uint32_t aic79xx_slowcrc;
+		u_int devconfig1;
+
+		ahd->features |= AHD_RTI|AHD_NEW_IOCELL_OPTS
+			      |  AHD_NEW_DFCNTRL_OPTS|AHD_FAST_CDB_DELIVERY
+			      |  AHD_BUSFREEREV_BUG;
+		ahd->bugs |= AHD_LQOOVERRUN_BUG|AHD_EARLY_REQ_BUG;
+
+		/* If the user requested that the SLOWCRC bit to be set. */
+		if (aic79xx_slowcrc)
+			ahd->features |= AHD_AIC79XXB_SLOWCRC;
+
+		/*
+		 * Some issues have been resolved in the 7901B.
+		 */
+		if ((ahd->features & AHD_MULTI_FUNC) != 0)
+			ahd->bugs |= AHD_INTCOLLISION_BUG|AHD_ABORT_LQI_BUG;
+
+		/*
+		 * IO Cell parameter setup.
+		 */
+		AHD_SET_PRECOMP(ahd, AHD_PRECOMP_CUTBACK_29);
+		AHD_SET_SLEWRATE(ahd, AHD_SLEWRATE_DEF_REVB);
+		AHD_SET_AMPLITUDE(ahd, AHD_AMPLITUDE_DEF);
+
+		/*
+		 * Set the PREQDIS bit for H2B which disables some workaround
+		 * that doesn't work on regular PCI busses.
+		 * XXX - Find out exactly what this does from the hardware
+		 * 	 folks!
+		 */
+		devconfig1 = ahd_pci_read_config(pci, DEVCONFIG1, /*bytes*/1);
+		ahd_pci_write_config(pci, DEVCONFIG1,
+				     devconfig1|PREQDIS, /*bytes*/1);
+		devconfig1 = ahd_pci_read_config(pci, DEVCONFIG1, /*bytes*/1);
+	}
+
+	return (0);
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.h b/drivers/scsi/aic7xxx/aic79xx_pci.h
new file mode 100644
index 0000000..16b7c70
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_pci.h
@@ -0,0 +1,72 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id$
+ *
+ */
+#ifndef _AIC79XX_PCI_H_
+#define _AIC79XX_PCI_H_
+
+#define ID_ALL_MASK			0xFFFFFFFFFFFFFFFFull
+#define ID_ALL_IROC_MASK		0xFF7FFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK		0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK		0xFFF0FFFF00000000ull
+#define ID_9005_GENERIC_IROC_MASK	0xFF70FFFF00000000ull
+
+#define ID_AIC7901			0x800F9005FFFF9005ull
+#define ID_AHA_29320A			0x8000900500609005ull
+#define ID_AHA_29320ALP			0x8017900500449005ull
+#define ID_AHA_29320LPE			0x8017900500459005ull
+
+#define ID_AIC7901A			0x801E9005FFFF9005ull
+#define ID_AHA_29320LP			0x8014900500449005ull
+
+#define ID_AIC7902			0x801F9005FFFF9005ull
+#define ID_AIC7902_B			0x801D9005FFFF9005ull
+#define ID_AHA_39320			0x8010900500409005ull
+#define ID_AHA_29320			0x8012900500429005ull
+#define ID_AHA_29320B			0x8013900500439005ull
+#define ID_AHA_39320_B			0x8015900500409005ull
+#define ID_AHA_39320_B_DELL		0x8015900501681028ull
+#define ID_AHA_39320A			0x8016900500409005ull
+#define ID_AHA_39320D			0x8011900500419005ull
+#define ID_AHA_39320D_B			0x801C900500419005ull
+#define ID_AHA_39320D_HP		0x8011900500AC0E11ull
+#define ID_AHA_39320D_B_HP		0x801C900500AC0E11ull
+
+#endif /* _AIC79XX_PCI_H_ */
diff --git a/drivers/scsi/aic7xxx/aic79xx_proc.c b/drivers/scsi/aic7xxx/aic79xx_proc.c
new file mode 100644
index 0000000..add2da5
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_proc.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_proc.c#19 $
+ */
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+
+static void	ahd_dump_target_state(struct ahd_softc *ahd,
+				      struct seq_file *m,
+				      u_int our_id, char channel,
+				      u_int target_id);
+static void	ahd_dump_device_state(struct seq_file *m,
+				      struct scsi_device *sdev);
+
+/*
+ * Table of syncrates that don't follow the "divisible by 4"
+ * rule. This table will be expanded in future SCSI specs.
+ */
+static const struct {
+	u_int period_factor;
+	u_int period;	/* in 100ths of ns */
+} scsi_syncrates[] = {
+	{ 0x08, 625 },	/* FAST-160 */
+	{ 0x09, 1250 },	/* FAST-80 */
+	{ 0x0a, 2500 },	/* FAST-40 40MHz */
+	{ 0x0b, 3030 },	/* FAST-40 33MHz */
+	{ 0x0c, 5000 }	/* FAST-20 */
+};
+
+/*
+ * Return the frequency in kHz corresponding to the given
+ * sync period factor.
+ */
+static u_int
+ahd_calc_syncsrate(u_int period_factor)
+{
+	int i;
+
+	/* See if the period is in the "exception" table */
+	for (i = 0; i < ARRAY_SIZE(scsi_syncrates); i++) {
+
+		if (period_factor == scsi_syncrates[i].period_factor) {
+			/* Period in kHz */
+			return (100000000 / scsi_syncrates[i].period);
+		}
+	}
+
+	/*
+	 * Wasn't in the table, so use the standard
+	 * 4 times conversion.
+	 */
+	return (10000000 / (period_factor * 4 * 10));
+}
+
+static void
+ahd_format_transinfo(struct seq_file *m, struct ahd_transinfo *tinfo)
+{
+	u_int speed;
+	u_int freq;
+	u_int mb;
+
+	if (tinfo->period == AHD_PERIOD_UNKNOWN) {
+		seq_puts(m, "Renegotiation Pending\n");
+		return;
+	}
+        speed = 3300;
+        freq = 0;
+	if (tinfo->offset != 0) {
+		freq = ahd_calc_syncsrate(tinfo->period);
+		speed = freq;
+	}
+	speed *= (0x01 << tinfo->width);
+        mb = speed / 1000;
+        if (mb > 0)
+		seq_printf(m, "%d.%03dMB/s transfers", mb, speed % 1000);
+        else
+		seq_printf(m, "%dKB/s transfers", speed);
+
+	if (freq != 0) {
+		int	printed_options;
+
+		printed_options = 0;
+		seq_printf(m, " (%d.%03dMHz", freq / 1000, freq % 1000);
+		if ((tinfo->ppr_options & MSG_EXT_PPR_RD_STRM) != 0) {
+			seq_puts(m, " RDSTRM");
+			printed_options++;
+		}
+		if ((tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
+			seq_puts(m, printed_options ? "|DT" : " DT");
+			printed_options++;
+		}
+		if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+			seq_puts(m, printed_options ? "|IU" : " IU");
+			printed_options++;
+		}
+		if ((tinfo->ppr_options & MSG_EXT_PPR_RTI) != 0) {
+			seq_puts(m, printed_options ? "|RTI" : " RTI");
+			printed_options++;
+		}
+		if ((tinfo->ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) {
+			seq_puts(m, printed_options ? "|QAS" : " QAS");
+			printed_options++;
+		}
+	}
+
+	if (tinfo->width > 0) {
+		if (freq != 0) {
+			seq_puts(m, ", ");
+		} else {
+			seq_puts(m, " (");
+		}
+		seq_printf(m, "%dbit)", 8 * (0x01 << tinfo->width));
+	} else if (freq != 0) {
+		seq_putc(m, ')');
+	}
+	seq_putc(m, '\n');
+}
+
+static void
+ahd_dump_target_state(struct ahd_softc *ahd, struct seq_file *m,
+		      u_int our_id, char channel, u_int target_id)
+{
+	struct  scsi_target *starget;
+	struct	ahd_initiator_tinfo *tinfo;
+	struct	ahd_tmode_tstate *tstate;
+	int	lun;
+
+	tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
+				    target_id, &tstate);
+	seq_printf(m, "Target %d Negotiation Settings\n", target_id);
+	seq_puts(m, "\tUser: ");
+	ahd_format_transinfo(m, &tinfo->user);
+	starget = ahd->platform_data->starget[target_id];
+	if (starget == NULL)
+		return;
+
+	seq_puts(m, "\tGoal: ");
+	ahd_format_transinfo(m, &tinfo->goal);
+	seq_puts(m, "\tCurr: ");
+	ahd_format_transinfo(m, &tinfo->curr);
+
+	for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+		struct scsi_device *dev;
+
+		dev = scsi_device_lookup_by_target(starget, lun);
+
+		if (dev == NULL)
+			continue;
+
+		ahd_dump_device_state(m, dev);
+	}
+}
+
+static void
+ahd_dump_device_state(struct seq_file *m, struct scsi_device *sdev)
+{
+	struct ahd_linux_device *dev = scsi_transport_device_data(sdev);
+
+	seq_printf(m, "\tChannel %c Target %d Lun %d Settings\n",
+		  sdev->sdev_target->channel + 'A',
+		   sdev->sdev_target->id, (u8)sdev->lun);
+
+	seq_printf(m, "\t\tCommands Queued %ld\n", dev->commands_issued);
+	seq_printf(m, "\t\tCommands Active %d\n", dev->active);
+	seq_printf(m, "\t\tCommand Openings %d\n", dev->openings);
+	seq_printf(m, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+	seq_printf(m, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+int
+ahd_proc_write_seeprom(struct Scsi_Host *shost, char *buffer, int length)
+{
+	struct	ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata;
+	ahd_mode_state saved_modes;
+	int have_seeprom;
+	u_long s;
+	int paused;
+	int written;
+
+	/* Default to failure. */
+	written = -EINVAL;
+	ahd_lock(ahd, &s);
+	paused = ahd_is_paused(ahd);
+	if (!paused)
+		ahd_pause(ahd);
+
+	saved_modes = ahd_save_modes(ahd);
+	ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+	if (length != sizeof(struct seeprom_config)) {
+		printk("ahd_proc_write_seeprom: incorrect buffer size\n");
+		goto done;
+	}
+
+	have_seeprom = ahd_verify_cksum((struct seeprom_config*)buffer);
+	if (have_seeprom == 0) {
+		printk("ahd_proc_write_seeprom: cksum verification failed\n");
+		goto done;
+	}
+
+	have_seeprom = ahd_acquire_seeprom(ahd);
+	if (!have_seeprom) {
+		printk("ahd_proc_write_seeprom: No Serial EEPROM\n");
+		goto done;
+	} else {
+		u_int start_addr;
+
+		if (ahd->seep_config == NULL) {
+			ahd->seep_config = kmalloc(sizeof(*ahd->seep_config), GFP_ATOMIC);
+			if (ahd->seep_config == NULL) {
+				printk("aic79xx: Unable to allocate serial "
+				       "eeprom buffer.  Write failing\n");
+				goto done;
+			}
+		}
+		printk("aic79xx: Writing Serial EEPROM\n");
+		start_addr = 32 * (ahd->channel - 'A');
+		ahd_write_seeprom(ahd, (u_int16_t *)buffer, start_addr,
+				  sizeof(struct seeprom_config)/2);
+		ahd_read_seeprom(ahd, (uint16_t *)ahd->seep_config,
+				 start_addr, sizeof(struct seeprom_config)/2,
+				 /*ByteStream*/FALSE);
+		ahd_release_seeprom(ahd);
+		written = length;
+	}
+
+done:
+	ahd_restore_modes(ahd, saved_modes);
+	if (!paused)
+		ahd_unpause(ahd);
+	ahd_unlock(ahd, &s);
+	return (written);
+}
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+ahd_linux_show_info(struct seq_file *m, struct Scsi_Host *shost)
+{
+	struct	ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata;
+	char	ahd_info[256];
+	u_int	max_targ;
+	u_int	i;
+
+	seq_printf(m, "Adaptec AIC79xx driver version: %s\n",
+		  AIC79XX_DRIVER_VERSION);
+	seq_printf(m, "%s\n", ahd->description);
+	ahd_controller_info(ahd, ahd_info);
+	seq_printf(m, "%s\n", ahd_info);
+	seq_printf(m, "Allocated SCBs: %d, SG List Length: %d\n\n",
+		  ahd->scb_data.numscbs, AHD_NSEG);
+
+	max_targ = 16;
+
+	if (ahd->seep_config == NULL)
+		seq_puts(m, "No Serial EEPROM\n");
+	else {
+		seq_puts(m, "Serial EEPROM:\n");
+		for (i = 0; i < sizeof(*ahd->seep_config)/2; i++) {
+			if (((i % 8) == 0) && (i != 0)) {
+				seq_putc(m, '\n');
+			}
+			seq_printf(m, "0x%.4x ",
+				  ((uint16_t*)ahd->seep_config)[i]);
+		}
+		seq_putc(m, '\n');
+	}
+	seq_putc(m, '\n');
+
+	if ((ahd->features & AHD_WIDE) == 0)
+		max_targ = 8;
+
+	for (i = 0; i < max_targ; i++) {
+
+		ahd_dump_target_state(ahd, m, ahd->our_id, 'A',
+				      /*target_id*/i);
+	}
+	return 0;
+}
diff --git a/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
new file mode 100644
index 0000000..cdcead0
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
@@ -0,0 +1,2685 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *		 from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#120 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#77 $
+ */
+typedef int (ahd_reg_print_t)(u_int, u_int *, u_int);
+typedef struct ahd_reg_parse_entry {
+	char	*name;
+	uint8_t	 value;
+	uint8_t	 mask;
+} ahd_reg_parse_entry_t;
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_mode_ptr_print;
+#else
+#define ahd_mode_ptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "MODE_PTR", 0x00, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intstat_print;
+#else
+#define ahd_intstat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INTSTAT", 0x01, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintcode_print;
+#else
+#define ahd_seqintcode_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEQINTCODE", 0x02, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_error_print;
+#else
+#define ahd_error_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "ERROR", 0x04, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hescb_qoff_print;
+#else
+#define ahd_hescb_qoff_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "HESCB_QOFF", 0x08, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_hs_mailbox_print;
+#else
+#define ahd_hs_mailbox_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "HS_MAILBOX", 0x0b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintstat_print;
+#else
+#define ahd_seqintstat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEQINTSTAT", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrseqintstat_print;
+#else
+#define ahd_clrseqintstat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CLRSEQINTSTAT", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_swtimer_print;
+#else
+#define ahd_swtimer_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SWTIMER", 0x0e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sescb_qoff_print;
+#else
+#define ahd_sescb_qoff_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SESCB_QOFF", 0x12, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intctl_print;
+#else
+#define ahd_intctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INTCTL", 0x18, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfcntrl_print;
+#else
+#define ahd_dfcntrl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DFCNTRL", 0x19, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dfstatus_print;
+#else
+#define ahd_dfstatus_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DFSTATUS", 0x1a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sg_cache_shadow_print;
+#else
+#define ahd_sg_cache_shadow_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SG_CACHE_SHADOW", 0x1b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqin_print;
+#else
+#define ahd_lqin_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQIN", 0x20, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lunptr_print;
+#else
+#define ahd_lunptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LUNPTR", 0x22, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdlenptr_print;
+#else
+#define ahd_cmdlenptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CMDLENPTR", 0x25, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_attrptr_print;
+#else
+#define ahd_attrptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "ATTRPTR", 0x26, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_flagptr_print;
+#else
+#define ahd_flagptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "FLAGPTR", 0x27, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdptr_print;
+#else
+#define ahd_cmdptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CMDPTR", 0x28, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qnextptr_print;
+#else
+#define ahd_qnextptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "QNEXTPTR", 0x29, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_abrtbyteptr_print;
+#else
+#define ahd_abrtbyteptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "ABRTBYTEPTR", 0x2b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_abrtbitptr_print;
+#else
+#define ahd_abrtbitptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "ABRTBITPTR", 0x2c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lunlen_print;
+#else
+#define ahd_lunlen_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LUNLEN", 0x30, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cdblimit_print;
+#else
+#define ahd_cdblimit_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CDBLIMIT", 0x31, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmd_print;
+#else
+#define ahd_maxcmd_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "MAXCMD", 0x32, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_maxcmdcnt_print;
+#else
+#define ahd_maxcmdcnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "MAXCMDCNT", 0x33, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqctl1_print;
+#else
+#define ahd_lqctl1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQCTL1", 0x38, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqctl2_print;
+#else
+#define ahd_lqctl2_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQCTL2", 0x39, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq0_print;
+#else
+#define ahd_scsiseq0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCSISEQ0", 0x3a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq1_print;
+#else
+#define ahd_scsiseq1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCSISEQ1", 0x3b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sxfrctl0_print;
+#else
+#define ahd_sxfrctl0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SXFRCTL0", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dffstat_print;
+#else
+#define ahd_dffstat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DFFSTAT", 0x3f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_multargid_print;
+#else
+#define ahd_multargid_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "MULTARGID", 0x40, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsisigi_print;
+#else
+#define ahd_scsisigi_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCSISIGI", 0x41, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiphase_print;
+#else
+#define ahd_scsiphase_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCSIPHASE", 0x42, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsidat_print;
+#else
+#define ahd_scsidat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCSIDAT", 0x44, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsibus_print;
+#else
+#define ahd_scsibus_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCSIBUS", 0x46, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_targidin_print;
+#else
+#define ahd_targidin_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "TARGIDIN", 0x48, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_selid_print;
+#else
+#define ahd_selid_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SELID", 0x49, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sblkctl_print;
+#else
+#define ahd_sblkctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SBLKCTL", 0x4a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat0_print;
+#else
+#define ahd_sstat0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SSTAT0", 0x4b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode0_print;
+#else
+#define ahd_simode0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SIMODE0", 0x4b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat1_print;
+#else
+#define ahd_sstat1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SSTAT1", 0x4c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat2_print;
+#else
+#define ahd_sstat2_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SSTAT2", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint2_print;
+#else
+#define ahd_clrsint2_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CLRSINT2", 0x4d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_perrdiag_print;
+#else
+#define ahd_perrdiag_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "PERRDIAG", 0x4e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistate_print;
+#else
+#define ahd_lqistate_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQISTATE", 0x4e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_soffcnt_print;
+#else
+#define ahd_soffcnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SOFFCNT", 0x4f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostate_print;
+#else
+#define ahd_lqostate_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQOSTATE", 0x4f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat0_print;
+#else
+#define ahd_lqistat0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQISTAT0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqiint0_print;
+#else
+#define ahd_clrlqiint0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CLRLQIINT0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqimode0_print;
+#else
+#define ahd_lqimode0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQIMODE0", 0x50, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqimode1_print;
+#else
+#define ahd_lqimode1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQIMODE1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat1_print;
+#else
+#define ahd_lqistat1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQISTAT1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqiint1_print;
+#else
+#define ahd_clrlqiint1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CLRLQIINT1", 0x51, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqistat2_print;
+#else
+#define ahd_lqistat2_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQISTAT2", 0x52, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sstat3_print;
+#else
+#define ahd_sstat3_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SSTAT3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode3_print;
+#else
+#define ahd_simode3_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SIMODE3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrsint3_print;
+#else
+#define ahd_clrsint3_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CLRSINT3", 0x53, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat0_print;
+#else
+#define ahd_lqostat0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQOSTAT0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqoint0_print;
+#else
+#define ahd_clrlqoint0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CLRLQOINT0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqomode0_print;
+#else
+#define ahd_lqomode0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQOMODE0", 0x54, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqomode1_print;
+#else
+#define ahd_lqomode1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQOMODE1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat1_print;
+#else
+#define ahd_lqostat1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQOSTAT1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrlqoint1_print;
+#else
+#define ahd_clrlqoint1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CLRLQOINT1", 0x55, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqostat2_print;
+#else
+#define ahd_lqostat2_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQOSTAT2", 0x56, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_os_space_cnt_print;
+#else
+#define ahd_os_space_cnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "OS_SPACE_CNT", 0x56, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_simode1_print;
+#else
+#define ahd_simode1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SIMODE1", 0x57, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_gsfifo_print;
+#else
+#define ahd_gsfifo_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "GSFIFO", 0x58, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dffsxfrctl_print;
+#else
+#define ahd_dffsxfrctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DFFSXFRCTL", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lqoscsctl_print;
+#else
+#define ahd_lqoscsctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LQOSCSCTL", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_nextscb_print;
+#else
+#define ahd_nextscb_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "NEXTSCB", 0x5a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_clrseqintsrc_print;
+#else
+#define ahd_clrseqintsrc_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CLRSEQINTSRC", 0x5b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintsrc_print;
+#else
+#define ahd_seqintsrc_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEQINTSRC", 0x5b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_currscb_print;
+#else
+#define ahd_currscb_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CURRSCB", 0x5c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqimode_print;
+#else
+#define ahd_seqimode_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEQIMODE", 0x5c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_mdffstat_print;
+#else
+#define ahd_mdffstat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "MDFFSTAT", 0x5d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lastscb_print;
+#else
+#define ahd_lastscb_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LASTSCB", 0x5e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negoaddr_print;
+#else
+#define ahd_negoaddr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "NEGOADDR", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negperiod_print;
+#else
+#define ahd_negperiod_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "NEGPERIOD", 0x61, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negoffset_print;
+#else
+#define ahd_negoffset_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "NEGOFFSET", 0x62, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negppropts_print;
+#else
+#define ahd_negppropts_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "NEGPPROPTS", 0x63, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_negconopts_print;
+#else
+#define ahd_negconopts_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "NEGCONOPTS", 0x64, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_annexcol_print;
+#else
+#define ahd_annexcol_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "ANNEXCOL", 0x65, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_annexdat_print;
+#else
+#define ahd_annexdat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "ANNEXDAT", 0x66, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scschkn_print;
+#else
+#define ahd_scschkn_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCSCHKN", 0x66, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_iownid_print;
+#else
+#define ahd_iownid_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "IOWNID", 0x67, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_shcnt_print;
+#else
+#define ahd_shcnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SHCNT", 0x68, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_townid_print;
+#else
+#define ahd_townid_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "TOWNID", 0x69, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seloid_print;
+#else
+#define ahd_seloid_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SELOID", 0x6b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbhaddr_print;
+#else
+#define ahd_scbhaddr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCBHADDR", 0x7c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sghaddr_print;
+#else
+#define ahd_sghaddr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SGHADDR", 0x7c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbhcnt_print;
+#else
+#define ahd_scbhcnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCBHCNT", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sghcnt_print;
+#else
+#define ahd_sghcnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SGHCNT", 0x84, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_pcixctl_print;
+#else
+#define ahd_pcixctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "PCIXCTL", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchspltstat0_print;
+#else
+#define ahd_dchspltstat0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DCHSPLTSTAT0", 0x96, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dchspltstat1_print;
+#else
+#define ahd_dchspltstat1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DCHSPLTSTAT1", 0x97, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgspltstat0_print;
+#else
+#define ahd_sgspltstat0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SGSPLTSTAT0", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sgspltstat1_print;
+#else
+#define ahd_sgspltstat1_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SGSPLTSTAT1", 0x9f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_df0pcistat_print;
+#else
+#define ahd_df0pcistat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DF0PCISTAT", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_reg0_print;
+#else
+#define ahd_reg0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "REG0", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_reg_isr_print;
+#else
+#define ahd_reg_isr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "REG_ISR", 0xa4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sg_state_print;
+#else
+#define ahd_sg_state_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SG_STATE", 0xa6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_targpcistat_print;
+#else
+#define ahd_targpcistat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "TARGPCISTAT", 0xa7, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scbautoptr_print;
+#else
+#define ahd_scbautoptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCBAUTOPTR", 0xab, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbaddr_print;
+#else
+#define ahd_ccscbaddr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CCSCBADDR", 0xac, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbctl_print;
+#else
+#define ahd_ccscbctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CCSCBCTL", 0xad, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccsgctl_print;
+#else
+#define ahd_ccsgctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CCSGCTL", 0xad, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_ccscbram_print;
+#else
+#define ahd_ccscbram_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CCSCBRAM", 0xb0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_brddat_print;
+#else
+#define ahd_brddat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "BRDDAT", 0xb8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seeadr_print;
+#else
+#define ahd_seeadr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEEADR", 0xba, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seedat_print;
+#else
+#define ahd_seedat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEEDAT", 0xbc, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seectl_print;
+#else
+#define ahd_seectl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEECTL", 0xbe, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seestat_print;
+#else
+#define ahd_seestat_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEESTAT", 0xbe, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspdatactl_print;
+#else
+#define ahd_dspdatactl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DSPDATACTL", 0xc1, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_dspselect_print;
+#else
+#define ahd_dspselect_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "DSPSELECT", 0xc4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_wrtbiasctl_print;
+#else
+#define ahd_wrtbiasctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "WRTBIASCTL", 0xc5, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqctl0_print;
+#else
+#define ahd_seqctl0_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEQCTL0", 0xd6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seqintctl_print;
+#else
+#define ahd_seqintctl_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEQINTCTL", 0xd9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_prgmcnt_print;
+#else
+#define ahd_prgmcnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "PRGMCNT", 0xde, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_none_print;
+#else
+#define ahd_none_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "NONE", 0xea, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intvec1_addr_print;
+#else
+#define ahd_intvec1_addr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INTVEC1_ADDR", 0xf4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_curaddr_print;
+#else
+#define ahd_curaddr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CURADDR", 0xf4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_intvec2_addr_print;
+#else
+#define ahd_intvec2_addr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INTVEC2_ADDR", 0xf6, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_longjmp_addr_print;
+#else
+#define ahd_longjmp_addr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LONGJMP_ADDR", 0xf8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_accum_save_print;
+#else
+#define ahd_accum_save_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "ACCUM_SAVE", 0xfa, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_scb_tails_print;
+#else
+#define ahd_waiting_scb_tails_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "WAITING_SCB_TAILS", 0x100, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_sram_base_print;
+#else
+#define ahd_sram_base_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SRAM_BASE", 0x100, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_tid_head_print;
+#else
+#define ahd_waiting_tid_head_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "WAITING_TID_HEAD", 0x120, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_waiting_tid_tail_print;
+#else
+#define ahd_waiting_tid_tail_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "WAITING_TID_TAIL", 0x122, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_next_queued_scb_addr_print;
+#else
+#define ahd_next_queued_scb_addr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "NEXT_QUEUED_SCB_ADDR", 0x124, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_scb_head_print;
+#else
+#define ahd_complete_scb_head_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "COMPLETE_SCB_HEAD", 0x128, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_scb_dmainprog_head_print;
+#else
+#define ahd_complete_scb_dmainprog_head_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "COMPLETE_SCB_DMAINPROG_HEAD", 0x12a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_dma_scb_head_print;
+#else
+#define ahd_complete_dma_scb_head_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "COMPLETE_DMA_SCB_HEAD", 0x12c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_dma_scb_tail_print;
+#else
+#define ahd_complete_dma_scb_tail_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "COMPLETE_DMA_SCB_TAIL", 0x12e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_complete_on_qfreeze_head_print;
+#else
+#define ahd_complete_on_qfreeze_head_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "COMPLETE_ON_QFREEZE_HEAD", 0x130, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qfreeze_count_print;
+#else
+#define ahd_qfreeze_count_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "QFREEZE_COUNT", 0x132, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_kernel_qfreeze_count_print;
+#else
+#define ahd_kernel_qfreeze_count_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "KERNEL_QFREEZE_COUNT", 0x134, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_saved_mode_print;
+#else
+#define ahd_saved_mode_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SAVED_MODE", 0x136, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_msg_out_print;
+#else
+#define ahd_msg_out_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "MSG_OUT", 0x137, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seq_flags_print;
+#else
+#define ahd_seq_flags_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEQ_FLAGS", 0x139, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_lastphase_print;
+#else
+#define ahd_lastphase_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LASTPHASE", 0x13c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qoutfifo_entry_valid_tag_print;
+#else
+#define ahd_qoutfifo_entry_valid_tag_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "QOUTFIFO_ENTRY_VALID_TAG", 0x13d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_kernel_tqinpos_print;
+#else
+#define ahd_kernel_tqinpos_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "KERNEL_TQINPOS", 0x13e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_qoutfifo_next_addr_print;
+#else
+#define ahd_qoutfifo_next_addr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "QOUTFIFO_NEXT_ADDR", 0x144, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_last_msg_print;
+#else
+#define ahd_last_msg_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LAST_MSG", 0x14a, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scsiseq_template_print;
+#else
+#define ahd_scsiseq_template_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCSISEQ_TEMPLATE", 0x14b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_initiator_tag_print;
+#else
+#define ahd_initiator_tag_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INITIATOR_TAG", 0x14c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_seq_flags2_print;
+#else
+#define ahd_seq_flags2_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SEQ_FLAGS2", 0x14d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_allocfifo_scbptr_print;
+#else
+#define ahd_allocfifo_scbptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "ALLOCFIFO_SCBPTR", 0x14e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalescing_timer_print;
+#else
+#define ahd_int_coalescing_timer_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INT_COALESCING_TIMER", 0x150, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalescing_maxcmds_print;
+#else
+#define ahd_int_coalescing_maxcmds_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INT_COALESCING_MAXCMDS", 0x152, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalescing_mincmds_print;
+#else
+#define ahd_int_coalescing_mincmds_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INT_COALESCING_MINCMDS", 0x153, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmds_pending_print;
+#else
+#define ahd_cmds_pending_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CMDS_PENDING", 0x154, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_int_coalescing_cmdcount_print;
+#else
+#define ahd_int_coalescing_cmdcount_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "INT_COALESCING_CMDCOUNT", 0x156, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_local_hs_mailbox_print;
+#else
+#define ahd_local_hs_mailbox_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "LOCAL_HS_MAILBOX", 0x157, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_cmdsize_table_print;
+#else
+#define ahd_cmdsize_table_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "CMDSIZE_TABLE", 0x158, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_mk_message_scb_print;
+#else
+#define ahd_mk_message_scb_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "MK_MESSAGE_SCB", 0x160, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_mk_message_scsiid_print;
+#else
+#define ahd_mk_message_scsiid_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "MK_MESSAGE_SCSIID", 0x162, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_base_print;
+#else
+#define ahd_scb_base_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_BASE", 0x180, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_residual_datacnt_print;
+#else
+#define ahd_scb_residual_datacnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_RESIDUAL_DATACNT", 0x180, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_sense_busaddr_print;
+#else
+#define ahd_scb_sense_busaddr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_SENSE_BUSADDR", 0x18c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_tag_print;
+#else
+#define ahd_scb_tag_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_TAG", 0x190, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_control_print;
+#else
+#define ahd_scb_control_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_CONTROL", 0x192, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_scsiid_print;
+#else
+#define ahd_scb_scsiid_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_SCSIID", 0x193, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_lun_print;
+#else
+#define ahd_scb_lun_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_LUN", 0x194, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_task_attribute_print;
+#else
+#define ahd_scb_task_attribute_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_TASK_ATTRIBUTE", 0x195, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_task_management_print;
+#else
+#define ahd_scb_task_management_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_TASK_MANAGEMENT", 0x197, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_dataptr_print;
+#else
+#define ahd_scb_dataptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_DATAPTR", 0x198, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_datacnt_print;
+#else
+#define ahd_scb_datacnt_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_DATACNT", 0x1a0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_sgptr_print;
+#else
+#define ahd_scb_sgptr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_SGPTR", 0x1a4, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_busaddr_print;
+#else
+#define ahd_scb_busaddr_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_BUSADDR", 0x1a8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_next2_print;
+#else
+#define ahd_scb_next2_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_NEXT2", 0x1ae, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahd_reg_print_t ahd_scb_disconnected_lists_print;
+#else
+#define ahd_scb_disconnected_lists_print(regvalue, cur_col, wrap) \
+    ahd_print_register(NULL, 0, "SCB_DISCONNECTED_LISTS", 0x1b8, regvalue, cur_col, wrap)
+#endif
+
+
+#define	MODE_PTR        		0x00
+#define		DST_MODE        	0x70
+#define		SRC_MODE        	0x07
+
+#define	INTSTAT         		0x01
+#define		INT_PEND        	0xff
+#define		HWERRINT        	0x80
+#define		BRKADRINT       	0x40
+#define		SWTMINT         	0x20
+#define		PCIINT          	0x10
+#define		SCSIINT         	0x08
+#define		SEQINT          	0x04
+#define		CMDCMPLT        	0x02
+#define		SPLTINT         	0x01
+
+#define	SEQINTCODE      		0x02
+#define		BAD_SCB_STATUS  	0x1a
+#define		SAW_HWERR       	0x19
+#define		TRACEPOINT3     	0x18
+#define		TRACEPOINT2     	0x17
+#define		TRACEPOINT1     	0x16
+#define		TRACEPOINT0     	0x15
+#define		TASKMGMT_CMD_CMPLT_OKAY	0x14
+#define		TASKMGMT_FUNC_COMPLETE	0x13
+#define		ENTERING_NONPACK	0x12
+#define		CFG4OVERRUN     	0x11
+#define		STATUS_OVERRUN  	0x10
+#define		CFG4ISTAT_INTR  	0x0f
+#define		INVALID_SEQINT  	0x0e
+#define		ILLEGAL_PHASE   	0x0d
+#define		DUMP_CARD_STATE 	0x0c
+#define		MISSED_BUSFREE  	0x0b
+#define		MKMSG_FAILED    	0x0a
+#define		DATA_OVERRUN    	0x09
+#define		BAD_STATUS      	0x08
+#define		HOST_MSG_LOOP   	0x07
+#define		PDATA_REINIT    	0x06
+#define		IGN_WIDE_RES    	0x05
+#define		NO_MATCH        	0x04
+#define		PROTO_VIOLATION 	0x03
+#define		SEND_REJECT     	0x02
+#define		BAD_PHASE       	0x01
+#define		NO_SEQINT       	0x00
+
+#define	CLRINT          		0x03
+#define		CLRHWERRINT     	0x80
+#define		CLRBRKADRINT    	0x40
+#define		CLRSWTMINT      	0x20
+#define		CLRPCIINT       	0x10
+#define		CLRSCSIINT      	0x08
+#define		CLRSEQINT       	0x04
+#define		CLRCMDINT       	0x02
+#define		CLRSPLTINT      	0x01
+
+#define	ERROR           		0x04
+#define		CIOPARERR       	0x80
+#define		CIOACCESFAIL    	0x40
+#define		MPARERR         	0x20
+#define		DPARERR         	0x10
+#define		SQPARERR        	0x08
+#define		ILLOPCODE       	0x04
+#define		DSCTMOUT        	0x02
+
+#define	CLRERR          		0x04
+#define		CLRCIOPARERR    	0x80
+#define		CLRCIOACCESFAIL 	0x40
+#define		CLRMPARERR      	0x20
+#define		CLRDPARERR      	0x10
+#define		CLRSQPARERR     	0x08
+#define		CLRILLOPCODE    	0x04
+#define		CLRDSCTMOUT     	0x02
+
+#define	HCNTRL          		0x05
+#define		SEQ_RESET       	0x80
+#define		POWRDN          	0x40
+#define		SWINT           	0x10
+#define		SWTIMER_START_B 	0x08
+#define		PAUSE           	0x04
+#define		INTEN           	0x02
+#define		CHIPRST         	0x01
+#define		CHIPRSTACK      	0x01
+
+#define	HNSCB_QOFF      		0x06
+
+#define	HESCB_QOFF      		0x08
+
+#define	HS_MAILBOX      		0x0b
+#define		HOST_TQINPOS    	0x80
+#define		ENINT_COALESCE  	0x40
+
+#define	SEQINTSTAT      		0x0c
+#define		SEQ_SWTMRTO     	0x10
+#define		SEQ_SEQINT      	0x08
+#define		SEQ_SCSIINT     	0x04
+#define		SEQ_PCIINT      	0x02
+#define		SEQ_SPLTINT     	0x01
+
+#define	CLRSEQINTSTAT   		0x0c
+#define		CLRSEQ_SWTMRTO  	0x10
+#define		CLRSEQ_SEQINT   	0x08
+#define		CLRSEQ_SCSIINT  	0x04
+#define		CLRSEQ_PCIINT   	0x02
+#define		CLRSEQ_SPLTINT  	0x01
+
+#define	SWTIMER         		0x0e
+
+#define	SNSCB_QOFF      		0x10
+
+#define	SESCB_QOFF      		0x12
+
+#define	SDSCB_QOFF      		0x14
+
+#define	QOFF_CTLSTA     		0x16
+#define		EMPTY_SCB_AVAIL 	0x80
+#define		NEW_SCB_AVAIL   	0x40
+#define		SDSCB_ROLLOVR   	0x20
+#define		HS_MAILBOX_ACT  	0x10
+#define		SCB_QSIZE       	0x0f
+#define		SCB_QSIZE_16384 	0x0c
+#define		SCB_QSIZE_8192  	0x0b
+#define		SCB_QSIZE_4096  	0x0a
+#define		SCB_QSIZE_2048  	0x09
+#define		SCB_QSIZE_1024  	0x08
+#define		SCB_QSIZE_512   	0x07
+#define		SCB_QSIZE_256   	0x06
+#define		SCB_QSIZE_128   	0x05
+#define		SCB_QSIZE_64    	0x04
+#define		SCB_QSIZE_32    	0x03
+#define		SCB_QSIZE_16    	0x02
+#define		SCB_QSIZE_8     	0x01
+#define		SCB_QSIZE_4     	0x00
+
+#define	INTCTL          		0x18
+#define		SWTMINTMASK     	0x80
+#define		SWTMINTEN       	0x40
+#define		SWTIMER_START   	0x20
+#define		AUTOCLRCMDINT   	0x10
+#define		PCIINTEN        	0x08
+#define		SCSIINTEN       	0x04
+#define		SEQINTEN        	0x02
+#define		SPLTINTEN       	0x01
+
+#define	DFCNTRL         		0x19
+#define		SCSIENWRDIS     	0x40
+#define		SCSIENACK       	0x20
+#define		DIRECTIONACK    	0x04
+#define		FIFOFLUSHACK    	0x02
+#define		DIRECTIONEN     	0x01
+
+#define	DSCOMMAND0      		0x19
+#define		CACHETHEN       	0x80
+#define		DPARCKEN        	0x40
+#define		MPARCKEN        	0x20
+#define		EXTREQLCK       	0x10
+#define		DISABLE_TWATE   	0x02
+#define		CIOPARCKEN      	0x01
+
+#define	DFSTATUS        		0x1a
+#define		PRELOAD_AVAIL   	0x80
+#define		PKT_PRELOAD_AVAIL	0x40
+#define		MREQPEND        	0x10
+#define		HDONE           	0x08
+#define		DFTHRESH        	0x04
+#define		FIFOFULL        	0x02
+#define		FIFOEMP         	0x01
+
+#define	SG_CACHE_SHADOW 		0x1b
+#define		ODD_SEG         	0x04
+#define		LAST_SEG        	0x02
+#define		LAST_SEG_DONE   	0x01
+
+#define	ARBCTL          		0x1b
+#define		RESET_HARB      	0x80
+#define		RETRY_SWEN      	0x08
+#define		USE_TIME        	0x07
+
+#define	SG_CACHE_PRE    		0x1b
+
+#define	LQIN            		0x20
+
+#define	TYPEPTR         		0x20
+
+#define	TAGPTR          		0x21
+
+#define	LUNPTR          		0x22
+
+#define	DATALENPTR      		0x23
+
+#define	STATLENPTR      		0x24
+
+#define	CMDLENPTR       		0x25
+
+#define	ATTRPTR         		0x26
+
+#define	FLAGPTR         		0x27
+
+#define	CMDPTR          		0x28
+
+#define	QNEXTPTR        		0x29
+
+#define	IDPTR           		0x2a
+
+#define	ABRTBYTEPTR     		0x2b
+
+#define	ABRTBITPTR      		0x2c
+
+#define	MAXCMDBYTES     		0x2d
+
+#define	MAXCMD2RCV      		0x2e
+
+#define	SHORTTHRESH     		0x2f
+
+#define	LUNLEN          		0x30
+#define		TLUNLEN         	0xf0
+#define		ILUNLEN         	0x0f
+
+#define	CDBLIMIT        		0x31
+
+#define	MAXCMD          		0x32
+
+#define	MAXCMDCNT       		0x33
+
+#define	LQRSVD01        		0x34
+
+#define	LQRSVD16        		0x35
+
+#define	LQRSVD17        		0x36
+
+#define	CMDRSVD0        		0x37
+
+#define	LQCTL0          		0x38
+#define		LQITARGCLT      	0xc0
+#define		LQIINITGCLT     	0x30
+#define		LQ0TARGCLT      	0x0c
+#define		LQ0INITGCLT     	0x03
+
+#define	LQCTL1          		0x38
+#define		PCI2PCI         	0x04
+#define		SINGLECMD       	0x02
+#define		ABORTPENDING    	0x01
+
+#define	SCSBIST0        		0x39
+#define		GSBISTERR       	0x40
+#define		GSBISTDONE      	0x20
+#define		GSBISTRUN       	0x10
+#define		OSBISTERR       	0x04
+#define		OSBISTDONE      	0x02
+#define		OSBISTRUN       	0x01
+
+#define	LQCTL2          		0x39
+#define		LQIRETRY        	0x80
+#define		LQICONTINUE     	0x40
+#define		LQITOIDLE       	0x20
+#define		LQIPAUSE        	0x10
+#define		LQORETRY        	0x08
+#define		LQOCONTINUE     	0x04
+#define		LQOTOIDLE       	0x02
+#define		LQOPAUSE        	0x01
+
+#define	SCSBIST1        		0x3a
+#define		NTBISTERR       	0x04
+#define		NTBISTDONE      	0x02
+#define		NTBISTRUN       	0x01
+
+#define	SCSISEQ0        		0x3a
+#define		TEMODEO         	0x80
+#define		ENSELO          	0x40
+#define		ENARBO          	0x20
+#define		FORCEBUSFREE    	0x10
+#define		SCSIRSTO        	0x01
+
+#define	SCSISEQ1        		0x3b
+
+#define	SXFRCTL0        		0x3c
+#define		DFON            	0x80
+#define		DFPEXP          	0x40
+#define		BIOSCANCELEN    	0x10
+#define		SPIOEN          	0x08
+
+#define	DLCOUNT         		0x3c
+
+#define	BUSINITID       		0x3c
+
+#define	SXFRCTL1        		0x3d
+#define		BITBUCKET       	0x80
+#define		ENSACHK         	0x40
+#define		ENSPCHK         	0x20
+#define		STIMESEL        	0x18
+#define		ENSTIMER        	0x04
+#define		ACTNEGEN        	0x02
+#define		STPWEN          	0x01
+
+#define	BUSTARGID       		0x3e
+
+#define	SXFRCTL2        		0x3e
+#define		AUTORSTDIS      	0x10
+#define		CMDDMAEN        	0x08
+#define		ASU             	0x07
+
+#define	DFFSTAT         		0x3f
+#define		CURRFIFO        	0x03
+#define		FIFO1FREE       	0x20
+#define		FIFO0FREE       	0x10
+#define		CURRFIFO_NONE   	0x03
+#define		CURRFIFO_1      	0x01
+#define		CURRFIFO_0      	0x00
+
+#define	SCSISIGO        		0x40
+#define		CDO             	0x80
+#define		IOO             	0x40
+#define		MSGO            	0x20
+#define		ATNO            	0x10
+#define		SELO            	0x08
+#define		BSYO            	0x04
+#define		REQO            	0x02
+#define		ACKO            	0x01
+
+#define	MULTARGID       		0x40
+
+#define	SCSISIGI        		0x41
+#define		ATNI            	0x10
+#define		SELI            	0x08
+#define		BSYI            	0x04
+#define		REQI            	0x02
+#define		ACKI            	0x01
+
+#define	SCSIPHASE       		0x42
+#define		STATUS_PHASE    	0x20
+#define		COMMAND_PHASE   	0x10
+#define		MSG_IN_PHASE    	0x08
+#define		MSG_OUT_PHASE   	0x04
+#define		DATA_PHASE_MASK 	0x03
+#define		DATA_IN_PHASE   	0x02
+#define		DATA_OUT_PHASE  	0x01
+
+#define	SCSIDAT0_IMG    		0x43
+
+#define	SCSIDAT         		0x44
+
+#define	SCSIBUS         		0x46
+
+#define	TARGIDIN        		0x48
+#define		CLKOUT          	0x80
+#define		TARGID          	0x0f
+
+#define	SELID           		0x49
+#define		SELID_MASK      	0xf0
+#define		ONEBIT          	0x08
+
+#define	OPTIONMODE      		0x4a
+#define		OPTIONMODE_DEFAULTS	0x02
+#define		BIOSCANCTL      	0x80
+#define		AUTOACKEN       	0x40
+#define		BIASCANCTL      	0x20
+#define		BUSFREEREV      	0x10
+#define		ENDGFORMCHK     	0x04
+#define		AUTO_MSGOUT_DE  	0x02
+
+#define	SBLKCTL         		0x4a
+#define		DIAGLEDEN       	0x80
+#define		DIAGLEDON       	0x40
+#define		ENAB40          	0x08
+#define		ENAB20          	0x04
+#define		SELWIDE         	0x02
+
+#define	CLRSINT0        		0x4b
+#define		CLRSELDO        	0x40
+#define		CLRSELDI        	0x20
+#define		CLRSELINGO      	0x10
+#define		CLRIOERR        	0x08
+#define		CLROVERRUN      	0x04
+#define		CLRSPIORDY      	0x02
+#define		CLRARBDO        	0x01
+
+#define	SSTAT0          		0x4b
+#define		TARGET          	0x80
+#define		SELDO           	0x40
+#define		SELDI           	0x20
+#define		SELINGO         	0x10
+#define		IOERR           	0x08
+#define		OVERRUN         	0x04
+#define		SPIORDY         	0x02
+#define		ARBDO           	0x01
+
+#define	SIMODE0         		0x4b
+#define		ENSELDO         	0x40
+#define		ENSELDI         	0x20
+#define		ENSELINGO       	0x10
+#define		ENIOERR         	0x08
+#define		ENOVERRUN       	0x04
+#define		ENSPIORDY       	0x02
+#define		ENARBDO         	0x01
+
+#define	CLRSINT1        		0x4c
+#define		CLRSELTIMEO     	0x80
+#define		CLRATNO         	0x40
+#define		CLRSCSIRSTI     	0x20
+#define		CLRBUSFREE      	0x08
+#define		CLRSCSIPERR     	0x04
+#define		CLRSTRB2FAST    	0x02
+#define		CLRREQINIT      	0x01
+
+#define	SSTAT1          		0x4c
+#define		SELTO           	0x80
+#define		ATNTARG         	0x40
+#define		SCSIRSTI        	0x20
+#define		PHASEMIS        	0x10
+#define		BUSFREE         	0x08
+#define		SCSIPERR        	0x04
+#define		STRB2FAST       	0x02
+#define		REQINIT         	0x01
+
+#define	SSTAT2          		0x4d
+#define		BUSFREETIME     	0xc0
+#define		NONPACKREQ      	0x20
+#define		EXP_ACTIVE      	0x10
+#define		BSYX            	0x08
+#define		WIDE_RES        	0x04
+#define		SDONE           	0x02
+#define		DMADONE         	0x01
+#define		BUSFREE_DFF1    	0xc0
+#define		BUSFREE_DFF0    	0x80
+#define		BUSFREE_LQO     	0x40
+
+#define	SIMODE2         		0x4d
+#define		ENWIDE_RES      	0x04
+#define		ENSDONE         	0x02
+#define		ENDMADONE       	0x01
+
+#define	CLRSINT2        		0x4d
+#define		CLRNONPACKREQ   	0x20
+#define		CLRWIDE_RES     	0x04
+#define		CLRSDONE        	0x02
+#define		CLRDMADONE      	0x01
+
+#define	PERRDIAG        		0x4e
+#define		HIZERO          	0x80
+#define		HIPERR          	0x40
+#define		PREVPHASE       	0x20
+#define		PARITYERR       	0x10
+#define		AIPERR          	0x08
+#define		CRCERR          	0x04
+#define		DGFORMERR       	0x02
+#define		DTERR           	0x01
+
+#define	LQISTATE        		0x4e
+
+#define	SOFFCNT         		0x4f
+
+#define	LQOSTATE        		0x4f
+
+#define	LQISTAT0        		0x50
+#define		LQIATNQAS       	0x20
+#define		LQICRCT1        	0x10
+#define		LQICRCT2        	0x08
+#define		LQIBADLQT       	0x04
+#define		LQIATNLQ        	0x02
+#define		LQIATNCMD       	0x01
+
+#define	CLRLQIINT0      		0x50
+#define		CLRLQIATNQAS    	0x20
+#define		CLRLQICRCT1     	0x10
+#define		CLRLQICRCT2     	0x08
+#define		CLRLQIBADLQT    	0x04
+#define		CLRLQIATNLQ     	0x02
+#define		CLRLQIATNCMD    	0x01
+
+#define	LQIMODE0        		0x50
+#define		ENLQIATNQASK    	0x20
+#define		ENLQICRCT1      	0x10
+#define		ENLQICRCT2      	0x08
+#define		ENLQIBADLQT     	0x04
+#define		ENLQIATNLQ      	0x02
+#define		ENLQIATNCMD     	0x01
+
+#define	LQIMODE1        		0x51
+#define		ENLQIPHASE_LQ   	0x80
+#define		ENLQIPHASE_NLQ  	0x40
+#define		ENLIQABORT      	0x20
+#define		ENLQICRCI_LQ    	0x10
+#define		ENLQICRCI_NLQ   	0x08
+#define		ENLQIBADLQI     	0x04
+#define		ENLQIOVERI_LQ   	0x02
+#define		ENLQIOVERI_NLQ  	0x01
+
+#define	LQISTAT1        		0x51
+#define		LQIPHASE_LQ     	0x80
+#define		LQIPHASE_NLQ    	0x40
+#define		LQIABORT        	0x20
+#define		LQICRCI_LQ      	0x10
+#define		LQICRCI_NLQ     	0x08
+#define		LQIBADLQI       	0x04
+#define		LQIOVERI_LQ     	0x02
+#define		LQIOVERI_NLQ    	0x01
+
+#define	CLRLQIINT1      		0x51
+#define		CLRLQIPHASE_LQ  	0x80
+#define		CLRLQIPHASE_NLQ 	0x40
+#define		CLRLIQABORT     	0x20
+#define		CLRLQICRCI_LQ   	0x10
+#define		CLRLQICRCI_NLQ  	0x08
+#define		CLRLQIBADLQI    	0x04
+#define		CLRLQIOVERI_LQ  	0x02
+#define		CLRLQIOVERI_NLQ 	0x01
+
+#define	LQISTAT2        		0x52
+#define		PACKETIZED      	0x80
+#define		LQIPHASE_OUTPKT 	0x40
+#define		LQIWORKONLQ     	0x20
+#define		LQIWAITFIFO     	0x10
+#define		LQISTOPPKT      	0x08
+#define		LQISTOPLQ       	0x04
+#define		LQISTOPCMD      	0x02
+#define		LQIGSAVAIL      	0x01
+
+#define	SSTAT3          		0x53
+#define		NTRAMPERR       	0x02
+#define		OSRAMPERR       	0x01
+
+#define	SIMODE3         		0x53
+#define		ENNTRAMPERR     	0x02
+#define		ENOSRAMPERR     	0x01
+
+#define	CLRSINT3        		0x53
+#define		CLRNTRAMPERR    	0x02
+#define		CLROSRAMPERR    	0x01
+
+#define	LQOSTAT0        		0x54
+#define		LQOTARGSCBPERR  	0x10
+#define		LQOSTOPT2       	0x08
+#define		LQOATNLQ        	0x04
+#define		LQOATNPKT       	0x02
+#define		LQOTCRC         	0x01
+
+#define	CLRLQOINT0      		0x54
+#define		CLRLQOTARGSCBPERR	0x10
+#define		CLRLQOSTOPT2    	0x08
+#define		CLRLQOATNLQ     	0x04
+#define		CLRLQOATNPKT    	0x02
+#define		CLRLQOTCRC      	0x01
+
+#define	LQOMODE0        		0x54
+#define		ENLQOTARGSCBPERR	0x10
+#define		ENLQOSTOPT2     	0x08
+#define		ENLQOATNLQ      	0x04
+#define		ENLQOATNPKT     	0x02
+#define		ENLQOTCRC       	0x01
+
+#define	LQOMODE1        		0x55
+#define		ENLQOINITSCBPERR	0x10
+#define		ENLQOSTOPI2     	0x08
+#define		ENLQOBADQAS     	0x04
+#define		ENLQOBUSFREE    	0x02
+#define		ENLQOPHACHGINPKT	0x01
+
+#define	LQOSTAT1        		0x55
+#define		LQOINITSCBPERR  	0x10
+#define		LQOSTOPI2       	0x08
+#define		LQOBADQAS       	0x04
+#define		LQOBUSFREE      	0x02
+#define		LQOPHACHGINPKT  	0x01
+
+#define	CLRLQOINT1      		0x55
+#define		CLRLQOINITSCBPERR	0x10
+#define		CLRLQOSTOPI2    	0x08
+#define		CLRLQOBADQAS    	0x04
+#define		CLRLQOBUSFREE   	0x02
+#define		CLRLQOPHACHGINPKT	0x01
+
+#define	LQOSTAT2        		0x56
+#define		LQOPKT          	0xe0
+#define		LQOWAITFIFO     	0x10
+#define		LQOPHACHGOUTPKT 	0x02
+#define		LQOSTOP0        	0x01
+
+#define	OS_SPACE_CNT    		0x56
+
+#define	SIMODE1         		0x57
+#define		ENSELTIMO       	0x80
+#define		ENATNTARG       	0x40
+#define		ENSCSIRST       	0x20
+#define		ENPHASEMIS      	0x10
+#define		ENBUSFREE       	0x08
+#define		ENSCSIPERR      	0x04
+#define		ENSTRB2FAST     	0x02
+#define		ENREQINIT       	0x01
+
+#define	GSFIFO          		0x58
+
+#define	DFFSXFRCTL      		0x5a
+#define		DFFBITBUCKET    	0x08
+#define		CLRSHCNT        	0x04
+#define		CLRCHN          	0x02
+#define		RSTCHN          	0x01
+
+#define	LQOSCSCTL       		0x5a
+#define		LQOH2A_VERSION  	0x80
+#define		LQOBUSETDLY     	0x40
+#define		LQONOHOLDLACK   	0x02
+#define		LQONOCHKOVER    	0x01
+
+#define	NEXTSCB         		0x5a
+
+#define	CLRSEQINTSRC    		0x5b
+#define		CLRCTXTDONE     	0x40
+#define		CLRSAVEPTRS     	0x20
+#define		CLRCFG4DATA     	0x10
+#define		CLRCFG4ISTAT    	0x08
+#define		CLRCFG4TSTAT    	0x04
+#define		CLRCFG4ICMD     	0x02
+#define		CLRCFG4TCMD     	0x01
+
+#define	SEQINTSRC       		0x5b
+#define		CTXTDONE        	0x40
+#define		SAVEPTRS        	0x20
+#define		CFG4DATA        	0x10
+#define		CFG4ISTAT       	0x08
+#define		CFG4TSTAT       	0x04
+#define		CFG4ICMD        	0x02
+#define		CFG4TCMD        	0x01
+
+#define	CURRSCB         		0x5c
+
+#define	SEQIMODE        		0x5c
+#define		ENCTXTDONE      	0x40
+#define		ENSAVEPTRS      	0x20
+#define		ENCFG4DATA      	0x10
+#define		ENCFG4ISTAT     	0x08
+#define		ENCFG4TSTAT     	0x04
+#define		ENCFG4ICMD      	0x02
+#define		ENCFG4TCMD      	0x01
+
+#define	MDFFSTAT        		0x5d
+#define		SHCNTNEGATIVE   	0x40
+#define		SHCNTMINUS1     	0x20
+#define		LASTSDONE       	0x10
+#define		SHVALID         	0x08
+#define		DLZERO          	0x04
+#define		DATAINFIFO      	0x02
+#define		FIFOFREE        	0x01
+
+#define	CRCCONTROL      		0x5d
+#define		CRCVALCHKEN     	0x40
+
+#define	DFFTAG          		0x5e
+
+#define	LASTSCB         		0x5e
+
+#define	SCSITEST        		0x5e
+#define		CNTRTEST        	0x08
+#define		SEL_TXPLL_DEBUG 	0x04
+
+#define	IOPDNCTL        		0x5f
+#define		DISABLE_OE      	0x80
+#define		PDN_IDIST       	0x04
+#define		PDN_DIFFSENSE   	0x01
+
+#define	SHADDR          		0x60
+
+#define	NEGOADDR        		0x60
+
+#define	DGRPCRCI        		0x60
+
+#define	NEGPERIOD       		0x61
+
+#define	PACKCRCI        		0x62
+
+#define	NEGOFFSET       		0x62
+
+#define	NEGPPROPTS      		0x63
+#define		PPROPT_PACE     	0x08
+#define		PPROPT_QAS      	0x04
+#define		PPROPT_DT       	0x02
+#define		PPROPT_IUT      	0x01
+
+#define	NEGCONOPTS      		0x64
+#define		ENSNAPSHOT      	0x40
+#define		RTI_WRTDIS      	0x20
+#define		RTI_OVRDTRN     	0x10
+#define		ENSLOWCRC       	0x08
+#define		ENAUTOATNI      	0x04
+#define		ENAUTOATNO      	0x02
+#define		WIDEXFER        	0x01
+
+#define	ANNEXCOL        		0x65
+
+#define	ANNEXDAT        		0x66
+
+#define	SCSCHKN         		0x66
+#define		BIDICHKDIS      	0x80
+#define		STSELSKIDDIS    	0x40
+#define		CURRFIFODEF     	0x20
+#define		WIDERESEN       	0x10
+#define		SDONEMSKDIS     	0x08
+#define		DFFACTCLR       	0x04
+#define		SHVALIDSTDIS    	0x02
+#define		LSTSGCLRDIS     	0x01
+
+#define	IOWNID          		0x67
+
+#define	PLL960CTL0      		0x68
+
+#define	SHCNT           		0x68
+
+#define	TOWNID          		0x69
+
+#define	PLL960CTL1      		0x69
+
+#define	PLL960CNT0      		0x6a
+
+#define	XSIG            		0x6a
+
+#define	SELOID          		0x6b
+
+#define	PLL400CTL0      		0x6c
+#define		PLL_VCOSEL      	0x80
+#define		PLL_PWDN        	0x40
+#define		PLL_NS          	0x30
+#define		PLL_ENLUD       	0x08
+#define		PLL_ENLPF       	0x04
+#define		PLL_DLPF        	0x02
+#define		PLL_ENFBM       	0x01
+
+#define	FAIRNESS        		0x6c
+
+#define	PLL400CTL1      		0x6d
+#define		PLL_CNTEN       	0x80
+#define		PLL_CNTCLR      	0x40
+#define		PLL_RST         	0x01
+
+#define	UNFAIRNESS      		0x6e
+
+#define	PLL400CNT0      		0x6e
+
+#define	HADDR           		0x70
+
+#define	PLLDELAY        		0x70
+#define		SPLIT_DROP_REQ  	0x80
+
+#define	HODMAADR        		0x70
+
+#define	HODMACNT        		0x78
+
+#define	HCNT            		0x78
+
+#define	HODMAEN         		0x7a
+
+#define	SCBHADDR        		0x7c
+
+#define	SGHADDR         		0x7c
+
+#define	SCBHCNT         		0x84
+
+#define	SGHCNT          		0x84
+
+#define	DFF_THRSH       		0x88
+#define		WR_DFTHRSH      	0x70
+#define		RD_DFTHRSH      	0x07
+#define		WR_DFTHRSH_MAX  	0x70
+#define		WR_DFTHRSH_90   	0x60
+#define		WR_DFTHRSH_85   	0x50
+#define		WR_DFTHRSH_75   	0x40
+#define		WR_DFTHRSH_63   	0x30
+#define		WR_DFTHRSH_50   	0x20
+#define		WR_DFTHRSH_25   	0x10
+#define		RD_DFTHRSH_MAX  	0x07
+#define		RD_DFTHRSH_90   	0x06
+#define		RD_DFTHRSH_85   	0x05
+#define		RD_DFTHRSH_75   	0x04
+#define		RD_DFTHRSH_63   	0x03
+#define		RD_DFTHRSH_50   	0x02
+#define		RD_DFTHRSH_25   	0x01
+#define		RD_DFTHRSH_MIN  	0x00
+#define		WR_DFTHRSH_MIN  	0x00
+
+#define	ROMADDR         		0x8a
+
+#define	ROMCNTRL        		0x8d
+#define		ROMOP           	0xe0
+#define		ROMSPD          	0x18
+#define		REPEAT          	0x02
+#define		RDY             	0x01
+
+#define	ROMDATA         		0x8e
+
+#define	CMCRXMSG0       		0x90
+
+#define	ROENABLE        		0x90
+#define		MSIROEN         	0x20
+#define		OVLYROEN        	0x10
+#define		CMCROEN         	0x08
+#define		SGROEN          	0x04
+#define		DCH1ROEN        	0x02
+#define		DCH0ROEN        	0x01
+
+#define	OVLYRXMSG0      		0x90
+
+#define	DCHRXMSG0       		0x90
+
+#define	OVLYRXMSG1      		0x91
+
+#define	NSENABLE        		0x91
+#define		MSINSEN         	0x20
+#define		OVLYNSEN        	0x10
+#define		CMCNSEN         	0x08
+#define		SGNSEN          	0x04
+#define		DCH1NSEN        	0x02
+#define		DCH0NSEN        	0x01
+
+#define	CMCRXMSG1       		0x91
+
+#define	DCHRXMSG1       		0x91
+
+#define	DCHRXMSG2       		0x92
+
+#define	CMCRXMSG2       		0x92
+
+#define	OST             		0x92
+
+#define	OVLYRXMSG2      		0x92
+
+#define	DCHRXMSG3       		0x93
+
+#define	OVLYRXMSG3      		0x93
+
+#define	CMCRXMSG3       		0x93
+
+#define	PCIXCTL         		0x93
+#define		SERRPULSE       	0x80
+#define		UNEXPSCIEN      	0x20
+#define		SPLTSMADIS      	0x10
+#define		SPLTSTADIS      	0x08
+#define		SRSPDPEEN       	0x04
+#define		TSCSERREN       	0x02
+#define		CMPABCDIS       	0x01
+
+#define	OVLYSEQBCNT     		0x94
+
+#define	DCHSEQBCNT      		0x94
+
+#define	CMCSEQBCNT      		0x94
+
+#define	CMCSPLTSTAT0    		0x96
+
+#define	DCHSPLTSTAT0    		0x96
+
+#define	OVLYSPLTSTAT0   		0x96
+
+#define	CMCSPLTSTAT1    		0x97
+
+#define	OVLYSPLTSTAT1   		0x97
+
+#define	DCHSPLTSTAT1    		0x97
+
+#define	SGRXMSG0        		0x98
+#define		CDNUM           	0xf8
+#define		CFNUM           	0x07
+
+#define	SLVSPLTOUTADR0  		0x98
+#define		LOWER_ADDR      	0x7f
+
+#define	SGRXMSG1        		0x99
+#define		CBNUM           	0xff
+
+#define	SLVSPLTOUTADR1  		0x99
+#define		REQ_DNUM        	0xf8
+#define		REQ_FNUM        	0x07
+
+#define	SGRXMSG2        		0x9a
+#define		MINDEX          	0xff
+
+#define	SLVSPLTOUTADR2  		0x9a
+#define		REQ_BNUM        	0xff
+
+#define	SGRXMSG3        		0x9b
+#define		MCLASS          	0x0f
+
+#define	SLVSPLTOUTADR3  		0x9b
+#define		TAG_NUM         	0x1f
+#define		RLXORD          	0x10
+
+#define	SGSEQBCNT       		0x9c
+
+#define	SLVSPLTOUTATTR0 		0x9c
+#define		LOWER_BCNT      	0xff
+
+#define	SLVSPLTOUTATTR1 		0x9d
+#define		CMPLT_DNUM      	0xf8
+#define		CMPLT_FNUM      	0x07
+
+#define	SLVSPLTOUTATTR2 		0x9e
+#define		CMPLT_BNUM      	0xff
+
+#define	SGSPLTSTAT0     		0x9e
+#define		STAETERM        	0x80
+#define		SCBCERR         	0x40
+#define		SCADERR         	0x20
+#define		SCDATBUCKET     	0x10
+#define		CNTNOTCMPLT     	0x08
+#define		RXOVRUN         	0x04
+#define		RXSCEMSG        	0x02
+#define		RXSPLTRSP       	0x01
+
+#define	SGSPLTSTAT1     		0x9f
+#define		RXDATABUCKET    	0x01
+
+#define	SFUNCT          		0x9f
+#define		TEST_GROUP      	0xf0
+#define		TEST_NUM        	0x0f
+
+#define	DF0PCISTAT      		0xa0
+
+#define	REG0            		0xa0
+
+#define	DF1PCISTAT      		0xa1
+
+#define	SGPCISTAT       		0xa2
+
+#define	REG1            		0xa2
+
+#define	CMCPCISTAT      		0xa3
+
+#define	OVLYPCISTAT     		0xa4
+#define		SCAAPERR        	0x08
+#define		RDPERR          	0x04
+
+#define	REG_ISR         		0xa4
+
+#define	SG_STATE        		0xa6
+#define		FETCH_INPROG    	0x04
+#define		LOADING_NEEDED  	0x02
+#define		SEGS_AVAIL      	0x01
+
+#define	MSIPCISTAT      		0xa6
+#define		RMA             	0x20
+#define		RTA             	0x10
+#define		CLRPENDMSI      	0x08
+#define		DPR             	0x01
+
+#define	TARGPCISTAT     		0xa7
+#define		DPE             	0x80
+#define		SSE             	0x40
+#define		STA             	0x08
+#define		TWATERR         	0x02
+
+#define	DATA_COUNT_ODD  		0xa7
+
+#define	SCBPTR          		0xa8
+
+#define	CCSCBACNT       		0xab
+
+#define	SCBAUTOPTR      		0xab
+#define		AUSCBPTR_EN     	0x80
+#define		SCBPTR_ADDR     	0x38
+#define		SCBPTR_OFF      	0x07
+
+#define	CCSGADDR        		0xac
+
+#define	CCSCBADR_BK     		0xac
+
+#define	CCSCBADDR       		0xac
+
+#define	CMC_RAMBIST     		0xad
+#define		SG_ELEMENT_SIZE 	0x80
+#define		SCBRAMBIST_FAIL 	0x40
+#define		SG_BIST_FAIL    	0x20
+#define		SG_BIST_EN      	0x10
+#define		CMC_BUFFER_BIST_FAIL	0x02
+#define		CMC_BUFFER_BIST_EN	0x01
+
+#define	CCSCBCTL        		0xad
+#define		CCSCBDONE       	0x80
+#define		ARRDONE         	0x40
+#define		CCARREN         	0x10
+#define		CCSCBEN         	0x08
+#define		CCSCBDIR        	0x04
+#define		CCSCBRESET      	0x01
+
+#define	CCSGCTL         		0xad
+#define		CCSGEN          	0x0c
+#define		CCSGDONE        	0x80
+#define		SG_CACHE_AVAIL  	0x10
+#define		CCSGENACK       	0x08
+#define		SG_FETCH_REQ    	0x02
+#define		CCSGRESET       	0x01
+
+#define	CCSGRAM         		0xb0
+
+#define	FLEXADR         		0xb0
+
+#define	CCSCBRAM        		0xb0
+
+#define	FLEXCNT         		0xb3
+
+#define	FLEXDMASTAT     		0xb5
+#define		FLEXDMAERR      	0x02
+#define		FLEXDMADONE     	0x01
+
+#define	FLEXDATA        		0xb6
+
+#define	BRDDAT          		0xb8
+
+#define	BRDCTL          		0xb9
+#define		FLXARBACK       	0x80
+#define		FLXARBREQ       	0x40
+#define		BRDADDR         	0x38
+#define		BRDEN           	0x04
+#define		BRDRW           	0x02
+#define		BRDSTB          	0x01
+
+#define	SEEADR          		0xba
+
+#define	SEEDAT          		0xbc
+
+#define	SEECTL          		0xbe
+#define		SEEOP_WALL      	0x40
+#define		SEEOP_EWEN      	0x40
+#define		SEEOP_EWDS      	0x40
+#define		SEEOPCODE       	0x70
+#define		SEERST          	0x02
+#define		SEESTART        	0x01
+#define		SEEOP_ERASE     	0x70
+#define		SEEOP_READ      	0x60
+#define		SEEOP_WRITE     	0x50
+#define		SEEOP_ERAL      	0x40
+
+#define	SEESTAT         		0xbe
+#define		INIT_DONE       	0x80
+#define		LDALTID_L       	0x08
+#define		SEEARBACK       	0x04
+#define		SEEBUSY         	0x02
+
+#define	SCBCNT          		0xbf
+
+#define	DFWADDR         		0xc0
+
+#define	DSPFLTRCTL      		0xc0
+#define		FLTRDISABLE     	0x20
+#define		EDGESENSE       	0x10
+#define		DSPFCNTSEL      	0x0f
+
+#define	DSPDATACTL      		0xc1
+#define		BYPASSENAB      	0x80
+#define		DESQDIS         	0x10
+#define		RCVROFFSTDIS    	0x04
+#define		XMITOFFSTDIS    	0x02
+
+#define	DFRADDR         		0xc2
+
+#define	DSPREQCTL       		0xc2
+#define		MANREQCTL       	0xc0
+#define		MANREQDLY       	0x3f
+
+#define	DSPACKCTL       		0xc3
+#define		MANACKCTL       	0xc0
+#define		MANACKDLY       	0x3f
+
+#define	DFDAT           		0xc4
+
+#define	DSPSELECT       		0xc4
+#define		AUTOINCEN       	0x80
+#define		DSPSEL          	0x1f
+
+#define	WRTBIASCTL      		0xc5
+#define		AUTOXBCDIS      	0x80
+#define		XMITMANVAL      	0x3f
+
+#define	RCVRBIOSCTL     		0xc6
+#define		AUTORBCDIS      	0x80
+#define		RCVRMANVAL      	0x3f
+
+#define	WRTBIASCALC     		0xc7
+
+#define	RCVRBIASCALC    		0xc8
+
+#define	DFPTRS          		0xc8
+
+#define	SKEWCALC        		0xc9
+
+#define	DFBKPTR         		0xc9
+
+#define	DFDBCTL         		0xcb
+#define		DFF_CIO_WR_RDY  	0x20
+#define		DFF_CIO_RD_RDY  	0x10
+#define		DFF_DIR_ERR     	0x08
+#define		DFF_RAMBIST_FAIL	0x04
+#define		DFF_RAMBIST_DONE	0x02
+#define		DFF_RAMBIST_EN  	0x01
+
+#define	DFSCNT          		0xcc
+
+#define	DFBCNT          		0xce
+
+#define	OVLYADDR        		0xd4
+
+#define	SEQCTL0         		0xd6
+#define		PERRORDIS       	0x80
+#define		PAUSEDIS        	0x40
+#define		FAILDIS         	0x20
+#define		FASTMODE        	0x10
+#define		BRKADRINTEN     	0x08
+#define		STEP            	0x04
+#define		SEQRESET        	0x02
+#define		LOADRAM         	0x01
+
+#define	SEQCTL1         		0xd7
+#define		OVRLAY_DATA_CHK 	0x08
+#define		RAMBIST_DONE    	0x04
+#define		RAMBIST_FAIL    	0x02
+#define		RAMBIST_EN      	0x01
+
+#define	FLAGS           		0xd8
+#define		ZERO            	0x02
+#define		CARRY           	0x01
+
+#define	SEQINTCTL       		0xd9
+#define		INTVEC1DSL      	0x80
+#define		INT1_CONTEXT    	0x20
+#define		SCS_SEQ_INT1M1  	0x10
+#define		SCS_SEQ_INT1M0  	0x08
+#define		INTMASK2        	0x04
+#define		INTMASK1        	0x02
+#define		IRET            	0x01
+
+#define	SEQRAM          		0xda
+
+#define	PRGMCNT         		0xde
+
+#define	ACCUM           		0xe0
+
+#define	SINDEX          		0xe2
+
+#define	DINDEX          		0xe4
+
+#define	BRKADDR0        		0xe6
+
+#define	BRKADDR1        		0xe6
+#define		BRKDIS          	0x80
+
+#define	ALLONES         		0xe8
+
+#define	ALLZEROS        		0xea
+
+#define	NONE            		0xea
+
+#define	SINDIR          		0xec
+
+#define	DINDIR          		0xed
+
+#define	FUNCTION1       		0xf0
+
+#define	STACK           		0xf2
+
+#define	INTVEC1_ADDR    		0xf4
+
+#define	CURADDR         		0xf4
+
+#define	LASTADDR        		0xf6
+
+#define	INTVEC2_ADDR    		0xf6
+
+#define	LONGJMP_ADDR    		0xf8
+
+#define	ACCUM_SAVE      		0xfa
+
+#define	WAITING_SCB_TAILS		0x100
+
+#define	AHD_PCI_CONFIG_BASE		0x100
+
+#define	SRAM_BASE       		0x100
+
+#define	WAITING_TID_HEAD		0x120
+
+#define	WAITING_TID_TAIL		0x122
+
+#define	NEXT_QUEUED_SCB_ADDR		0x124
+
+#define	COMPLETE_SCB_HEAD		0x128
+
+#define	COMPLETE_SCB_DMAINPROG_HEAD		0x12a
+
+#define	COMPLETE_DMA_SCB_HEAD		0x12c
+
+#define	COMPLETE_DMA_SCB_TAIL		0x12e
+
+#define	COMPLETE_ON_QFREEZE_HEAD		0x130
+
+#define	QFREEZE_COUNT   		0x132
+
+#define	KERNEL_QFREEZE_COUNT		0x134
+
+#define	SAVED_MODE      		0x136
+
+#define	MSG_OUT         		0x137
+
+#define	DMAPARAMS       		0x138
+#define		PRELOADEN       	0x80
+#define		WIDEODD         	0x40
+#define		SCSIEN          	0x20
+#define		SDMAEN          	0x10
+#define		SDMAENACK       	0x10
+#define		HDMAEN          	0x08
+#define		HDMAENACK       	0x08
+#define		DIRECTION       	0x04
+#define		FIFOFLUSH       	0x02
+#define		FIFORESET       	0x01
+
+#define	SEQ_FLAGS       		0x139
+#define		NOT_IDENTIFIED  	0x80
+#define		NO_CDB_SENT     	0x40
+#define		TARGET_CMD_IS_TAGGED	0x40
+#define		DPHASE          	0x20
+#define		TARG_CMD_PENDING	0x10
+#define		CMDPHASE_PENDING	0x08
+#define		DPHASE_PENDING  	0x04
+#define		SPHASE_PENDING  	0x02
+#define		NO_DISCONNECT   	0x01
+
+#define	SAVED_SCSIID    		0x13a
+
+#define	SAVED_LUN       		0x13b
+
+#define	LASTPHASE       		0x13c
+#define		PHASE_MASK      	0xe0
+#define		CDI             	0x80
+#define		IOI             	0x40
+#define		MSGI            	0x20
+#define		P_BUSFREE       	0x01
+#define		P_MESGIN        	0xe0
+#define		P_STATUS        	0xc0
+#define		P_MESGOUT       	0xa0
+#define		P_COMMAND       	0x80
+#define		P_DATAIN_DT     	0x60
+#define		P_DATAIN        	0x40
+#define		P_DATAOUT_DT    	0x20
+#define		P_DATAOUT       	0x00
+
+#define	QOUTFIFO_ENTRY_VALID_TAG		0x13d
+
+#define	KERNEL_TQINPOS  		0x13e
+
+#define	TQINPOS         		0x13f
+
+#define	SHARED_DATA_ADDR		0x140
+
+#define	QOUTFIFO_NEXT_ADDR		0x144
+
+#define	ARG_1           		0x148
+#define	RETURN_1        		0x148
+#define		SEND_MSG        	0x80
+#define		SEND_SENSE      	0x40
+#define		SEND_REJ        	0x20
+#define		MSGOUT_PHASEMIS 	0x10
+#define		EXIT_MSG_LOOP   	0x08
+#define		CONT_MSG_LOOP_WRITE	0x04
+#define		CONT_MSG_LOOP_READ	0x03
+#define		CONT_MSG_LOOP_TARG	0x02
+
+#define	ARG_2           		0x149
+#define	RETURN_2        		0x149
+
+#define	LAST_MSG        		0x14a
+
+#define	SCSISEQ_TEMPLATE		0x14b
+#define		MANUALCTL       	0x40
+#define		ENSELI          	0x20
+#define		ENRSELI         	0x10
+#define		MANUALP         	0x0c
+#define		ENAUTOATNP      	0x02
+#define		ALTSTIM         	0x01
+
+#define	INITIATOR_TAG   		0x14c
+
+#define	SEQ_FLAGS2      		0x14d
+#define		SELECTOUT_QFROZEN	0x04
+#define		TARGET_MSG_PENDING	0x02
+#define		PENDING_MK_MESSAGE	0x01
+
+#define	ALLOCFIFO_SCBPTR		0x14e
+
+#define	INT_COALESCING_TIMER		0x150
+
+#define	INT_COALESCING_MAXCMDS		0x152
+
+#define	INT_COALESCING_MINCMDS		0x153
+
+#define	CMDS_PENDING    		0x154
+
+#define	INT_COALESCING_CMDCOUNT		0x156
+
+#define	LOCAL_HS_MAILBOX		0x157
+
+#define	CMDSIZE_TABLE   		0x158
+
+#define	MK_MESSAGE_SCB  		0x160
+
+#define	MK_MESSAGE_SCSIID		0x162
+
+#define	SCB_BASE        		0x180
+
+#define	SCB_RESIDUAL_DATACNT		0x180
+#define	SCB_CDB_STORE   		0x180
+#define	SCB_HOST_CDB_PTR		0x180
+
+#define	SCB_RESIDUAL_SGPTR		0x184
+#define		SG_ADDR_MASK    	0xf8
+#define		SG_OVERRUN_RESID	0x02
+
+#define	SCB_SCSI_STATUS 		0x188
+#define	SCB_HOST_CDB_LEN		0x188
+
+#define	SCB_TARGET_PHASES		0x189
+
+#define	SCB_TARGET_DATA_DIR		0x18a
+
+#define	SCB_TARGET_ITAG 		0x18b
+
+#define	SCB_SENSE_BUSADDR		0x18c
+#define	SCB_NEXT_COMPLETE		0x18c
+
+#define	SCB_TAG         		0x190
+#define	SCB_FIFO_USE_COUNT		0x190
+
+#define	SCB_CONTROL     		0x192
+#define		TARGET_SCB      	0x80
+#define		DISCENB         	0x40
+#define		TAG_ENB         	0x20
+#define		MK_MESSAGE      	0x10
+#define		STATUS_RCVD     	0x08
+#define		DISCONNECTED    	0x04
+#define		SCB_TAG_TYPE    	0x03
+
+#define	SCB_SCSIID      		0x193
+#define		TID             	0xf0
+#define		OID             	0x0f
+
+#define	SCB_LUN         		0x194
+#define		LID             	0xff
+
+#define	SCB_TASK_ATTRIBUTE		0x195
+#define		SCB_XFERLEN_ODD 	0x01
+
+#define	SCB_CDB_LEN     		0x196
+#define		SCB_CDB_LEN_PTR 	0x80
+
+#define	SCB_TASK_MANAGEMENT		0x197
+
+#define	SCB_DATAPTR     		0x198
+
+#define	SCB_DATACNT     		0x1a0
+#define		SG_LAST_SEG     	0x80
+#define		SG_HIGH_ADDR_BITS	0x7f
+
+#define	SCB_SGPTR       		0x1a4
+#define		SG_STATUS_VALID 	0x04
+#define		SG_FULL_RESID   	0x02
+#define		SG_LIST_NULL    	0x01
+
+#define	SCB_BUSADDR     		0x1a8
+
+#define	SCB_NEXT        		0x1ac
+#define	SCB_NEXT_SCB_BUSADDR		0x1ac
+
+#define	SCB_NEXT2       		0x1ae
+
+#define	SCB_SPARE       		0x1b0
+#define	SCB_PKT_LUN     		0x1b0
+
+#define	SCB_DISCONNECTED_LISTS		0x1b8
+
+
+#define	AHD_TIMER_MAX_US	0x18ffe7
+#define	AHD_TIMER_MAX_TICKS	0xffff
+#define	AHD_SENSE_BUFSIZE	0x100
+#define	BUS_8_BIT	0x00
+#define	TARGET_CMD_CMPLT	0xfe
+#define	SEEOP_WRAL_ADDR	0x40
+#define	AHD_AMPLITUDE_DEF	0x07
+#define	AHD_PRECOMP_CUTBACK_37	0x07
+#define	AHD_PRECOMP_SHIFT	0x00
+#define	AHD_ANNEXCOL_PRECOMP_SLEW	0x04
+#define	AHD_TIMER_US_PER_TICK	0x19
+#define	SCB_TRANSFER_SIZE_FULL_LUN	0x38
+#define	STATUS_QUEUE_FULL	0x28
+#define	STATUS_BUSY	0x08
+#define	MAX_OFFSET_NON_PACED	0x7f
+#define	MAX_OFFSET_PACED	0xfe
+#define	BUS_32_BIT	0x02
+#define	CCSGADDR_MAX	0x80
+#define	TID_SHIFT	0x04
+#define	MK_MESSAGE_BIT_OFFSET	0x04
+#define	WRTBIASCTL_HP_DEFAULT	0x00
+#define	SEEOP_EWDS_ADDR	0x00
+#define	AHD_AMPLITUDE_SHIFT	0x00
+#define	AHD_AMPLITUDE_MASK	0x07
+#define	AHD_ANNEXCOL_AMPLITUDE	0x06
+#define	AHD_SLEWRATE_DEF_REVA	0x08
+#define	AHD_SLEWRATE_SHIFT	0x03
+#define	AHD_SLEWRATE_MASK	0x78
+#define	AHD_PRECOMP_CUTBACK_29	0x06
+#define	AHD_NUM_PER_DEV_ANNEXCOLS	0x04
+#define	B_CURRFIFO_0	0x02
+#define	LUNLEN_SINGLE_LEVEL_LUN	0x0f
+#define	NVRAM_SCB_OFFSET	0x2c
+#define	STATUS_PKT_SENSE	0xff
+#define	CMD_GROUP_CODE_SHIFT	0x05
+#define	MAX_OFFSET_PACED_BUG	0x7f
+#define	STIMESEL_BUG_ADJ	0x08
+#define	STIMESEL_MIN	0x18
+#define	STIMESEL_SHIFT	0x03
+#define	CCSGRAM_MAXSEGS	0x10
+#define	INVALID_ADDR	0x80
+#define	SEEOP_ERAL_ADDR	0x80
+#define	AHD_SLEWRATE_DEF_REVB	0x08
+#define	AHD_PRECOMP_CUTBACK_17	0x04
+#define	AHD_PRECOMP_MASK	0x07
+#define	SRC_MODE_SHIFT	0x00
+#define	PKT_OVERRUN_BUFSIZE	0x200
+#define	SCB_TRANSFER_SIZE_1BYTE_LUN	0x30
+#define	TARGET_DATA_IN	0x01
+#define	HOST_MSG	0xff
+#define	MAX_OFFSET	0xfe
+#define	BUS_16_BIT	0x01
+#define	CCSCBADDR_MAX	0x80
+#define	NUMDSPS 	0x14
+#define	SEEOP_EWEN_ADDR	0xc0
+#define	AHD_ANNEXCOL_PER_DEV0	0x04
+#define	DST_MODE_SHIFT	0x04
+
+
+/* Downloaded Constant Definitions */
+#define	CACHELINE_MASK	0x07
+#define	SCB_TRANSFER_SIZE	0x06
+#define	PKT_OVERRUN_BUFOFFSET	0x05
+#define	SG_SIZEOF	0x04
+#define	SG_PREFETCH_ADDR_MASK	0x03
+#define	SG_PREFETCH_ALIGN_MASK	0x02
+#define	SG_PREFETCH_CNT_LIMIT	0x01
+#define	SG_PREFETCH_CNT	0x00
+#define	DOWNLOAD_CONST_COUNT	0x08
+
+
+/* Exported Labels */
+#define	LABEL_seq_isr 	0x28f
+#define	LABEL_timer_isr	0x28b
diff --git a/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped b/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
new file mode 100644
index 0000000..f5ea715
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped
@@ -0,0 +1,745 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *		 from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#120 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#77 $
+ */
+
+#include "aic79xx_osm.h"
+
+static const ahd_reg_parse_entry_t INTSTAT_parse_table[] = {
+	{ "SPLTINT",		0x01, 0x01 },
+	{ "CMDCMPLT",		0x02, 0x02 },
+	{ "SEQINT",		0x04, 0x04 },
+	{ "SCSIINT",		0x08, 0x08 },
+	{ "PCIINT",		0x10, 0x10 },
+	{ "SWTMINT",		0x20, 0x20 },
+	{ "BRKADRINT",		0x40, 0x40 },
+	{ "HWERRINT",		0x80, 0x80 },
+	{ "INT_PEND",		0xff, 0xff }
+};
+
+int
+ahd_intstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(INTSTAT_parse_table, 9, "INTSTAT",
+	    0x01, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t HS_MAILBOX_parse_table[] = {
+	{ "ENINT_COALESCE",	0x40, 0x40 },
+	{ "HOST_TQINPOS",	0x80, 0x80 }
+};
+
+int
+ahd_hs_mailbox_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(HS_MAILBOX_parse_table, 2, "HS_MAILBOX",
+	    0x0b, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SEQINTSTAT_parse_table[] = {
+	{ "SEQ_SPLTINT",	0x01, 0x01 },
+	{ "SEQ_PCIINT",		0x02, 0x02 },
+	{ "SEQ_SCSIINT",	0x04, 0x04 },
+	{ "SEQ_SEQINT",		0x08, 0x08 },
+	{ "SEQ_SWTMRTO",	0x10, 0x10 }
+};
+
+int
+ahd_seqintstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SEQINTSTAT_parse_table, 5, "SEQINTSTAT",
+	    0x0c, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t INTCTL_parse_table[] = {
+	{ "SPLTINTEN",		0x01, 0x01 },
+	{ "SEQINTEN",		0x02, 0x02 },
+	{ "SCSIINTEN",		0x04, 0x04 },
+	{ "PCIINTEN",		0x08, 0x08 },
+	{ "AUTOCLRCMDINT",	0x10, 0x10 },
+	{ "SWTIMER_START",	0x20, 0x20 },
+	{ "SWTMINTEN",		0x40, 0x40 },
+	{ "SWTMINTMASK",	0x80, 0x80 }
+};
+
+int
+ahd_intctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(INTCTL_parse_table, 8, "INTCTL",
+	    0x18, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t DFCNTRL_parse_table[] = {
+	{ "DIRECTIONEN",	0x01, 0x01 },
+	{ "FIFOFLUSH",		0x02, 0x02 },
+	{ "FIFOFLUSHACK",	0x02, 0x02 },
+	{ "DIRECTION",		0x04, 0x04 },
+	{ "DIRECTIONACK",	0x04, 0x04 },
+	{ "HDMAEN",		0x08, 0x08 },
+	{ "HDMAENACK",		0x08, 0x08 },
+	{ "SCSIEN",		0x20, 0x20 },
+	{ "SCSIENACK",		0x20, 0x20 },
+	{ "SCSIENWRDIS",	0x40, 0x40 },
+	{ "PRELOADEN",		0x80, 0x80 }
+};
+
+int
+ahd_dfcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(DFCNTRL_parse_table, 11, "DFCNTRL",
+	    0x19, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t DFSTATUS_parse_table[] = {
+	{ "FIFOEMP",		0x01, 0x01 },
+	{ "FIFOFULL",		0x02, 0x02 },
+	{ "DFTHRESH",		0x04, 0x04 },
+	{ "HDONE",		0x08, 0x08 },
+	{ "MREQPEND",		0x10, 0x10 },
+	{ "PKT_PRELOAD_AVAIL",	0x40, 0x40 },
+	{ "PRELOAD_AVAIL",	0x80, 0x80 }
+};
+
+int
+ahd_dfstatus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(DFSTATUS_parse_table, 7, "DFSTATUS",
+	    0x1a, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SG_CACHE_SHADOW_parse_table[] = {
+	{ "LAST_SEG_DONE",	0x01, 0x01 },
+	{ "LAST_SEG",		0x02, 0x02 },
+	{ "ODD_SEG",		0x04, 0x04 },
+	{ "SG_ADDR_MASK",	0xf8, 0xf8 }
+};
+
+int
+ahd_sg_cache_shadow_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SG_CACHE_SHADOW_parse_table, 4, "SG_CACHE_SHADOW",
+	    0x1b, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SCSISEQ0_parse_table[] = {
+	{ "SCSIRSTO",		0x01, 0x01 },
+	{ "FORCEBUSFREE",	0x10, 0x10 },
+	{ "ENARBO",		0x20, 0x20 },
+	{ "ENSELO",		0x40, 0x40 },
+	{ "TEMODEO",		0x80, 0x80 }
+};
+
+int
+ahd_scsiseq0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SCSISEQ0_parse_table, 5, "SCSISEQ0",
+	    0x3a, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SCSISEQ1_parse_table[] = {
+	{ "ALTSTIM",		0x01, 0x01 },
+	{ "ENAUTOATNP",		0x02, 0x02 },
+	{ "MANUALP",		0x0c, 0x0c },
+	{ "ENRSELI",		0x10, 0x10 },
+	{ "ENSELI",		0x20, 0x20 },
+	{ "MANUALCTL",		0x40, 0x40 }
+};
+
+int
+ahd_scsiseq1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SCSISEQ1_parse_table, 6, "SCSISEQ1",
+	    0x3b, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t DFFSTAT_parse_table[] = {
+	{ "CURRFIFO_0",		0x00, 0x03 },
+	{ "CURRFIFO_1",		0x01, 0x03 },
+	{ "CURRFIFO_NONE",	0x03, 0x03 },
+	{ "FIFO0FREE",		0x10, 0x10 },
+	{ "FIFO1FREE",		0x20, 0x20 },
+	{ "CURRFIFO",		0x03, 0x03 }
+};
+
+int
+ahd_dffstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(DFFSTAT_parse_table, 6, "DFFSTAT",
+	    0x3f, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SCSISIGI_parse_table[] = {
+	{ "P_DATAOUT",		0x00, 0xe0 },
+	{ "P_DATAOUT_DT",	0x20, 0xe0 },
+	{ "P_DATAIN",		0x40, 0xe0 },
+	{ "P_DATAIN_DT",	0x60, 0xe0 },
+	{ "P_COMMAND",		0x80, 0xe0 },
+	{ "P_MESGOUT",		0xa0, 0xe0 },
+	{ "P_STATUS",		0xc0, 0xe0 },
+	{ "P_MESGIN",		0xe0, 0xe0 },
+	{ "ACKI",		0x01, 0x01 },
+	{ "REQI",		0x02, 0x02 },
+	{ "BSYI",		0x04, 0x04 },
+	{ "SELI",		0x08, 0x08 },
+	{ "ATNI",		0x10, 0x10 },
+	{ "MSGI",		0x20, 0x20 },
+	{ "IOI",		0x40, 0x40 },
+	{ "CDI",		0x80, 0x80 },
+	{ "PHASE_MASK",		0xe0, 0xe0 }
+};
+
+int
+ahd_scsisigi_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SCSISIGI_parse_table, 17, "SCSISIGI",
+	    0x41, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SCSIPHASE_parse_table[] = {
+	{ "DATA_OUT_PHASE",	0x01, 0x03 },
+	{ "DATA_IN_PHASE",	0x02, 0x03 },
+	{ "DATA_PHASE_MASK",	0x03, 0x03 },
+	{ "MSG_OUT_PHASE",	0x04, 0x04 },
+	{ "MSG_IN_PHASE",	0x08, 0x08 },
+	{ "COMMAND_PHASE",	0x10, 0x10 },
+	{ "STATUS_PHASE",	0x20, 0x20 }
+};
+
+int
+ahd_scsiphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SCSIPHASE_parse_table, 7, "SCSIPHASE",
+	    0x42, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scsibus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "SCSIBUS",
+	    0x46, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SELID_parse_table[] = {
+	{ "ONEBIT",		0x08, 0x08 },
+	{ "SELID_MASK",		0xf0, 0xf0 }
+};
+
+int
+ahd_selid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SELID_parse_table, 2, "SELID",
+	    0x49, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SSTAT0_parse_table[] = {
+	{ "ARBDO",		0x01, 0x01 },
+	{ "SPIORDY",		0x02, 0x02 },
+	{ "OVERRUN",		0x04, 0x04 },
+	{ "IOERR",		0x08, 0x08 },
+	{ "SELINGO",		0x10, 0x10 },
+	{ "SELDI",		0x20, 0x20 },
+	{ "SELDO",		0x40, 0x40 },
+	{ "TARGET",		0x80, 0x80 }
+};
+
+int
+ahd_sstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SSTAT0_parse_table, 8, "SSTAT0",
+	    0x4b, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SIMODE0_parse_table[] = {
+	{ "ENARBDO",		0x01, 0x01 },
+	{ "ENSPIORDY",		0x02, 0x02 },
+	{ "ENOVERRUN",		0x04, 0x04 },
+	{ "ENIOERR",		0x08, 0x08 },
+	{ "ENSELINGO",		0x10, 0x10 },
+	{ "ENSELDI",		0x20, 0x20 },
+	{ "ENSELDO",		0x40, 0x40 }
+};
+
+int
+ahd_simode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SIMODE0_parse_table, 7, "SIMODE0",
+	    0x4b, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SSTAT1_parse_table[] = {
+	{ "REQINIT",		0x01, 0x01 },
+	{ "STRB2FAST",		0x02, 0x02 },
+	{ "SCSIPERR",		0x04, 0x04 },
+	{ "BUSFREE",		0x08, 0x08 },
+	{ "PHASEMIS",		0x10, 0x10 },
+	{ "SCSIRSTI",		0x20, 0x20 },
+	{ "ATNTARG",		0x40, 0x40 },
+	{ "SELTO",		0x80, 0x80 }
+};
+
+int
+ahd_sstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SSTAT1_parse_table, 8, "SSTAT1",
+	    0x4c, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SSTAT2_parse_table[] = {
+	{ "BUSFREE_LQO",	0x40, 0xc0 },
+	{ "BUSFREE_DFF0",	0x80, 0xc0 },
+	{ "BUSFREE_DFF1",	0xc0, 0xc0 },
+	{ "DMADONE",		0x01, 0x01 },
+	{ "SDONE",		0x02, 0x02 },
+	{ "WIDE_RES",		0x04, 0x04 },
+	{ "BSYX",		0x08, 0x08 },
+	{ "EXP_ACTIVE",		0x10, 0x10 },
+	{ "NONPACKREQ",		0x20, 0x20 },
+	{ "BUSFREETIME",	0xc0, 0xc0 }
+};
+
+int
+ahd_sstat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SSTAT2_parse_table, 10, "SSTAT2",
+	    0x4d, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t PERRDIAG_parse_table[] = {
+	{ "DTERR",		0x01, 0x01 },
+	{ "DGFORMERR",		0x02, 0x02 },
+	{ "CRCERR",		0x04, 0x04 },
+	{ "AIPERR",		0x08, 0x08 },
+	{ "PARITYERR",		0x10, 0x10 },
+	{ "PREVPHASE",		0x20, 0x20 },
+	{ "HIPERR",		0x40, 0x40 },
+	{ "HIZERO",		0x80, 0x80 }
+};
+
+int
+ahd_perrdiag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(PERRDIAG_parse_table, 8, "PERRDIAG",
+	    0x4e, regvalue, cur_col, wrap));
+}
+
+int
+ahd_soffcnt_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "SOFFCNT",
+	    0x4f, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t LQISTAT0_parse_table[] = {
+	{ "LQIATNCMD",		0x01, 0x01 },
+	{ "LQIATNLQ",		0x02, 0x02 },
+	{ "LQIBADLQT",		0x04, 0x04 },
+	{ "LQICRCT2",		0x08, 0x08 },
+	{ "LQICRCT1",		0x10, 0x10 },
+	{ "LQIATNQAS",		0x20, 0x20 }
+};
+
+int
+ahd_lqistat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(LQISTAT0_parse_table, 6, "LQISTAT0",
+	    0x50, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t LQISTAT1_parse_table[] = {
+	{ "LQIOVERI_NLQ",	0x01, 0x01 },
+	{ "LQIOVERI_LQ",	0x02, 0x02 },
+	{ "LQIBADLQI",		0x04, 0x04 },
+	{ "LQICRCI_NLQ",	0x08, 0x08 },
+	{ "LQICRCI_LQ",		0x10, 0x10 },
+	{ "LQIABORT",		0x20, 0x20 },
+	{ "LQIPHASE_NLQ",	0x40, 0x40 },
+	{ "LQIPHASE_LQ",	0x80, 0x80 }
+};
+
+int
+ahd_lqistat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(LQISTAT1_parse_table, 8, "LQISTAT1",
+	    0x51, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t LQISTAT2_parse_table[] = {
+	{ "LQIGSAVAIL",		0x01, 0x01 },
+	{ "LQISTOPCMD",		0x02, 0x02 },
+	{ "LQISTOPLQ",		0x04, 0x04 },
+	{ "LQISTOPPKT",		0x08, 0x08 },
+	{ "LQIWAITFIFO",	0x10, 0x10 },
+	{ "LQIWORKONLQ",	0x20, 0x20 },
+	{ "LQIPHASE_OUTPKT",	0x40, 0x40 },
+	{ "PACKETIZED",		0x80, 0x80 }
+};
+
+int
+ahd_lqistat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(LQISTAT2_parse_table, 8, "LQISTAT2",
+	    0x52, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SSTAT3_parse_table[] = {
+	{ "OSRAMPERR",		0x01, 0x01 },
+	{ "NTRAMPERR",		0x02, 0x02 }
+};
+
+int
+ahd_sstat3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SSTAT3_parse_table, 2, "SSTAT3",
+	    0x53, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t LQOSTAT0_parse_table[] = {
+	{ "LQOTCRC",		0x01, 0x01 },
+	{ "LQOATNPKT",		0x02, 0x02 },
+	{ "LQOATNLQ",		0x04, 0x04 },
+	{ "LQOSTOPT2",		0x08, 0x08 },
+	{ "LQOTARGSCBPERR",	0x10, 0x10 }
+};
+
+int
+ahd_lqostat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(LQOSTAT0_parse_table, 5, "LQOSTAT0",
+	    0x54, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t LQOSTAT1_parse_table[] = {
+	{ "LQOPHACHGINPKT",	0x01, 0x01 },
+	{ "LQOBUSFREE",		0x02, 0x02 },
+	{ "LQOBADQAS",		0x04, 0x04 },
+	{ "LQOSTOPI2",		0x08, 0x08 },
+	{ "LQOINITSCBPERR",	0x10, 0x10 }
+};
+
+int
+ahd_lqostat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(LQOSTAT1_parse_table, 5, "LQOSTAT1",
+	    0x55, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t LQOSTAT2_parse_table[] = {
+	{ "LQOSTOP0",		0x01, 0x01 },
+	{ "LQOPHACHGOUTPKT",	0x02, 0x02 },
+	{ "LQOWAITFIFO",	0x10, 0x10 },
+	{ "LQOPKT",		0xe0, 0xe0 }
+};
+
+int
+ahd_lqostat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(LQOSTAT2_parse_table, 4, "LQOSTAT2",
+	    0x56, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SIMODE1_parse_table[] = {
+	{ "ENREQINIT",		0x01, 0x01 },
+	{ "ENSTRB2FAST",	0x02, 0x02 },
+	{ "ENSCSIPERR",		0x04, 0x04 },
+	{ "ENBUSFREE",		0x08, 0x08 },
+	{ "ENPHASEMIS",		0x10, 0x10 },
+	{ "ENSCSIRST",		0x20, 0x20 },
+	{ "ENATNTARG",		0x40, 0x40 },
+	{ "ENSELTIMO",		0x80, 0x80 }
+};
+
+int
+ahd_simode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SIMODE1_parse_table, 8, "SIMODE1",
+	    0x57, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t DFFSXFRCTL_parse_table[] = {
+	{ "RSTCHN",		0x01, 0x01 },
+	{ "CLRCHN",		0x02, 0x02 },
+	{ "CLRSHCNT",		0x04, 0x04 },
+	{ "DFFBITBUCKET",	0x08, 0x08 }
+};
+
+int
+ahd_dffsxfrctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(DFFSXFRCTL_parse_table, 4, "DFFSXFRCTL",
+	    0x5a, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SEQINTSRC_parse_table[] = {
+	{ "CFG4TCMD",		0x01, 0x01 },
+	{ "CFG4ICMD",		0x02, 0x02 },
+	{ "CFG4TSTAT",		0x04, 0x04 },
+	{ "CFG4ISTAT",		0x08, 0x08 },
+	{ "CFG4DATA",		0x10, 0x10 },
+	{ "SAVEPTRS",		0x20, 0x20 },
+	{ "CTXTDONE",		0x40, 0x40 }
+};
+
+int
+ahd_seqintsrc_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SEQINTSRC_parse_table, 7, "SEQINTSRC",
+	    0x5b, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SEQIMODE_parse_table[] = {
+	{ "ENCFG4TCMD",		0x01, 0x01 },
+	{ "ENCFG4ICMD",		0x02, 0x02 },
+	{ "ENCFG4TSTAT",	0x04, 0x04 },
+	{ "ENCFG4ISTAT",	0x08, 0x08 },
+	{ "ENCFG4DATA",		0x10, 0x10 },
+	{ "ENSAVEPTRS",		0x20, 0x20 },
+	{ "ENCTXTDONE",		0x40, 0x40 }
+};
+
+int
+ahd_seqimode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SEQIMODE_parse_table, 7, "SEQIMODE",
+	    0x5c, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t MDFFSTAT_parse_table[] = {
+	{ "FIFOFREE",		0x01, 0x01 },
+	{ "DATAINFIFO",		0x02, 0x02 },
+	{ "DLZERO",		0x04, 0x04 },
+	{ "SHVALID",		0x08, 0x08 },
+	{ "LASTSDONE",		0x10, 0x10 },
+	{ "SHCNTMINUS1",	0x20, 0x20 },
+	{ "SHCNTNEGATIVE",	0x40, 0x40 }
+};
+
+int
+ahd_mdffstat_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(MDFFSTAT_parse_table, 7, "MDFFSTAT",
+	    0x5d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_seloid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "SELOID",
+	    0x6b, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SG_STATE_parse_table[] = {
+	{ "SEGS_AVAIL",		0x01, 0x01 },
+	{ "LOADING_NEEDED",	0x02, 0x02 },
+	{ "FETCH_INPROG",	0x04, 0x04 }
+};
+
+int
+ahd_sg_state_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SG_STATE_parse_table, 3, "SG_STATE",
+	    0xa6, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t CCSCBCTL_parse_table[] = {
+	{ "CCSCBRESET",		0x01, 0x01 },
+	{ "CCSCBDIR",		0x04, 0x04 },
+	{ "CCSCBEN",		0x08, 0x08 },
+	{ "CCARREN",		0x10, 0x10 },
+	{ "ARRDONE",		0x40, 0x40 },
+	{ "CCSCBDONE",		0x80, 0x80 }
+};
+
+int
+ahd_ccscbctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(CCSCBCTL_parse_table, 6, "CCSCBCTL",
+	    0xad, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t CCSGCTL_parse_table[] = {
+	{ "CCSGRESET",		0x01, 0x01 },
+	{ "SG_FETCH_REQ",	0x02, 0x02 },
+	{ "CCSGENACK",		0x08, 0x08 },
+	{ "SG_CACHE_AVAIL",	0x10, 0x10 },
+	{ "CCSGDONE",		0x80, 0x80 },
+	{ "CCSGEN",		0x0c, 0x0c }
+};
+
+int
+ahd_ccsgctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(CCSGCTL_parse_table, 6, "CCSGCTL",
+	    0xad, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SEQCTL0_parse_table[] = {
+	{ "LOADRAM",		0x01, 0x01 },
+	{ "SEQRESET",		0x02, 0x02 },
+	{ "STEP",		0x04, 0x04 },
+	{ "BRKADRINTEN",	0x08, 0x08 },
+	{ "FASTMODE",		0x10, 0x10 },
+	{ "FAILDIS",		0x20, 0x20 },
+	{ "PAUSEDIS",		0x40, 0x40 },
+	{ "PERRORDIS",		0x80, 0x80 }
+};
+
+int
+ahd_seqctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SEQCTL0_parse_table, 8, "SEQCTL0",
+	    0xd6, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SEQINTCTL_parse_table[] = {
+	{ "IRET",		0x01, 0x01 },
+	{ "INTMASK1",		0x02, 0x02 },
+	{ "INTMASK2",		0x04, 0x04 },
+	{ "SCS_SEQ_INT1M0",	0x08, 0x08 },
+	{ "SCS_SEQ_INT1M1",	0x10, 0x10 },
+	{ "INT1_CONTEXT",	0x20, 0x20 },
+	{ "INTVEC1DSL",		0x80, 0x80 }
+};
+
+int
+ahd_seqintctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SEQINTCTL_parse_table, 7, "SEQINTCTL",
+	    0xd9, regvalue, cur_col, wrap));
+}
+
+int
+ahd_sram_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "SRAM_BASE",
+	    0x100, regvalue, cur_col, wrap));
+}
+
+int
+ahd_qfreeze_count_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "QFREEZE_COUNT",
+	    0x132, regvalue, cur_col, wrap));
+}
+
+int
+ahd_kernel_qfreeze_count_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "KERNEL_QFREEZE_COUNT",
+	    0x134, regvalue, cur_col, wrap));
+}
+
+int
+ahd_saved_mode_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "SAVED_MODE",
+	    0x136, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SEQ_FLAGS_parse_table[] = {
+	{ "NO_DISCONNECT",	0x01, 0x01 },
+	{ "SPHASE_PENDING",	0x02, 0x02 },
+	{ "DPHASE_PENDING",	0x04, 0x04 },
+	{ "CMDPHASE_PENDING",	0x08, 0x08 },
+	{ "TARG_CMD_PENDING",	0x10, 0x10 },
+	{ "DPHASE",		0x20, 0x20 },
+	{ "NO_CDB_SENT",	0x40, 0x40 },
+	{ "TARGET_CMD_IS_TAGGED",0x40, 0x40 },
+	{ "NOT_IDENTIFIED",	0x80, 0x80 }
+};
+
+int
+ahd_seq_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SEQ_FLAGS_parse_table, 9, "SEQ_FLAGS",
+	    0x139, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t LASTPHASE_parse_table[] = {
+	{ "P_DATAOUT",		0x00, 0xe0 },
+	{ "P_DATAOUT_DT",	0x20, 0xe0 },
+	{ "P_DATAIN",		0x40, 0xe0 },
+	{ "P_DATAIN_DT",	0x60, 0xe0 },
+	{ "P_COMMAND",		0x80, 0xe0 },
+	{ "P_MESGOUT",		0xa0, 0xe0 },
+	{ "P_STATUS",		0xc0, 0xe0 },
+	{ "P_MESGIN",		0xe0, 0xe0 },
+	{ "P_BUSFREE",		0x01, 0x01 },
+	{ "MSGI",		0x20, 0x20 },
+	{ "IOI",		0x40, 0x40 },
+	{ "CDI",		0x80, 0x80 },
+	{ "PHASE_MASK",		0xe0, 0xe0 }
+};
+
+int
+ahd_lastphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(LASTPHASE_parse_table, 13, "LASTPHASE",
+	    0x13c, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SEQ_FLAGS2_parse_table[] = {
+	{ "PENDING_MK_MESSAGE",	0x01, 0x01 },
+	{ "TARGET_MSG_PENDING",	0x02, 0x02 },
+	{ "SELECTOUT_QFROZEN",	0x04, 0x04 }
+};
+
+int
+ahd_seq_flags2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SEQ_FLAGS2_parse_table, 3, "SEQ_FLAGS2",
+	    0x14d, regvalue, cur_col, wrap));
+}
+
+int
+ahd_mk_message_scb_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "MK_MESSAGE_SCB",
+	    0x160, regvalue, cur_col, wrap));
+}
+
+int
+ahd_mk_message_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "MK_MESSAGE_SCSIID",
+	    0x162, regvalue, cur_col, wrap));
+}
+
+int
+ahd_scb_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(NULL, 0, "SCB_BASE",
+	    0x180, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SCB_CONTROL_parse_table[] = {
+	{ "SCB_TAG_TYPE",	0x03, 0x03 },
+	{ "DISCONNECTED",	0x04, 0x04 },
+	{ "STATUS_RCVD",	0x08, 0x08 },
+	{ "MK_MESSAGE",		0x10, 0x10 },
+	{ "TAG_ENB",		0x20, 0x20 },
+	{ "DISCENB",		0x40, 0x40 },
+	{ "TARGET_SCB",		0x80, 0x80 }
+};
+
+int
+ahd_scb_control_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SCB_CONTROL_parse_table, 7, "SCB_CONTROL",
+	    0x192, regvalue, cur_col, wrap));
+}
+
+static const ahd_reg_parse_entry_t SCB_SCSIID_parse_table[] = {
+	{ "OID",		0x0f, 0x0f },
+	{ "TID",		0xf0, 0xf0 }
+};
+
+int
+ahd_scb_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahd_print_register(SCB_SCSIID_parse_table, 2, "SCB_SCSIID",
+	    0x193, regvalue, cur_col, wrap));
+}
+
diff --git a/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
new file mode 100644
index 0000000..4b51e23
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
@@ -0,0 +1,1190 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *		 from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#120 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#77 $
+ */
+static const uint8_t seqprog[] = {
+	0xff, 0x02, 0x06, 0x78,
+	0x00, 0xea, 0x6e, 0x59,
+	0x01, 0xea, 0x04, 0x30,
+	0xff, 0x04, 0x0c, 0x78,
+	0x19, 0xea, 0x6e, 0x59,
+	0x19, 0xea, 0x04, 0x00,
+	0x33, 0xea, 0x68, 0x59,
+	0x33, 0xea, 0x00, 0x00,
+	0x60, 0x3a, 0x3a, 0x68,
+	0x04, 0x4d, 0x35, 0x78,
+	0x01, 0x34, 0xc1, 0x31,
+	0x00, 0x32, 0x21, 0x60,
+	0x01, 0x35, 0xc1, 0x31,
+	0x00, 0x33, 0x21, 0x60,
+	0xfb, 0x4d, 0x9b, 0x0a,
+	0x00, 0xe2, 0x34, 0x40,
+	0x50, 0x4b, 0x3a, 0x68,
+	0xff, 0x31, 0x3b, 0x70,
+	0x02, 0x30, 0x51, 0x31,
+	0xff, 0x8d, 0x2d, 0x70,
+	0x02, 0x8c, 0x51, 0x31,
+	0xff, 0x8d, 0x29, 0x60,
+	0x02, 0x28, 0x19, 0x33,
+	0x02, 0x30, 0x51, 0x32,
+	0xff, 0xea, 0x62, 0x02,
+	0x00, 0xe2, 0x3a, 0x40,
+	0xff, 0x21, 0x3b, 0x70,
+	0x40, 0x4b, 0xb4, 0x69,
+	0x00, 0xe2, 0x72, 0x59,
+	0x40, 0x4b, 0xb4, 0x69,
+	0x20, 0x4b, 0xa0, 0x69,
+	0xfc, 0x42, 0x44, 0x78,
+	0x10, 0x40, 0x44, 0x78,
+	0x00, 0xe2, 0x10, 0x5e,
+	0x20, 0x4d, 0x48, 0x78,
+	0x00, 0xe2, 0x10, 0x5e,
+	0x30, 0x3f, 0xc0, 0x09,
+	0x30, 0xe0, 0x50, 0x60,
+	0x7f, 0x4a, 0x94, 0x08,
+	0x00, 0xe2, 0x52, 0x40,
+	0xc0, 0x4a, 0x94, 0x00,
+	0x00, 0xe2, 0x5e, 0x58,
+	0x00, 0xe2, 0x76, 0x58,
+	0x00, 0xe2, 0x86, 0x58,
+	0x00, 0xe2, 0x06, 0x40,
+	0x33, 0xea, 0x68, 0x59,
+	0x33, 0xea, 0x00, 0x00,
+	0x01, 0x52, 0x84, 0x78,
+	0x02, 0x58, 0x50, 0x31,
+	0xff, 0xea, 0x10, 0x0b,
+	0xff, 0x97, 0x6f, 0x78,
+	0x50, 0x4b, 0x6a, 0x68,
+	0xbf, 0x3a, 0x74, 0x08,
+	0x14, 0xea, 0x6e, 0x59,
+	0x14, 0xea, 0x04, 0x00,
+	0x08, 0x92, 0x25, 0x03,
+	0xff, 0x90, 0x5f, 0x68,
+	0x00, 0xe2, 0x8a, 0x5b,
+	0x00, 0xe2, 0x5e, 0x40,
+	0x00, 0xea, 0x68, 0x59,
+	0x01, 0xea, 0x00, 0x30,
+	0x80, 0xf9, 0x7e, 0x68,
+	0x00, 0xe2, 0x66, 0x59,
+	0x11, 0xea, 0x68, 0x59,
+	0x11, 0xea, 0x00, 0x00,
+	0x80, 0xf9, 0x66, 0x79,
+	0xff, 0xea, 0xd4, 0x0d,
+	0x22, 0xea, 0x68, 0x59,
+	0x22, 0xea, 0x00, 0x00,
+	0x10, 0x16, 0x90, 0x78,
+	0x10, 0x16, 0x2c, 0x00,
+	0x01, 0x0b, 0xae, 0x32,
+	0x18, 0xad, 0x1c, 0x79,
+	0x04, 0xad, 0xdc, 0x68,
+	0x80, 0xad, 0x84, 0x78,
+	0x10, 0xad, 0xaa, 0x78,
+	0xe7, 0xad, 0x5a, 0x09,
+	0x02, 0x8c, 0x59, 0x32,
+	0xff, 0x8d, 0xa1, 0x60,
+	0xff, 0xea, 0x5e, 0x02,
+	0xff, 0x88, 0xa7, 0x78,
+	0x02, 0x30, 0x19, 0x33,
+	0x02, 0xa8, 0x60, 0x36,
+	0x02, 0x28, 0x19, 0x33,
+	0x02, 0xa8, 0x50, 0x36,
+	0xe7, 0xad, 0x5a, 0x09,
+	0x00, 0xe2, 0xb8, 0x58,
+	0xff, 0xea, 0x56, 0x02,
+	0x04, 0x7c, 0x88, 0x32,
+	0x20, 0x16, 0x84, 0x78,
+	0x04, 0x40, 0x89, 0x32,
+	0x80, 0x3d, 0x7b, 0x16,
+	0xff, 0x2d, 0xc7, 0x60,
+	0xff, 0x29, 0xc7, 0x60,
+	0x40, 0x57, 0xd7, 0x78,
+	0xff, 0x55, 0xc7, 0x68,
+	0xff, 0x53, 0xc1, 0x19,
+	0x00, 0x54, 0xd5, 0x19,
+	0x00, 0xe2, 0xd6, 0x50,
+	0x01, 0x52, 0xc1, 0x31,
+	0x00, 0x56, 0xd5, 0x19,
+	0x00, 0xe2, 0xd6, 0x48,
+	0x80, 0x18, 0x84, 0x78,
+	0x02, 0x50, 0x1d, 0x30,
+	0x10, 0xea, 0x18, 0x00,
+	0x60, 0x18, 0x30, 0x00,
+	0x7f, 0x18, 0x30, 0x0c,
+	0x02, 0xea, 0x02, 0x00,
+	0xff, 0xea, 0xac, 0x0a,
+	0x80, 0x18, 0x30, 0x04,
+	0x40, 0xad, 0x84, 0x78,
+	0xe7, 0xad, 0x5a, 0x09,
+	0xff, 0xea, 0xc0, 0x09,
+	0x01, 0x54, 0xa9, 0x1a,
+	0x00, 0x55, 0xab, 0x22,
+	0x01, 0x94, 0x6d, 0x33,
+	0xff, 0xea, 0x20, 0x0b,
+	0x04, 0xac, 0x49, 0x32,
+	0xff, 0xea, 0x5a, 0x03,
+	0xff, 0xea, 0x5e, 0x03,
+	0x01, 0x10, 0xd4, 0x31,
+	0x02, 0xa8, 0x40, 0x31,
+	0x01, 0x92, 0xc1, 0x31,
+	0x3d, 0x93, 0xc5, 0x29,
+	0xfe, 0xe2, 0xc4, 0x09,
+	0x01, 0xea, 0xc6, 0x01,
+	0x02, 0xe2, 0xc8, 0x31,
+	0x02, 0xec, 0x50, 0x31,
+	0x02, 0xa0, 0xda, 0x31,
+	0xff, 0xa9, 0x10, 0x71,
+	0x10, 0xe0, 0x0e, 0x79,
+	0x10, 0x92, 0x0f, 0x79,
+	0x01, 0x4d, 0x9b, 0x02,
+	0x02, 0xa0, 0xc0, 0x32,
+	0x01, 0x93, 0xc5, 0x36,
+	0x02, 0xa0, 0x58, 0x37,
+	0xff, 0x21, 0x19, 0x71,
+	0x02, 0x22, 0x51, 0x31,
+	0x02, 0xa0, 0x5c, 0x33,
+	0x02, 0xa0, 0x44, 0x36,
+	0x02, 0xa0, 0x40, 0x32,
+	0x02, 0xa0, 0x44, 0x36,
+	0x05, 0x4d, 0x21, 0x69,
+	0x40, 0x16, 0x52, 0x69,
+	0xff, 0x2d, 0x57, 0x61,
+	0xff, 0x29, 0x85, 0x70,
+	0x02, 0x28, 0x55, 0x32,
+	0x01, 0xea, 0x5a, 0x01,
+	0x04, 0x44, 0xf9, 0x30,
+	0x01, 0x44, 0xc1, 0x31,
+	0x02, 0x28, 0x51, 0x31,
+	0x02, 0xa8, 0x60, 0x31,
+	0x01, 0xa4, 0x61, 0x31,
+	0x01, 0x3d, 0x61, 0x31,
+	0x01, 0x14, 0xd4, 0x31,
+	0x01, 0x56, 0xad, 0x1a,
+	0xff, 0x54, 0xa9, 0x1a,
+	0xff, 0x55, 0xab, 0x22,
+	0xff, 0x8d, 0x4b, 0x71,
+	0x80, 0xac, 0x4a, 0x71,
+	0x20, 0x16, 0x4a, 0x69,
+	0x00, 0xac, 0xc4, 0x19,
+	0x07, 0xe2, 0x4a, 0xf9,
+	0x02, 0x8c, 0x51, 0x31,
+	0x00, 0xe2, 0x2e, 0x41,
+	0x01, 0xac, 0x08, 0x31,
+	0x09, 0xea, 0x5a, 0x01,
+	0x02, 0x8c, 0x51, 0x32,
+	0xff, 0xea, 0x1a, 0x07,
+	0x04, 0x24, 0xf9, 0x30,
+	0x1d, 0xea, 0x5c, 0x41,
+	0x02, 0x2c, 0x51, 0x31,
+	0x04, 0xa8, 0xf9, 0x30,
+	0x19, 0xea, 0x5c, 0x41,
+	0x06, 0xea, 0x08, 0x81,
+	0x01, 0xe2, 0x5a, 0x35,
+	0x02, 0xf2, 0xf0, 0x31,
+	0xff, 0xea, 0xd4, 0x0d,
+	0x02, 0xf2, 0xf0, 0x31,
+	0x02, 0xf8, 0xe4, 0x35,
+	0x80, 0xea, 0xb2, 0x01,
+	0x01, 0xe2, 0x00, 0x30,
+	0xff, 0xea, 0xb2, 0x0d,
+	0x01, 0xe2, 0x04, 0x30,
+	0x01, 0xea, 0x04, 0x34,
+	0x02, 0x20, 0xbd, 0x30,
+	0x02, 0x20, 0xb9, 0x30,
+	0x02, 0x20, 0x51, 0x31,
+	0x4c, 0x93, 0xd7, 0x28,
+	0x10, 0x92, 0x81, 0x79,
+	0x01, 0x6b, 0xc0, 0x30,
+	0x02, 0x64, 0xc8, 0x00,
+	0x40, 0x3a, 0x74, 0x04,
+	0x00, 0xe2, 0x76, 0x58,
+	0x33, 0xea, 0x68, 0x59,
+	0x33, 0xea, 0x00, 0x00,
+	0x30, 0x3f, 0xc0, 0x09,
+	0x30, 0xe0, 0x82, 0x61,
+	0x20, 0x3f, 0x98, 0x69,
+	0x10, 0x3f, 0x82, 0x79,
+	0x02, 0xea, 0x7e, 0x00,
+	0x00, 0xea, 0x68, 0x59,
+	0x01, 0xea, 0x00, 0x30,
+	0x02, 0x4e, 0x51, 0x35,
+	0x01, 0xea, 0x7e, 0x00,
+	0x11, 0xea, 0x68, 0x59,
+	0x11, 0xea, 0x00, 0x00,
+	0x02, 0x4e, 0x51, 0x35,
+	0xc0, 0x4a, 0x94, 0x00,
+	0x04, 0x41, 0xa6, 0x79,
+	0x08, 0xea, 0x98, 0x00,
+	0x08, 0x57, 0xae, 0x00,
+	0x08, 0x3c, 0x78, 0x00,
+	0xf0, 0x49, 0x74, 0x0a,
+	0x0f, 0x67, 0xc0, 0x09,
+	0x00, 0x3a, 0x75, 0x02,
+	0x20, 0xea, 0x96, 0x00,
+	0x00, 0xe2, 0x28, 0x42,
+	0xc0, 0x4a, 0x94, 0x00,
+	0x40, 0x3a, 0xd2, 0x69,
+	0x02, 0x55, 0x06, 0x68,
+	0x02, 0x56, 0xd2, 0x69,
+	0xff, 0x5b, 0xd2, 0x61,
+	0x02, 0x20, 0x51, 0x31,
+	0x80, 0xea, 0xb2, 0x01,
+	0x44, 0xea, 0x00, 0x00,
+	0x01, 0x33, 0xc0, 0x31,
+	0x33, 0xea, 0x00, 0x00,
+	0xff, 0xea, 0xb2, 0x09,
+	0xff, 0xe0, 0xc0, 0x19,
+	0xff, 0xe0, 0xd4, 0x79,
+	0x02, 0xac, 0x51, 0x31,
+	0x00, 0xe2, 0xca, 0x41,
+	0x02, 0x5e, 0x50, 0x31,
+	0x02, 0xa8, 0xb8, 0x30,
+	0x02, 0x5c, 0x50, 0x31,
+	0xff, 0xad, 0xe5, 0x71,
+	0x02, 0xac, 0x41, 0x31,
+	0x02, 0x22, 0x51, 0x31,
+	0x02, 0xa0, 0x5c, 0x33,
+	0x02, 0xa0, 0x44, 0x32,
+	0x00, 0xe2, 0xf8, 0x41,
+	0x01, 0x4d, 0xf1, 0x79,
+	0x01, 0x62, 0xc1, 0x31,
+	0x00, 0x93, 0xf1, 0x61,
+	0xfe, 0x4d, 0x9b, 0x0a,
+	0x02, 0x60, 0x41, 0x31,
+	0x00, 0xe2, 0xdc, 0x41,
+	0x3d, 0x93, 0xc9, 0x29,
+	0x01, 0xe4, 0xc8, 0x01,
+	0x01, 0xea, 0xca, 0x01,
+	0xff, 0xea, 0xda, 0x01,
+	0x02, 0x20, 0x51, 0x31,
+	0x02, 0xae, 0x41, 0x32,
+	0xff, 0x21, 0x01, 0x62,
+	0xff, 0xea, 0x46, 0x02,
+	0x02, 0x5c, 0x50, 0x31,
+	0x40, 0xea, 0x96, 0x00,
+	0x02, 0x56, 0x20, 0x6e,
+	0x01, 0x55, 0x20, 0x6e,
+	0x10, 0x92, 0x0d, 0x7a,
+	0x10, 0x40, 0x16, 0x6a,
+	0x01, 0x56, 0x16, 0x7a,
+	0xff, 0x97, 0x07, 0x78,
+	0x13, 0xea, 0x6e, 0x59,
+	0x13, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x06, 0x40,
+	0xbf, 0x3a, 0x74, 0x08,
+	0x04, 0x41, 0x1c, 0x7a,
+	0x08, 0xea, 0x98, 0x00,
+	0x08, 0x57, 0xae, 0x00,
+	0x01, 0x93, 0x75, 0x32,
+	0x01, 0x94, 0x77, 0x32,
+	0x40, 0xea, 0x72, 0x02,
+	0x08, 0x3c, 0x78, 0x00,
+	0x80, 0xea, 0x6e, 0x02,
+	0x00, 0xe2, 0xf6, 0x5b,
+	0x01, 0x3c, 0xc1, 0x31,
+	0x9f, 0xe0, 0x98, 0x7c,
+	0x80, 0xe0, 0x3c, 0x72,
+	0xa0, 0xe0, 0x78, 0x72,
+	0xc0, 0xe0, 0x6e, 0x72,
+	0xe0, 0xe0, 0xa8, 0x72,
+	0x01, 0xea, 0x6e, 0x59,
+	0x01, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x28, 0x42,
+	0x80, 0x39, 0x43, 0x7a,
+	0x03, 0xea, 0x6e, 0x59,
+	0x03, 0xea, 0x04, 0x00,
+	0xee, 0x00, 0x4a, 0x6a,
+	0x05, 0xea, 0xb4, 0x00,
+	0x33, 0xea, 0x68, 0x59,
+	0x33, 0xea, 0x00, 0x00,
+	0x02, 0xa8, 0x9c, 0x32,
+	0x00, 0xe2, 0x88, 0x59,
+	0xef, 0x96, 0xd5, 0x19,
+	0x00, 0xe2, 0x5a, 0x52,
+	0x09, 0x80, 0xe1, 0x30,
+	0x02, 0xea, 0x36, 0x00,
+	0xa8, 0xea, 0x32, 0x00,
+	0x00, 0xe2, 0x60, 0x42,
+	0x01, 0x96, 0xd1, 0x30,
+	0x10, 0x80, 0x89, 0x31,
+	0x20, 0xea, 0x32, 0x00,
+	0xbf, 0x39, 0x73, 0x0a,
+	0x10, 0x4c, 0x6a, 0x6a,
+	0x20, 0x19, 0x62, 0x6a,
+	0x20, 0x19, 0x66, 0x6a,
+	0x02, 0x4d, 0x28, 0x6a,
+	0x40, 0x39, 0x73, 0x02,
+	0x00, 0xe2, 0x28, 0x42,
+	0x80, 0x39, 0xe9, 0x6a,
+	0x01, 0x44, 0x10, 0x33,
+	0x08, 0x92, 0x25, 0x03,
+	0x00, 0xe2, 0x28, 0x42,
+	0x10, 0xea, 0x80, 0x00,
+	0x01, 0x37, 0xc5, 0x31,
+	0x80, 0xe2, 0x94, 0x62,
+	0x10, 0x92, 0xb9, 0x6a,
+	0xc0, 0x94, 0xc5, 0x01,
+	0x40, 0x92, 0x85, 0x6a,
+	0xbf, 0xe2, 0xc4, 0x09,
+	0x20, 0x92, 0x99, 0x7a,
+	0x01, 0xe2, 0x88, 0x30,
+	0x00, 0xe2, 0xf6, 0x5b,
+	0xa0, 0x3c, 0xa1, 0x62,
+	0x23, 0x92, 0x89, 0x08,
+	0x00, 0xe2, 0xf6, 0x5b,
+	0xa0, 0x3c, 0xa1, 0x62,
+	0x00, 0xa8, 0x98, 0x42,
+	0xff, 0xe2, 0x98, 0x62,
+	0x00, 0xe2, 0xb8, 0x42,
+	0x40, 0xea, 0x98, 0x00,
+	0x01, 0xe2, 0x88, 0x30,
+	0x00, 0xe2, 0xf6, 0x5b,
+	0xa0, 0x3c, 0x77, 0x72,
+	0x40, 0xea, 0x98, 0x00,
+	0x01, 0x37, 0x95, 0x32,
+	0x08, 0xea, 0x6e, 0x02,
+	0x00, 0xe2, 0x28, 0x42,
+	0xe0, 0xea, 0x12, 0x5c,
+	0x80, 0xe0, 0xf4, 0x6a,
+	0x04, 0xe0, 0xa6, 0x73,
+	0x02, 0xe0, 0xd8, 0x73,
+	0x00, 0xea, 0x52, 0x73,
+	0x03, 0xe0, 0xe8, 0x73,
+	0x23, 0xe0, 0xca, 0x72,
+	0x08, 0xe0, 0xf0, 0x72,
+	0x00, 0xe2, 0xf6, 0x5b,
+	0x07, 0xea, 0x6e, 0x59,
+	0x07, 0xea, 0x04, 0x00,
+	0x08, 0x48, 0x29, 0x72,
+	0x04, 0x48, 0xc7, 0x62,
+	0x01, 0x49, 0x89, 0x30,
+	0x00, 0xe2, 0xb8, 0x42,
+	0x01, 0x44, 0xd4, 0x31,
+	0x00, 0xe2, 0xb8, 0x42,
+	0x01, 0x00, 0x6c, 0x32,
+	0x33, 0xea, 0x68, 0x59,
+	0x33, 0xea, 0x00, 0x00,
+	0x4c, 0x3a, 0xc1, 0x28,
+	0x01, 0x64, 0xc0, 0x31,
+	0x00, 0x36, 0x69, 0x59,
+	0x01, 0x36, 0x01, 0x30,
+	0x01, 0xe0, 0xee, 0x7a,
+	0xa0, 0xea, 0x08, 0x5c,
+	0x01, 0xa0, 0xee, 0x62,
+	0x01, 0x84, 0xe3, 0x7a,
+	0x01, 0x95, 0xf1, 0x6a,
+	0x05, 0xea, 0x6e, 0x59,
+	0x05, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0xf0, 0x42,
+	0x03, 0xea, 0x6e, 0x59,
+	0x03, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0xf0, 0x42,
+	0x07, 0xea, 0x1a, 0x5c,
+	0x01, 0x44, 0xd4, 0x31,
+	0x00, 0xe2, 0x28, 0x42,
+	0x3f, 0xe0, 0x76, 0x0a,
+	0xc0, 0x3a, 0xc1, 0x09,
+	0x00, 0x3b, 0x51, 0x01,
+	0xff, 0xea, 0x52, 0x09,
+	0x30, 0x3a, 0xc5, 0x09,
+	0x3d, 0xe2, 0xc4, 0x29,
+	0xb8, 0xe2, 0xc4, 0x19,
+	0x01, 0xea, 0xc6, 0x01,
+	0x02, 0xe2, 0xc8, 0x31,
+	0x02, 0xec, 0x40, 0x31,
+	0xff, 0xa1, 0x10, 0x73,
+	0x02, 0xe8, 0xda, 0x31,
+	0x02, 0xa0, 0x50, 0x31,
+	0x00, 0xe2, 0x32, 0x43,
+	0x80, 0x39, 0x73, 0x02,
+	0x01, 0x44, 0xd4, 0x31,
+	0x00, 0xe2, 0xf6, 0x5b,
+	0x01, 0x39, 0x73, 0x02,
+	0xe0, 0x3c, 0x4d, 0x63,
+	0x02, 0x39, 0x73, 0x02,
+	0x20, 0x46, 0x46, 0x63,
+	0xff, 0xea, 0x52, 0x09,
+	0xa8, 0xea, 0x08, 0x5c,
+	0x04, 0x92, 0x2d, 0x7b,
+	0x01, 0x3a, 0xc1, 0x31,
+	0x00, 0x93, 0x2d, 0x63,
+	0x01, 0x3b, 0xc1, 0x31,
+	0x00, 0x94, 0x37, 0x73,
+	0x01, 0xa9, 0x52, 0x11,
+	0xff, 0xa9, 0x22, 0x6b,
+	0x00, 0xe2, 0x46, 0x43,
+	0x10, 0x39, 0x73, 0x02,
+	0x04, 0x92, 0x47, 0x7b,
+	0xfb, 0x92, 0x25, 0x0b,
+	0xff, 0xea, 0x72, 0x0a,
+	0x01, 0xa4, 0x41, 0x6b,
+	0x02, 0xa8, 0x9c, 0x32,
+	0x00, 0xe2, 0x88, 0x59,
+	0x10, 0x92, 0xf1, 0x7a,
+	0xff, 0xea, 0x1a, 0x5c,
+	0x00, 0xe2, 0xf0, 0x42,
+	0x04, 0xea, 0x6e, 0x59,
+	0x04, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0xf0, 0x42,
+	0x04, 0xea, 0x6e, 0x59,
+	0x04, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x28, 0x42,
+	0x08, 0x92, 0xe9, 0x7a,
+	0xc0, 0x39, 0x5d, 0x7b,
+	0x80, 0x39, 0xe9, 0x6a,
+	0xff, 0x88, 0x5d, 0x6b,
+	0x40, 0x39, 0xe9, 0x6a,
+	0x10, 0x92, 0x63, 0x7b,
+	0x0a, 0xea, 0x6e, 0x59,
+	0x0a, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x82, 0x5b,
+	0x00, 0xe2, 0xc2, 0x43,
+	0x50, 0x4b, 0x6a, 0x6b,
+	0xbf, 0x3a, 0x74, 0x08,
+	0x01, 0xe0, 0xf4, 0x31,
+	0xff, 0xea, 0xc0, 0x09,
+	0x01, 0x32, 0x65, 0x1a,
+	0x00, 0x33, 0x67, 0x22,
+	0x04, 0x4d, 0x9b, 0x02,
+	0x01, 0xfa, 0xc0, 0x35,
+	0x02, 0xa8, 0x90, 0x32,
+	0x02, 0xea, 0xb4, 0x00,
+	0x33, 0xea, 0x68, 0x59,
+	0x33, 0xea, 0x00, 0x00,
+	0x02, 0x48, 0x51, 0x31,
+	0xff, 0x90, 0x85, 0x68,
+	0xff, 0x88, 0x8f, 0x6b,
+	0x01, 0xa4, 0x8b, 0x6b,
+	0x02, 0xa4, 0x93, 0x6b,
+	0x01, 0x84, 0x93, 0x7b,
+	0x02, 0x28, 0x19, 0x33,
+	0x02, 0xa8, 0x50, 0x36,
+	0xff, 0x88, 0x93, 0x73,
+	0x00, 0xe2, 0x66, 0x5b,
+	0x02, 0xa8, 0x20, 0x33,
+	0x04, 0xa4, 0x49, 0x03,
+	0xff, 0xea, 0x1a, 0x03,
+	0xff, 0x2d, 0x9f, 0x63,
+	0x02, 0xa8, 0x58, 0x32,
+	0x02, 0xa8, 0x5c, 0x36,
+	0x02, 0xa8, 0x40, 0x31,
+	0x02, 0x2e, 0x51, 0x31,
+	0x02, 0xa0, 0x18, 0x33,
+	0x02, 0xa0, 0x5c, 0x36,
+	0xc0, 0x39, 0xe9, 0x6a,
+	0x04, 0x92, 0x25, 0x03,
+	0x20, 0x92, 0xc3, 0x6b,
+	0x02, 0xa8, 0x40, 0x31,
+	0xc0, 0x3a, 0xc1, 0x09,
+	0x00, 0x3b, 0x51, 0x01,
+	0xff, 0xea, 0x52, 0x09,
+	0x30, 0x3a, 0xc5, 0x09,
+	0x3d, 0xe2, 0xc4, 0x29,
+	0xb8, 0xe2, 0xc4, 0x19,
+	0x01, 0xea, 0xc6, 0x01,
+	0x02, 0xe2, 0xc8, 0x31,
+	0x02, 0xa0, 0xda, 0x31,
+	0x02, 0xa0, 0x50, 0x31,
+	0xf7, 0x57, 0xae, 0x08,
+	0x08, 0xea, 0x98, 0x00,
+	0x01, 0x44, 0xd4, 0x31,
+	0xee, 0x00, 0xcc, 0x6b,
+	0x02, 0xea, 0xb4, 0x00,
+	0xc0, 0xea, 0x72, 0x02,
+	0x09, 0x4c, 0xce, 0x7b,
+	0x01, 0xea, 0x78, 0x02,
+	0x08, 0x4c, 0x06, 0x68,
+	0x0b, 0xea, 0x6e, 0x59,
+	0x0b, 0xea, 0x04, 0x00,
+	0x01, 0x44, 0xd4, 0x31,
+	0x20, 0x39, 0x29, 0x7a,
+	0x00, 0xe2, 0xe0, 0x5b,
+	0x00, 0xe2, 0x28, 0x42,
+	0x01, 0x84, 0xe5, 0x7b,
+	0x01, 0xa4, 0x49, 0x07,
+	0x08, 0x60, 0x30, 0x33,
+	0x08, 0x80, 0x41, 0x37,
+	0xdf, 0x39, 0x73, 0x0a,
+	0xee, 0x00, 0xf2, 0x6b,
+	0x05, 0xea, 0xb4, 0x00,
+	0x33, 0xea, 0x68, 0x59,
+	0x33, 0xea, 0x00, 0x00,
+	0x00, 0xe2, 0x88, 0x59,
+	0x00, 0xe2, 0xf0, 0x42,
+	0xff, 0x42, 0x02, 0x6c,
+	0x01, 0x41, 0xf6, 0x6b,
+	0x02, 0x41, 0xf6, 0x7b,
+	0xff, 0x42, 0x02, 0x6c,
+	0x01, 0x41, 0xf6, 0x6b,
+	0x02, 0x41, 0xf6, 0x7b,
+	0xff, 0x42, 0x02, 0x7c,
+	0x04, 0x4c, 0xf6, 0x6b,
+	0xe0, 0x41, 0x78, 0x0e,
+	0x01, 0x44, 0xd4, 0x31,
+	0xff, 0x42, 0x0a, 0x7c,
+	0x04, 0x4c, 0x0a, 0x6c,
+	0xe0, 0x41, 0x78, 0x0a,
+	0xe0, 0x3c, 0x29, 0x62,
+	0xff, 0xea, 0xca, 0x09,
+	0x01, 0xe2, 0xc8, 0x31,
+	0x01, 0x46, 0xda, 0x35,
+	0x01, 0x44, 0xd4, 0x35,
+	0x10, 0xea, 0x80, 0x00,
+	0x01, 0xe2, 0x6e, 0x36,
+	0x04, 0xa6, 0x22, 0x7c,
+	0xff, 0xea, 0x5a, 0x09,
+	0xff, 0xea, 0x4c, 0x0d,
+	0x01, 0xa6, 0x4e, 0x6c,
+	0x10, 0xad, 0x84, 0x78,
+	0x80, 0xad, 0x46, 0x6c,
+	0x08, 0xad, 0x84, 0x68,
+	0x20, 0x19, 0x3a, 0x7c,
+	0x80, 0xea, 0xb2, 0x01,
+	0x11, 0x00, 0x00, 0x10,
+	0x02, 0xa6, 0x36, 0x7c,
+	0xff, 0xea, 0xb2, 0x0d,
+	0x11, 0x00, 0x00, 0x10,
+	0xff, 0xea, 0xb2, 0x09,
+	0x04, 0x84, 0xf9, 0x30,
+	0x00, 0xea, 0x08, 0x81,
+	0xff, 0xea, 0xd4, 0x09,
+	0x02, 0x84, 0xf9, 0x88,
+	0x0d, 0xea, 0x5a, 0x01,
+	0x04, 0xa6, 0x4c, 0x05,
+	0x04, 0xa6, 0x84, 0x78,
+	0xff, 0xea, 0x5a, 0x09,
+	0x03, 0x84, 0x59, 0x89,
+	0x03, 0xea, 0x4c, 0x01,
+	0x80, 0x1a, 0x84, 0x78,
+	0x08, 0x19, 0x84, 0x78,
+	0x08, 0xb0, 0xe0, 0x30,
+	0x04, 0xb0, 0xe0, 0x30,
+	0x03, 0xb0, 0xf0, 0x30,
+	0x01, 0xb0, 0x06, 0x33,
+	0x7f, 0x83, 0xe9, 0x08,
+	0x04, 0xac, 0x58, 0x19,
+	0xff, 0xea, 0xc0, 0x09,
+	0x04, 0x84, 0x09, 0x9b,
+	0x00, 0x85, 0x0b, 0x23,
+	0x00, 0x86, 0x0d, 0x23,
+	0x00, 0x87, 0x0f, 0x23,
+	0x01, 0x84, 0xc5, 0x31,
+	0x80, 0x83, 0x71, 0x7c,
+	0x02, 0xe2, 0xc4, 0x01,
+	0xff, 0xea, 0x4c, 0x09,
+	0x01, 0xe2, 0x36, 0x30,
+	0xc8, 0x19, 0x32, 0x00,
+	0x88, 0x19, 0x32, 0x00,
+	0x01, 0xac, 0xd4, 0x99,
+	0x00, 0xe2, 0x84, 0x50,
+	0xfe, 0xa6, 0x4c, 0x0d,
+	0x0b, 0x98, 0xe1, 0x30,
+	0xfd, 0xa4, 0x49, 0x09,
+	0x80, 0xa3, 0x85, 0x7c,
+	0x02, 0xa4, 0x48, 0x01,
+	0x01, 0xa4, 0x36, 0x30,
+	0xa8, 0xea, 0x32, 0x00,
+	0xfd, 0xa4, 0x49, 0x0b,
+	0x05, 0xa3, 0x07, 0x33,
+	0x80, 0x83, 0x91, 0x6c,
+	0x02, 0xea, 0x4c, 0x05,
+	0xff, 0xea, 0x4c, 0x0d,
+	0x00, 0xe2, 0x60, 0x59,
+	0x02, 0xa6, 0x24, 0x6c,
+	0x80, 0xf9, 0xf2, 0x05,
+	0xc0, 0x39, 0x9f, 0x7c,
+	0x03, 0xea, 0x6e, 0x59,
+	0x03, 0xea, 0x04, 0x00,
+	0x20, 0x39, 0xc3, 0x7c,
+	0x01, 0x84, 0xa9, 0x6c,
+	0x06, 0xea, 0x6e, 0x59,
+	0x06, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0xc6, 0x44,
+	0x01, 0x00, 0x6c, 0x32,
+	0xee, 0x00, 0xb2, 0x6c,
+	0x05, 0xea, 0xb4, 0x00,
+	0x33, 0xea, 0x68, 0x59,
+	0x33, 0xea, 0x00, 0x00,
+	0x80, 0x3d, 0x7a, 0x00,
+	0xfc, 0x42, 0xb4, 0x7c,
+	0x7f, 0x3d, 0x7a, 0x08,
+	0x00, 0x36, 0x69, 0x59,
+	0x01, 0x36, 0x01, 0x30,
+	0x09, 0xea, 0x6e, 0x59,
+	0x09, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x28, 0x42,
+	0x01, 0xa4, 0xa9, 0x6c,
+	0x00, 0xe2, 0x7c, 0x5c,
+	0x20, 0x39, 0x73, 0x02,
+	0x01, 0x00, 0x6c, 0x32,
+	0x02, 0xa6, 0xce, 0x7c,
+	0x00, 0xe2, 0x92, 0x5c,
+	0x00, 0xe2, 0x76, 0x58,
+	0x00, 0xe2, 0x86, 0x58,
+	0x00, 0xe2, 0x5a, 0x58,
+	0x00, 0x36, 0x69, 0x59,
+	0x01, 0x36, 0x01, 0x30,
+	0x20, 0x19, 0xce, 0x6c,
+	0x00, 0xe2, 0xfe, 0x5c,
+	0x04, 0x19, 0xe8, 0x6c,
+	0x02, 0x19, 0x32, 0x00,
+	0x01, 0x84, 0xe9, 0x7c,
+	0x01, 0x1b, 0xe2, 0x7c,
+	0x01, 0x1a, 0xe8, 0x6c,
+	0x00, 0xe2, 0x98, 0x44,
+	0x80, 0x4b, 0xee, 0x6c,
+	0x01, 0x4c, 0xea, 0x7c,
+	0x03, 0x42, 0x98, 0x6c,
+	0x00, 0xe2, 0x1e, 0x5c,
+	0x80, 0xf9, 0xf2, 0x01,
+	0x04, 0x39, 0x29, 0x7a,
+	0x00, 0xe2, 0x28, 0x42,
+	0x08, 0x5d, 0x06, 0x6d,
+	0x00, 0xe2, 0x76, 0x58,
+	0x00, 0x36, 0x69, 0x59,
+	0x01, 0x36, 0x01, 0x30,
+	0x02, 0x1b, 0xf6, 0x7c,
+	0x08, 0x5d, 0x04, 0x7d,
+	0x03, 0x68, 0x00, 0x37,
+	0x01, 0x84, 0x09, 0x07,
+	0x80, 0x1b, 0x10, 0x7d,
+	0x80, 0x84, 0x11, 0x6d,
+	0xff, 0x85, 0x0b, 0x1b,
+	0xff, 0x86, 0x0d, 0x23,
+	0xff, 0x87, 0x0f, 0x23,
+	0xf8, 0x1b, 0x08, 0x0b,
+	0xff, 0xea, 0x06, 0x0b,
+	0x03, 0x68, 0x00, 0x37,
+	0x00, 0xe2, 0xd6, 0x58,
+	0x10, 0xea, 0x18, 0x00,
+	0xf9, 0xd9, 0xb2, 0x0d,
+	0x01, 0xd9, 0xb2, 0x05,
+	0x01, 0x52, 0x48, 0x31,
+	0x20, 0xa4, 0x3a, 0x7d,
+	0x20, 0x5b, 0x3a, 0x7d,
+	0x80, 0xf9, 0x48, 0x7d,
+	0x02, 0xea, 0xb4, 0x00,
+	0x11, 0x00, 0x00, 0x10,
+	0x04, 0x19, 0x54, 0x7d,
+	0xdf, 0x19, 0x32, 0x08,
+	0x60, 0x5b, 0x54, 0x6d,
+	0x01, 0x4c, 0x2e, 0x7d,
+	0x20, 0x19, 0x32, 0x00,
+	0x01, 0xd9, 0xb2, 0x05,
+	0x02, 0xea, 0xb4, 0x00,
+	0x01, 0xd9, 0xb2, 0x05,
+	0x10, 0x5b, 0x4c, 0x6d,
+	0x08, 0x5b, 0x56, 0x6d,
+	0x20, 0x5b, 0x46, 0x6d,
+	0x02, 0x5b, 0x76, 0x6d,
+	0x0e, 0xea, 0x6e, 0x59,
+	0x0e, 0xea, 0x04, 0x00,
+	0x80, 0xf9, 0x36, 0x6d,
+	0xdf, 0x5c, 0xb8, 0x08,
+	0x01, 0xd9, 0xb2, 0x05,
+	0x01, 0xa4, 0x37, 0x6e,
+	0x00, 0xe2, 0x7c, 0x5c,
+	0x00, 0xe2, 0x80, 0x5d,
+	0x01, 0x90, 0x21, 0x1b,
+	0x01, 0xd9, 0xb2, 0x05,
+	0x00, 0xe2, 0x66, 0x5b,
+	0xf3, 0x96, 0xd5, 0x19,
+	0x00, 0xe2, 0x64, 0x55,
+	0x80, 0x96, 0x65, 0x6d,
+	0x0f, 0xea, 0x6e, 0x59,
+	0x0f, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x6c, 0x45,
+	0x04, 0x8c, 0xe1, 0x30,
+	0x01, 0xea, 0xf2, 0x00,
+	0x02, 0xea, 0x36, 0x00,
+	0xa8, 0xea, 0x32, 0x00,
+	0xff, 0x97, 0x73, 0x7d,
+	0x14, 0xea, 0x6e, 0x59,
+	0x14, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0xe2, 0x5d,
+	0x01, 0xd9, 0xb2, 0x05,
+	0x09, 0x80, 0xe1, 0x30,
+	0x02, 0xea, 0x36, 0x00,
+	0xa8, 0xea, 0x32, 0x00,
+	0x00, 0xe2, 0xda, 0x5d,
+	0x01, 0xd9, 0xb2, 0x05,
+	0x02, 0xa6, 0x90, 0x7d,
+	0x00, 0xe2, 0x60, 0x59,
+	0x20, 0x5b, 0x9e, 0x6d,
+	0xfc, 0x42, 0x8a, 0x7d,
+	0x10, 0x40, 0x8c, 0x6d,
+	0x20, 0x4d, 0x8e, 0x7d,
+	0x08, 0x5d, 0x9e, 0x6d,
+	0x02, 0xa6, 0x24, 0x6c,
+	0x00, 0xe2, 0x60, 0x59,
+	0x20, 0x5b, 0x9e, 0x6d,
+	0x01, 0x1b, 0xbe, 0x6d,
+	0xfc, 0x42, 0x9a, 0x7d,
+	0x10, 0x40, 0x9c, 0x6d,
+	0x20, 0x4d, 0x84, 0x78,
+	0x08, 0x5d, 0x84, 0x78,
+	0x02, 0x19, 0x32, 0x00,
+	0x01, 0x5b, 0x40, 0x31,
+	0x00, 0xe2, 0xfe, 0x5c,
+	0x00, 0xe2, 0xe0, 0x5b,
+	0x20, 0xea, 0xb6, 0x00,
+	0x00, 0xe2, 0x1e, 0x5c,
+	0x20, 0x5c, 0xb8, 0x00,
+	0x04, 0x19, 0xb4, 0x6d,
+	0x01, 0x1a, 0xb4, 0x6d,
+	0x00, 0xe2, 0x60, 0x59,
+	0x01, 0x1a, 0x84, 0x78,
+	0x80, 0xf9, 0xf2, 0x01,
+	0x20, 0xa0, 0x18, 0x7e,
+	0xff, 0x90, 0x21, 0x1b,
+	0x08, 0x92, 0x77, 0x6b,
+	0x02, 0xea, 0xb4, 0x04,
+	0x01, 0xa4, 0x49, 0x03,
+	0x40, 0x5b, 0xce, 0x6d,
+	0x00, 0xe2, 0x60, 0x59,
+	0x40, 0x5b, 0xce, 0x6d,
+	0x04, 0x5d, 0x38, 0x7e,
+	0x01, 0x1a, 0x38, 0x7e,
+	0x20, 0x4d, 0x84, 0x78,
+	0x40, 0x5b, 0x18, 0x7e,
+	0x04, 0x5d, 0x38, 0x7e,
+	0x01, 0x1a, 0x38, 0x7e,
+	0x80, 0xf9, 0xf2, 0x01,
+	0xff, 0x90, 0x21, 0x1b,
+	0x08, 0x92, 0x77, 0x6b,
+	0x02, 0xea, 0xb4, 0x04,
+	0x00, 0xe2, 0x60, 0x59,
+	0x01, 0x1b, 0x84, 0x78,
+	0x80, 0xf9, 0xf2, 0x01,
+	0x02, 0xea, 0xb4, 0x04,
+	0x00, 0xe2, 0x60, 0x59,
+	0x01, 0x1b, 0xf6, 0x6d,
+	0x40, 0x5b, 0x04, 0x7e,
+	0x01, 0x1b, 0xf6, 0x6d,
+	0x02, 0x19, 0x32, 0x00,
+	0x01, 0x1a, 0x84, 0x78,
+	0x80, 0xf9, 0xf2, 0x01,
+	0xff, 0xea, 0x10, 0x03,
+	0x08, 0x92, 0x25, 0x03,
+	0x00, 0xe2, 0x76, 0x43,
+	0x01, 0x1a, 0x00, 0x7e,
+	0x40, 0x5b, 0xfc, 0x7d,
+	0x01, 0x1a, 0xea, 0x6d,
+	0xfc, 0x42, 0x84, 0x78,
+	0x01, 0x1a, 0x04, 0x6e,
+	0x10, 0xea, 0x6e, 0x59,
+	0x10, 0xea, 0x04, 0x00,
+	0xfc, 0x42, 0x84, 0x78,
+	0x10, 0x40, 0x0a, 0x6e,
+	0x20, 0x4d, 0x84, 0x78,
+	0x40, 0x5b, 0xea, 0x6d,
+	0x01, 0x1a, 0x84, 0x78,
+	0x01, 0x90, 0x21, 0x1b,
+	0x30, 0x3f, 0xc0, 0x09,
+	0x30, 0xe0, 0x84, 0x60,
+	0x40, 0x4b, 0x84, 0x68,
+	0xff, 0xea, 0x52, 0x01,
+	0xee, 0x00, 0x20, 0x6e,
+	0x80, 0xf9, 0xf2, 0x01,
+	0xff, 0x90, 0x21, 0x1b,
+	0x02, 0xea, 0xb4, 0x00,
+	0x20, 0xea, 0x9a, 0x00,
+	0x04, 0x41, 0x26, 0x7e,
+	0x08, 0xea, 0x98, 0x00,
+	0x08, 0x57, 0xae, 0x00,
+	0xf3, 0x42, 0x30, 0x6e,
+	0x12, 0xea, 0x6e, 0x59,
+	0x12, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x28, 0x42,
+	0x0d, 0xea, 0x6e, 0x59,
+	0x0d, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x28, 0x42,
+	0x01, 0x90, 0x21, 0x1b,
+	0x11, 0xea, 0x6e, 0x59,
+	0x11, 0xea, 0x04, 0x00,
+	0x00, 0xe2, 0x66, 0x5b,
+	0x08, 0x5a, 0xb4, 0x00,
+	0x00, 0xe2, 0x5e, 0x5e,
+	0xa8, 0xea, 0x32, 0x00,
+	0x00, 0xe2, 0x60, 0x59,
+	0x80, 0x1a, 0x4c, 0x7e,
+	0x00, 0xe2, 0x5e, 0x5e,
+	0x80, 0x19, 0x32, 0x00,
+	0x40, 0x5b, 0x52, 0x6e,
+	0x08, 0x5a, 0x52, 0x7e,
+	0x20, 0x4d, 0x84, 0x78,
+	0x02, 0x84, 0x09, 0x03,
+	0x40, 0x5b, 0x18, 0x7e,
+	0xff, 0x90, 0x21, 0x1b,
+	0x80, 0xf9, 0xf2, 0x01,
+	0x08, 0x92, 0x77, 0x6b,
+	0x02, 0xea, 0xb4, 0x04,
+	0x01, 0x40, 0xe1, 0x30,
+	0x05, 0x41, 0xe3, 0x98,
+	0x01, 0xe0, 0xf4, 0x31,
+	0xff, 0xea, 0xc0, 0x09,
+	0x00, 0x42, 0xe5, 0x20,
+	0x00, 0x43, 0xe7, 0x20,
+	0x01, 0xfa, 0xc0, 0x31,
+	0x04, 0xea, 0xe8, 0x30,
+	0xff, 0xea, 0xf0, 0x08,
+	0x02, 0xea, 0xf2, 0x00,
+	0xff, 0xea, 0xf4, 0x0c
+};
+
+typedef int ahd_patch_func_t (struct ahd_softc *ahd);
+static ahd_patch_func_t ahd_patch23_func;
+
+static int
+ahd_patch23_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch22_func;
+
+static int
+ahd_patch22_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0);
+}
+
+static ahd_patch_func_t ahd_patch21_func;
+
+static int
+ahd_patch21_func(struct ahd_softc *ahd)
+{
+	return ((ahd->flags & AHD_INITIATORROLE) != 0);
+}
+
+static ahd_patch_func_t ahd_patch20_func;
+
+static int
+ahd_patch20_func(struct ahd_softc *ahd)
+{
+	return ((ahd->flags & AHD_TARGETROLE) != 0);
+}
+
+static ahd_patch_func_t ahd_patch19_func;
+
+static int
+ahd_patch19_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch18_func;
+
+static int
+ahd_patch18_func(struct ahd_softc *ahd)
+{
+	return ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0);
+}
+
+static ahd_patch_func_t ahd_patch17_func;
+
+static int
+ahd_patch17_func(struct ahd_softc *ahd)
+{
+	return ((ahd->flags & AHD_39BIT_ADDRESSING) != 0);
+}
+
+static ahd_patch_func_t ahd_patch16_func;
+
+static int
+ahd_patch16_func(struct ahd_softc *ahd)
+{
+	return ((ahd->flags & AHD_64BIT_ADDRESSING) != 0);
+}
+
+static ahd_patch_func_t ahd_patch15_func;
+
+static int
+ahd_patch15_func(struct ahd_softc *ahd)
+{
+	return ((ahd->features & AHD_NEW_DFCNTRL_OPTS) == 0);
+}
+
+static ahd_patch_func_t ahd_patch14_func;
+
+static int
+ahd_patch14_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch13_func;
+
+static int
+ahd_patch13_func(struct ahd_softc *ahd)
+{
+	return ((ahd->features & AHD_RTI) == 0);
+}
+
+static ahd_patch_func_t ahd_patch12_func;
+
+static int
+ahd_patch12_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_EARLY_REQ_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch11_func;
+
+static int
+ahd_patch11_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_BUSFREEREV_BUG) == 0);
+}
+
+static ahd_patch_func_t ahd_patch10_func;
+
+static int
+ahd_patch10_func(struct ahd_softc *ahd)
+{
+	return ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch9_func;
+
+static int
+ahd_patch9_func(struct ahd_softc *ahd)
+{
+	return ((ahd->features & AHD_FAST_CDB_DELIVERY) != 0);
+}
+
+static ahd_patch_func_t ahd_patch8_func;
+
+static int
+ahd_patch8_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch7_func;
+
+static int
+ahd_patch7_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch6_func;
+
+static int
+ahd_patch6_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_NONPACKFIFO_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch5_func;
+
+static int
+ahd_patch5_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch4_func;
+
+static int
+ahd_patch4_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_PKT_LUN_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch3_func;
+
+static int
+ahd_patch3_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_FAINT_LED_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch2_func;
+
+static int
+ahd_patch2_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_SET_MODE_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch1_func;
+
+static int
+ahd_patch1_func(struct ahd_softc *ahd)
+{
+	return ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0);
+}
+
+static ahd_patch_func_t ahd_patch0_func;
+
+static int
+ahd_patch0_func(struct ahd_softc *ahd)
+{
+	return (0);
+}
+
+static const struct patch {
+	ahd_patch_func_t		*patch_func;
+	uint32_t		 begin		:10,
+				 skip_instr	:10,
+				 skip_patch	:12;
+} patches[] = {
+	{ ahd_patch1_func, 0, 3, 3 },
+	{ ahd_patch1_func, 1, 1, 2 },
+	{ ahd_patch0_func, 2, 1, 1 },
+	{ ahd_patch1_func, 3, 3, 3 },
+	{ ahd_patch1_func, 4, 1, 2 },
+	{ ahd_patch0_func, 5, 1, 1 },
+	{ ahd_patch2_func, 6, 1, 2 },
+	{ ahd_patch0_func, 7, 1, 1 },
+	{ ahd_patch3_func, 36, 5, 1 },
+	{ ahd_patch2_func, 45, 1, 2 },
+	{ ahd_patch0_func, 46, 1, 1 },
+	{ ahd_patch1_func, 53, 1, 2 },
+	{ ahd_patch0_func, 54, 1, 1 },
+	{ ahd_patch2_func, 59, 1, 2 },
+	{ ahd_patch0_func, 60, 1, 1 },
+	{ ahd_patch2_func, 63, 1, 2 },
+	{ ahd_patch0_func, 64, 1, 1 },
+	{ ahd_patch2_func, 67, 1, 2 },
+	{ ahd_patch0_func, 68, 1, 1 },
+	{ ahd_patch4_func, 115, 1, 1 },
+	{ ahd_patch2_func, 180, 3, 1 },
+	{ ahd_patch1_func, 183, 2, 1 },
+	{ ahd_patch5_func, 185, 1, 1 },
+	{ ahd_patch2_func, 194, 1, 2 },
+	{ ahd_patch0_func, 195, 1, 1 },
+	{ ahd_patch6_func, 196, 2, 2 },
+	{ ahd_patch0_func, 198, 6, 3 },
+	{ ahd_patch2_func, 201, 1, 2 },
+	{ ahd_patch0_func, 202, 1, 1 },
+	{ ahd_patch2_func, 205, 1, 2 },
+	{ ahd_patch0_func, 206, 1, 1 },
+	{ ahd_patch3_func, 208, 1, 1 },
+	{ ahd_patch7_func, 209, 3, 1 },
+	{ ahd_patch3_func, 218, 1, 1 },
+	{ ahd_patch5_func, 219, 16, 2 },
+	{ ahd_patch0_func, 235, 1, 1 },
+	{ ahd_patch8_func, 260, 2, 1 },
+	{ ahd_patch1_func, 264, 1, 2 },
+	{ ahd_patch0_func, 265, 1, 1 },
+	{ ahd_patch7_func, 268, 3, 1 },
+	{ ahd_patch1_func, 283, 1, 2 },
+	{ ahd_patch0_func, 284, 1, 1 },
+	{ ahd_patch1_func, 287, 1, 2 },
+	{ ahd_patch0_func, 288, 1, 1 },
+	{ ahd_patch2_func, 291, 1, 2 },
+	{ ahd_patch0_func, 292, 1, 1 },
+	{ ahd_patch9_func, 305, 2, 2 },
+	{ ahd_patch0_func, 307, 1, 1 },
+	{ ahd_patch1_func, 349, 1, 2 },
+	{ ahd_patch0_func, 350, 1, 1 },
+	{ ahd_patch2_func, 358, 1, 2 },
+	{ ahd_patch0_func, 359, 1, 1 },
+	{ ahd_patch2_func, 362, 1, 2 },
+	{ ahd_patch0_func, 363, 1, 1 },
+	{ ahd_patch1_func, 369, 1, 2 },
+	{ ahd_patch0_func, 370, 1, 1 },
+	{ ahd_patch1_func, 372, 1, 2 },
+	{ ahd_patch0_func, 373, 1, 1 },
+	{ ahd_patch10_func, 392, 1, 1 },
+	{ ahd_patch10_func, 395, 1, 1 },
+	{ ahd_patch10_func, 397, 1, 1 },
+	{ ahd_patch10_func, 409, 1, 1 },
+	{ ahd_patch1_func, 419, 1, 2 },
+	{ ahd_patch0_func, 420, 1, 1 },
+	{ ahd_patch1_func, 422, 1, 2 },
+	{ ahd_patch0_func, 423, 1, 1 },
+	{ ahd_patch1_func, 431, 1, 2 },
+	{ ahd_patch0_func, 432, 1, 1 },
+	{ ahd_patch2_func, 445, 1, 2 },
+	{ ahd_patch0_func, 446, 1, 1 },
+	{ ahd_patch11_func, 482, 1, 1 },
+	{ ahd_patch1_func, 490, 1, 2 },
+	{ ahd_patch0_func, 491, 1, 1 },
+	{ ahd_patch2_func, 503, 1, 2 },
+	{ ahd_patch0_func, 504, 1, 1 },
+	{ ahd_patch12_func, 507, 6, 2 },
+	{ ahd_patch0_func, 513, 1, 1 },
+	{ ahd_patch13_func, 534, 7, 1 },
+	{ ahd_patch14_func, 543, 1, 1 },
+	{ ahd_patch15_func, 552, 1, 1 },
+	{ ahd_patch16_func, 553, 1, 2 },
+	{ ahd_patch0_func, 554, 1, 1 },
+	{ ahd_patch17_func, 557, 1, 1 },
+	{ ahd_patch16_func, 558, 1, 1 },
+	{ ahd_patch18_func, 569, 1, 2 },
+	{ ahd_patch0_func, 570, 1, 1 },
+	{ ahd_patch1_func, 589, 1, 2 },
+	{ ahd_patch0_func, 590, 1, 1 },
+	{ ahd_patch1_func, 593, 1, 2 },
+	{ ahd_patch0_func, 594, 1, 1 },
+	{ ahd_patch2_func, 599, 1, 2 },
+	{ ahd_patch0_func, 600, 1, 1 },
+	{ ahd_patch2_func, 604, 1, 2 },
+	{ ahd_patch0_func, 605, 1, 1 },
+	{ ahd_patch1_func, 606, 1, 2 },
+	{ ahd_patch0_func, 607, 1, 1 },
+	{ ahd_patch2_func, 618, 1, 2 },
+	{ ahd_patch0_func, 619, 1, 1 },
+	{ ahd_patch19_func, 623, 1, 1 },
+	{ ahd_patch20_func, 628, 1, 1 },
+	{ ahd_patch21_func, 629, 2, 1 },
+	{ ahd_patch20_func, 633, 1, 2 },
+	{ ahd_patch0_func, 634, 1, 1 },
+	{ ahd_patch2_func, 637, 1, 2 },
+	{ ahd_patch0_func, 638, 1, 1 },
+	{ ahd_patch2_func, 653, 1, 2 },
+	{ ahd_patch0_func, 654, 1, 1 },
+	{ ahd_patch13_func, 655, 14, 1 },
+	{ ahd_patch1_func, 673, 1, 2 },
+	{ ahd_patch0_func, 674, 1, 1 },
+	{ ahd_patch13_func, 675, 1, 1 },
+	{ ahd_patch1_func, 687, 1, 2 },
+	{ ahd_patch0_func, 688, 1, 1 },
+	{ ahd_patch1_func, 695, 1, 2 },
+	{ ahd_patch0_func, 696, 1, 1 },
+	{ ahd_patch19_func, 719, 1, 1 },
+	{ ahd_patch19_func, 757, 1, 1 },
+	{ ahd_patch1_func, 768, 1, 2 },
+	{ ahd_patch0_func, 769, 1, 1 },
+	{ ahd_patch7_func, 785, 3, 1 },
+	{ ahd_patch1_func, 789, 1, 2 },
+	{ ahd_patch0_func, 790, 1, 1 },
+	{ ahd_patch1_func, 792, 1, 2 },
+	{ ahd_patch0_func, 793, 1, 1 },
+	{ ahd_patch1_func, 796, 1, 2 },
+	{ ahd_patch0_func, 797, 1, 1 },
+	{ ahd_patch22_func, 799, 1, 2 },
+	{ ahd_patch0_func, 800, 2, 1 },
+	{ ahd_patch23_func, 803, 4, 2 },
+	{ ahd_patch0_func, 807, 1, 1 },
+	{ ahd_patch23_func, 815, 11, 1 }
+};
+
+static const struct cs {
+	uint16_t	begin;
+	uint16_t	end;
+} critical_sections[] = {
+	{ 17, 30 },
+	{ 47, 58 },
+	{ 61, 63 },
+	{ 65, 66 },
+	{ 72, 92 },
+	{ 110, 142 },
+	{ 143, 180 },
+	{ 185, 193 },
+	{ 218, 274 },
+	{ 435, 443 },
+	{ 453, 455 },
+	{ 458, 467 },
+	{ 719, 749 },
+	{ 759, 763 }
+};
+
+static const int num_critical_sections = sizeof(critical_sections)
+				       / sizeof(*critical_sections);
diff --git a/drivers/scsi/aic7xxx/aic7xxx.h b/drivers/scsi/aic7xxx/aic7xxx.h
new file mode 100644
index 0000000..f695774
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx.h
@@ -0,0 +1,1284 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#85 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC7XXX_H_
+#define _AIC7XXX_H_
+
+/* Register Definitions */
+#include "aic7xxx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahc_platform_data;
+struct scb_platform_data;
+struct seeprom_descriptor;
+
+/****************************** Useful Macros *********************************/
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD	(~0)
+
+#define SCSIID_TARGET(ahc, scsiid) \
+	(((scsiid) & ((((ahc)->features & AHC_TWIN) != 0) ? TWIN_TID : TID)) \
+	>> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid) \
+	((scsiid) & OID)
+#define SCSIID_CHANNEL(ahc, scsiid) \
+	((((ahc)->features & AHC_TWIN) != 0) \
+        ? ((((scsiid) & TWIN_CHNLB) != 0) ? 'B' : 'A') \
+       : 'A')
+#define	SCB_IS_SCSIBUS_B(ahc, scb) \
+	(SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid) == 'B')
+#define	SCB_GET_OUR_ID(scb) \
+	SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define	SCB_GET_TARGET(ahc, scb) \
+	SCSIID_TARGET((ahc), (scb)->hscb->scsiid)
+#define	SCB_GET_CHANNEL(ahc, scb) \
+	SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid)
+#define	SCB_GET_LUN(scb) \
+	((scb)->hscb->lun & LID)
+#define SCB_GET_TARGET_OFFSET(ahc, scb)	\
+	(SCB_GET_TARGET(ahc, scb) + (SCB_IS_SCSIBUS_B(ahc, scb) ? 8 : 0))
+#define SCB_GET_TARGET_MASK(ahc, scb) \
+	(0x01 << (SCB_GET_TARGET_OFFSET(ahc, scb)))
+#ifdef AHC_DEBUG
+#define SCB_IS_SILENT(scb)					\
+	((ahc_debug & AHC_SHOW_MASKED_ERRORS) == 0		\
+      && (((scb)->flags & SCB_SILENT) != 0))
+#else
+#define SCB_IS_SILENT(scb)					\
+	(((scb)->flags & SCB_SILENT) != 0)
+#endif
+#define TCL_TARGET_OFFSET(tcl) \
+	((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+	(tcl & (AHC_NUM_LUNS - 1))
+#define BUILD_TCL(scsiid, lun) \
+	((lun) | (((scsiid) & TID) << 4))
+
+#ifndef	AHC_TARGET_MODE
+#undef	AHC_TMODE_ENABLE
+#define	AHC_TMODE_ENABLE 0
+#endif
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHC_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in SPI3.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but it is doubtful this driver will ever support IUTs.
+ */
+#define AHC_NUM_LUNS 64
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHC_MAXTRANSFER_SIZE	 0x00ffffff	/* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound.  Controllers vary in the number
+ * they actually support.
+ */
+#define AHC_SCB_MAX	255
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information.  Although
+ * the space for SCBs on the host adapter varies by model, the driver will
+ * page the SCBs between host and controller memory as needed.  We are limited
+ * to 253 because:
+ * 	1) The 8bit nature of the RISC engine holds us to an 8bit value.
+ * 	2) We reserve one value, 255, to represent the invalid element.
+ *	3) Our input queue scheme requires one SCB to always be reserved
+ *	   in advance of queuing any SCBs.  This takes us down to 254.
+ *	4) To handle our output queue correctly on machines that only
+ * 	   support 32bit stores, we must clear the array 4 bytes at a
+ *	   time.  To avoid colliding with a DMA write from the sequencer,
+ *	   we must be sure that 4 slots are empty when we write to clear
+ *	   the queue.  This reduces us to 253 SCBs: 1 that just completed
+ *	   and the known three additional empty slots in the queue that
+ *	   precede it.
+ */
+#define AHC_MAX_QUEUE	253
+
+/*
+ * The maximum amount of SCB storage we allocate in host memory.  This
+ * number should reflect the 1 additional SCB we require to handle our
+ * qinfifo mechanism.
+ */
+#define AHC_SCB_MAX_ALLOC (AHC_MAX_QUEUE+1)
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHC_TMODE_CMDS	256
+
+/* Reset line assertion time in us */
+#define AHC_BUSRESET_DELAY	25
+
+/******************* Chip Characteristics/Operating Settings  *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+	AHC_NONE	= 0x0000,
+	AHC_CHIPID_MASK	= 0x00FF,
+	AHC_AIC7770	= 0x0001,
+	AHC_AIC7850	= 0x0002,
+	AHC_AIC7855	= 0x0003,
+	AHC_AIC7859	= 0x0004,
+	AHC_AIC7860	= 0x0005,
+	AHC_AIC7870	= 0x0006,
+	AHC_AIC7880	= 0x0007,
+	AHC_AIC7895	= 0x0008,
+	AHC_AIC7895C	= 0x0009,
+	AHC_AIC7890	= 0x000a,
+	AHC_AIC7896	= 0x000b,
+	AHC_AIC7892	= 0x000c,
+	AHC_AIC7899	= 0x000d,
+	AHC_VL		= 0x0100,	/* Bus type VL */
+	AHC_EISA	= 0x0200,	/* Bus type EISA */
+	AHC_PCI		= 0x0400,	/* Bus type PCI */
+	AHC_BUS_MASK	= 0x0F00
+} ahc_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+	AHC_FENONE	= 0x00000,
+	AHC_ULTRA	= 0x00001,	/* Supports 20MHz Transfers */
+	AHC_ULTRA2	= 0x00002,	/* Supports 40MHz Transfers */
+	AHC_WIDE  	= 0x00004,	/* Wide Channel */
+	AHC_TWIN	= 0x00008,	/* Twin Channel */
+	AHC_MORE_SRAM	= 0x00010,	/* 80 bytes instead of 64 */
+	AHC_CMD_CHAN	= 0x00020,	/* Has a Command DMA Channel */
+	AHC_QUEUE_REGS	= 0x00040,	/* Has Queue management registers */
+	AHC_SG_PRELOAD	= 0x00080,	/* Can perform auto-SG preload */
+	AHC_SPIOCAP	= 0x00100,	/* Has a Serial Port I/O Cap Register */
+	AHC_MULTI_TID	= 0x00200,	/* Has bitmask of TIDs for select-in */
+	AHC_HS_MAILBOX	= 0x00400,	/* Has HS_MAILBOX register */
+	AHC_DT		= 0x00800,	/* Double Transition transfers */
+	AHC_NEW_TERMCTL	= 0x01000,	/* Newer termination scheme */
+	AHC_MULTI_FUNC	= 0x02000,	/* Multi-Function Twin Channel Device */
+	AHC_LARGE_SCBS	= 0x04000,	/* 64byte SCBs */
+	AHC_AUTORATE	= 0x08000,	/* Automatic update of SCSIRATE/OFFSET*/
+	AHC_AUTOPAUSE	= 0x10000,	/* Automatic pause on register access */
+	AHC_TARGETMODE	= 0x20000,	/* Has tested target mode support */
+	AHC_MULTIROLE	= 0x40000,	/* Space for two roles at a time */
+	AHC_REMOVABLE	= 0x80000,	/* Hot-Swap supported */
+	AHC_HVD		= 0x100000,	/* HVD rather than SE */
+	AHC_AIC7770_FE	= AHC_FENONE,
+	/*
+	 * The real 7850 does not support Ultra modes, but there are
+	 * several cards that use the generic 7850 PCI ID even though
+	 * they are using an Ultra capable chip (7859/7860).  We start
+	 * out with the AHC_ULTRA feature set and then check the DEVSTATUS
+	 * register to determine if the capability is really present.
+	 */
+	AHC_AIC7850_FE	= AHC_SPIOCAP|AHC_AUTOPAUSE|AHC_TARGETMODE|AHC_ULTRA,
+	AHC_AIC7860_FE	= AHC_AIC7850_FE,
+	AHC_AIC7870_FE	= AHC_TARGETMODE|AHC_AUTOPAUSE,
+	AHC_AIC7880_FE	= AHC_AIC7870_FE|AHC_ULTRA,
+	/*
+	 * Although we have space for both the initiator and
+	 * target roles on ULTRA2 chips, we currently disable
+	 * the initiator role to allow multi-scsi-id target mode
+	 * configurations.  We can only respond on the same SCSI
+	 * ID as our initiator role if we allow initiator operation.
+	 * At some point, we should add a configuration knob to
+	 * allow both roles to be loaded.
+	 */
+	AHC_AIC7890_FE	= AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2
+			  |AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_MULTI_TID
+			  |AHC_HS_MAILBOX|AHC_NEW_TERMCTL|AHC_LARGE_SCBS
+			  |AHC_TARGETMODE,
+	AHC_AIC7892_FE	= AHC_AIC7890_FE|AHC_DT|AHC_AUTORATE|AHC_AUTOPAUSE,
+	AHC_AIC7895_FE	= AHC_AIC7880_FE|AHC_MORE_SRAM|AHC_AUTOPAUSE
+			  |AHC_CMD_CHAN|AHC_MULTI_FUNC|AHC_LARGE_SCBS,
+	AHC_AIC7895C_FE	= AHC_AIC7895_FE|AHC_MULTI_TID,
+	AHC_AIC7896_FE	= AHC_AIC7890_FE|AHC_MULTI_FUNC,
+	AHC_AIC7899_FE	= AHC_AIC7892_FE|AHC_MULTI_FUNC
+} ahc_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+	AHC_BUGNONE		= 0x00,
+	/*
+	 * On all chips prior to the U2 product line,
+	 * the WIDEODD S/G segment feature does not
+	 * work during scsi->HostBus transfers.
+	 */
+	AHC_TMODE_WIDEODD_BUG	= 0x01,
+	/*
+	 * On the aic7890/91 Rev 0 chips, the autoflush
+	 * feature does not work.  A manual flush of
+	 * the DMA FIFO is required.
+	 */
+	AHC_AUTOFLUSH_BUG	= 0x02,
+	/*
+	 * On many chips, cacheline streaming does not work.
+	 */
+	AHC_CACHETHEN_BUG	= 0x04,
+	/*
+	 * On the aic7896/97 chips, cacheline
+	 * streaming must be enabled.
+	 */
+	AHC_CACHETHEN_DIS_BUG	= 0x08,
+	/*
+	 * PCI 2.1 Retry failure on non-empty data fifo.
+	 */
+	AHC_PCI_2_1_RETRY_BUG	= 0x10,
+	/*
+	 * Controller does not handle cacheline residuals
+	 * properly on S/G segments if PCI MWI instructions
+	 * are allowed.
+	 */
+	AHC_PCI_MWI_BUG		= 0x20,
+	/*
+	 * An SCB upload using the SCB channel's
+	 * auto array entry copy feature may 
+	 * corrupt data.  This appears to only
+	 * occur on 66MHz systems.
+	 */
+	AHC_SCBCHAN_UPLOAD_BUG	= 0x40
+} ahc_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+	AHC_FNONE	      = 0x000,
+	AHC_PRIMARY_CHANNEL   = 0x003,  /*
+					 * The channel that should
+					 * be probed first.
+					 */
+	AHC_USEDEFAULTS	      = 0x004,  /*
+					 * For cards without an seeprom
+					 * or a BIOS to initialize the chip's
+					 * SRAM, we use the default target
+					 * settings.
+					 */
+	AHC_SEQUENCER_DEBUG   = 0x008,
+	AHC_SHARED_SRAM	      = 0x010,
+	AHC_LARGE_SEEPROM     = 0x020,  /* Uses C56_66 not C46 */
+	AHC_RESET_BUS_A	      = 0x040,
+	AHC_RESET_BUS_B	      = 0x080,
+	AHC_EXTENDED_TRANS_A  = 0x100,
+	AHC_EXTENDED_TRANS_B  = 0x200,
+	AHC_TERM_ENB_A	      = 0x400,
+	AHC_TERM_ENB_B	      = 0x800,
+	AHC_INITIATORROLE     = 0x1000,  /*
+					  * Allow initiator operations on
+					  * this controller.
+					  */
+	AHC_TARGETROLE	      = 0x2000,  /*
+					  * Allow target operations on this
+					  * controller.
+					  */
+	AHC_NEWEEPROM_FMT     = 0x4000,
+	AHC_TQINFIFO_BLOCKED  = 0x10000,  /* Blocked waiting for ATIOs */
+	AHC_INT50_SPEEDFLEX   = 0x20000,  /*
+					   * Internal 50pin connector
+					   * sits behind an aic3860
+					   */
+	AHC_SCB_BTT	      = 0x40000,  /*
+					   * The busy targets table is
+					   * stored in SCB space rather
+					   * than SRAM.
+					   */
+	AHC_BIOS_ENABLED      = 0x80000,
+	AHC_ALL_INTERRUPTS    = 0x100000,
+	AHC_PAGESCBS	      = 0x400000,  /* Enable SCB paging */
+	AHC_EDGE_INTERRUPT    = 0x800000,  /* Device uses edge triggered ints */
+	AHC_39BIT_ADDRESSING  = 0x1000000, /* Use 39 bit addressing scheme. */
+	AHC_LSCBS_ENABLED     = 0x2000000, /* 64Byte SCBs enabled */
+	AHC_SCB_CONFIG_USED   = 0x4000000, /* No SEEPROM but SCB2 had info. */
+	AHC_NO_BIOS_INIT      = 0x8000000, /* No BIOS left over settings. */
+	AHC_DISABLE_PCI_PERR  = 0x10000000,
+	AHC_HAS_TERM_LOGIC    = 0x20000000
+} ahc_flag;
+
+/************************* Hardware  SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory.  The SCB
+ * consists of a "hardware SCB" mirroring the fields available on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the  cdb) to be executed.  After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role.  See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target.  The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally (non-zero status byte).
+ */
+struct status_pkt {
+	uint32_t residual_datacnt;	/* Residual in the current S/G seg */
+	uint32_t residual_sg_ptr;	/* The next S/G for this transfer */
+	uint8_t	 scsi_status;		/* Standard SCSI status byte */
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+	uint32_t residual_datacnt;	/* Residual in the current S/G seg */
+	uint32_t residual_sg_ptr;	/* The next S/G for this transfer */
+	uint8_t  scsi_status;		/* SCSI status to give to initiator */
+	uint8_t  target_phases;		/* Bitmap of phases to execute */
+	uint8_t  data_phase;		/* Data-In or Data-Out */
+	uint8_t  initiator_tag;		/* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/	union {
+		/*
+		 * If the cdb is 12 bytes or less, we embed it directly
+		 * in the SCB.  For longer cdbs, we embed the address
+		 * of the cdb payload as seen by the chip and a DMA
+		 * is used to pull it in.
+		 */
+		uint8_t	 cdb[12];
+		uint32_t cdb_ptr;
+		struct	 status_pkt status;
+		struct	 target_data tdata;
+	} shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer.  The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag.  Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields).  If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur.  Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields.  After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced.  After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set.  If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete.  If the
+ * sequencer determines that there is a residual in the tranfer, it
+ * will set the SG_RESID_VALID flag in sgptr and dma the scb back into
+ * host memory.  To sumarize:
+ *
+ * Sequencer:
+ *	o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ *	  or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ *	o We are transferring the last segment if residual_datacnt has
+ *	  the SG_LAST_SEG flag set.
+ *
+ * Host:
+ *	o A residual has occurred if a completed scb has the
+ *	  SG_RESID_VALID flag set.
+ *
+ *	o residual_sgptr and sgptr refer to the "next" sg entry
+ *	  and so may point beyond the last valid sg entry for the
+ *	  transfer.
+ */ 
+/*12*/	uint32_t dataptr;
+/*16*/	uint32_t datacnt;		/*
+					 * Byte 3 (numbered from 0) of
+					 * the datacnt is really the
+					 * 4th byte in that data address.
+					 */
+/*20*/	uint32_t sgptr;
+#define SG_PTR_MASK	0xFFFFFFF8
+/*24*/	uint8_t  control;	/* See SCB_CONTROL in aic7xxx.reg for details */
+/*25*/	uint8_t  scsiid;	/* what to load in the SCSIID register */
+/*26*/	uint8_t  lun;
+/*27*/	uint8_t  tag;			/*
+					 * Index into our kernel SCB array.
+					 * Also used as the tag for tagged I/O
+					 */
+/*28*/	uint8_t  cdb_len;
+/*29*/	uint8_t  scsirate;		/* Value for SCSIRATE register */
+/*30*/	uint8_t  scsioffset;		/* Value for SCSIOFFSET register */
+/*31*/	uint8_t  next;			/*
+					 * Used for threading SCBs in the
+					 * "Waiting for Selection" and
+					 * "Disconnected SCB" lists down
+					 * in the sequencer.
+					 */
+/*32*/	uint8_t  cdb32[32];		/*
+					 * CDB storage for cdbs of size
+					 * 13->32.  We store them here
+					 * because hardware scbs are
+					 * allocated from DMA safe
+					 * memory so we are guaranteed
+					 * the controller can access
+					 * this data.
+					 */
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent.  Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transferred to the controller.
+ * The aic7xxx chips only support a 24bit length.  We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer.  This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahc_dma_seg {
+	uint32_t	addr;
+	uint32_t	len;
+#define	AHC_DMA_LAST_SEG	0x80000000
+#define	AHC_SG_HIGH_ADDR_MASK	0x7F000000
+#define	AHC_SG_LEN_MASK		0x00FFFFFF
+};
+
+struct sg_map_node {
+	bus_dmamap_t		 sg_dmamap;
+	dma_addr_t		 sg_physaddr;
+	struct ahc_dma_seg*	 sg_vaddr;
+	SLIST_ENTRY(sg_map_node) links;
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+	SCB_FREE		= 0x0000,
+	SCB_OTHERTCL_TIMEOUT	= 0x0002,/*
+					  * Another device was active
+					  * during the first timeout for
+					  * this SCB so we gave ourselves
+					  * an additional timeout period
+					  * in case it was hogging the
+					  * bus.
+				          */
+	SCB_DEVICE_RESET	= 0x0004,
+	SCB_SENSE		= 0x0008,
+	SCB_CDB32_PTR		= 0x0010,
+	SCB_RECOVERY_SCB	= 0x0020,
+	SCB_AUTO_NEGOTIATE	= 0x0040,/* Negotiate to achieve goal. */
+	SCB_NEGOTIATE		= 0x0080,/* Negotiation forced for command. */
+	SCB_ABORT		= 0x0100,
+	SCB_UNTAGGEDQ		= 0x0200,
+	SCB_ACTIVE		= 0x0400,
+	SCB_TARGET_IMMEDIATE	= 0x0800,
+	SCB_TRANSMISSION_ERROR	= 0x1000,/*
+					  * We detected a parity or CRC
+					  * error that has effected the
+					  * payload of the command.  This
+					  * flag is checked when normal
+					  * status is returned to catch
+					  * the case of a target not
+					  * responding to our attempt
+					  * to report the error.
+					  */
+	SCB_TARGET_SCB		= 0x2000,
+	SCB_SILENT		= 0x4000 /*
+					  * Be quiet about transmission type
+					  * errors.  They are expected and we
+					  * don't want to upset the user.  This
+					  * flag is typically used during DV.
+					  */
+} scb_flag;
+
+struct scb {
+	struct	hardware_scb	 *hscb;
+	union {
+		SLIST_ENTRY(scb)  sle;
+		TAILQ_ENTRY(scb)  tqe;
+	} links;
+	LIST_ENTRY(scb)		  pending_links;
+	ahc_io_ctx_t		  io_ctx;
+	struct ahc_softc	 *ahc_softc;
+	scb_flag		  flags;
+#ifndef __linux__
+	bus_dmamap_t		  dmamap;
+#endif
+	struct scb_platform_data *platform_data;
+	struct sg_map_node	 *sg_map;
+	struct ahc_dma_seg 	 *sg_list;
+	dma_addr_t		  sg_list_phys;
+	u_int			  sg_count;/* How full ahc_dma_seg is */
+};
+
+struct scb_data {
+	SLIST_HEAD(, scb) free_scbs;	/*
+					 * Pool of SCBs ready to be assigned
+					 * commands to execute.
+					 */
+	struct	scb *scbindex[256];	/*
+					 * Mapping from tag to SCB.
+					 * As tag identifiers are an
+					 * 8bit value, we provide space
+					 * for all possible tag values.
+					 * Any lookups to entries at or
+					 * above AHC_SCB_MAX_ALLOC will
+					 * always fail.
+					 */
+	struct	hardware_scb	*hscbs;	/* Array of hardware SCBs */
+	struct	scb *scbarray;		/* Array of kernel SCBs */
+	struct	scsi_sense_data *sense; /* Per SCB sense data */
+
+	/*
+	 * "Bus" addresses of our data structures.
+	 */
+	bus_dma_tag_t	 hscb_dmat;	/* dmat for our hardware SCB array */
+	bus_dmamap_t	 hscb_dmamap;
+	dma_addr_t	 hscb_busaddr;
+	bus_dma_tag_t	 sense_dmat;
+	bus_dmamap_t	 sense_dmamap;
+	dma_addr_t	 sense_busaddr;
+	bus_dma_tag_t	 sg_dmat;	/* dmat for our sg segments */
+	SLIST_HEAD(, sg_map_node) sg_maps;
+	uint8_t	numscbs;
+	uint8_t	maxhscbs;		/* Number of SCBs on the card */
+	uint8_t	init_level;		/*
+					 * How far we've initialized
+					 * this structure.
+					 */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection descriptor for select-in requests in target mode.
+ */
+struct target_cmd {
+	uint8_t scsiid;		/* Our ID and the initiator's ID */
+	uint8_t identify;	/* Identify message */
+	uint8_t bytes[22];	/* 
+				 * Bytes contains any additional message
+				 * bytes terminated by 0xFF.  The remainder
+				 * is the cdb to execute.
+				 */
+	uint8_t cmd_valid;	/*
+				 * When a command is complete, the firmware
+				 * will set cmd_valid to all bits set.
+				 * After the host has seen the command,
+				 * the bits are cleared.  This allows us
+				 * to just peek at host memory to determine
+				 * if more work is complete. cmd_valid is on
+				 * an 8 byte boundary to simplify setting
+				 * it on aic7880 hardware which only has
+				 * limited direct access to the DMA FIFO.
+				 */
+	uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHC_TMODE_EVENT_BUFFER_SIZE 8
+struct ahc_tmode_event {
+	uint8_t initiator_id;
+	uint8_t event_type;	/* MSG type or EVENT_TYPE_BUS_RESET */
+#define	EVENT_TYPE_BUS_RESET 0xFF
+	uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it.  Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHC_TARGET_MODE 
+struct ahc_tmode_lstate {
+	struct cam_path *path;
+	struct ccb_hdr_slist accept_tios;
+	struct ccb_hdr_slist immed_notifies;
+	struct ahc_tmode_event event_buffer[AHC_TMODE_EVENT_BUFFER_SIZE];
+	uint8_t event_r_idx;
+	uint8_t event_w_idx;
+};
+#else
+struct ahc_tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHC_TRANS_CUR		0x01	/* Modify current neogtiation status */
+#define AHC_TRANS_ACTIVE	0x03	/* Assume this target is on the bus */
+#define AHC_TRANS_GOAL		0x04	/* Modify negotiation goal */
+#define AHC_TRANS_USER		0x08	/* Modify user negotiation settings */
+
+#define AHC_WIDTH_UNKNOWN	0xFF
+#define AHC_PERIOD_UNKNOWN	0xFF
+#define AHC_OFFSET_UNKNOWN	0xFF
+#define AHC_PPR_OPTS_UNKNOWN	0xFF
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahc_transinfo {
+	uint8_t protocol_version;	/* SCSI Revision level */
+	uint8_t transport_version;	/* SPI Revision level */
+	uint8_t width;			/* Bus width */
+	uint8_t period;			/* Sync rate factor */
+	uint8_t offset;			/* Sync offset */
+	uint8_t ppr_options;		/* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahc_initiator_tinfo {
+	uint8_t scsirate;		/* Computed value for SCSIRATE reg */
+	struct ahc_transinfo curr;
+	struct ahc_transinfo goal;
+	struct ahc_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping.  For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct ahc_tmode_tstate {
+	struct ahc_tmode_lstate*	enabled_luns[AHC_NUM_LUNS];
+	struct ahc_initiator_tinfo	transinfo[AHC_NUM_TARGETS];
+
+	/*
+	 * Per initiator state bitmasks.
+	 */
+	uint16_t	 auto_negotiate;/* Auto Negotiation Required */
+	uint16_t	 ultraenb;	/* Using ultra sync rate  */
+	uint16_t	 discenable;	/* Disconnection allowed  */
+	uint16_t	 tagenable;	/* Tagged Queuing allowed */
+};
+
+/*
+ * Data structure for our table of allowed synchronous transfer rates.
+ */
+struct ahc_syncrate {
+	u_int sxfr_u2;	/* Value of the SXFR parameter for Ultra2+ Chips */
+	u_int sxfr;	/* Value of the SXFR parameter for <= Ultra Chips */
+#define		ULTRA_SXFR 0x100	/* Rate Requires Ultra Mode set */
+#define		ST_SXFR	   0x010	/* Rate Single Transition Only */
+#define		DT_SXFR	   0x040	/* Rate Double Transition Only */
+	uint8_t period; /* Period to send to SCSI target */
+	const char *rate;
+};
+
+/* Safe and valid period for async negotiations. */
+#define	AHC_ASYNC_XFER_PERIOD 0x45
+#define	AHC_ULTRA2_XFER_PERIOD 0x0a
+
+/*
+ * Indexes into our table of syncronous transfer rates.
+ */
+#define AHC_SYNCRATE_DT		0
+#define AHC_SYNCRATE_ULTRA2	1
+#define AHC_SYNCRATE_ULTRA	3
+#define AHC_SYNCRATE_FAST	6
+#define AHC_SYNCRATE_MAX	AHC_SYNCRATE_DT
+#define	AHC_SYNCRATE_MIN	13
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table. 
+ */
+struct ahc_phase_table_entry {
+        uint8_t phase;
+        uint8_t mesg_out; /* Message response to parity errors */
+	char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+	uint16_t device_flags[16];	/* words 0-15 */
+#define		CFXFER		0x0007	/* synchronous transfer rate */
+#define		CFSYNCH		0x0008	/* enable synchronous transfer */
+#define		CFDISC		0x0010	/* enable disconnection */
+#define		CFWIDEB		0x0020	/* wide bus device */
+#define		CFSYNCHISULTRA	0x0040	/* CFSYNCH is an ultra offset (2940AU)*/
+#define		CFSYNCSINGLE	0x0080	/* Single-Transition signalling */
+#define		CFSTART		0x0100	/* send start unit SCSI command */
+#define		CFINCBIOS	0x0200	/* include in BIOS scan */
+#define		CFRNFOUND	0x0400	/* report even if not found */
+#define		CFMULTILUNDEV	0x0800	/* Probe multiple luns in BIOS scan */
+#define		CFWBCACHEENB	0x4000	/* Enable W-Behind Cache on disks */
+#define		CFWBCACHENOP	0xc000	/* Don't touch W-Behind Cache */
+
+/*
+ * BIOS Control Bits
+ */
+	uint16_t bios_control;		/* word 16 */
+#define		CFSUPREM	0x0001	/* support all removeable drives */
+#define		CFSUPREMB	0x0002	/* support removeable boot drives */
+#define		CFBIOSEN	0x0004	/* BIOS enabled */
+#define		CFBIOS_BUSSCAN	0x0008	/* Have the BIOS Scan the Bus */
+#define		CFSM2DRV	0x0010	/* support more than two drives */
+#define		CFSTPWLEVEL	0x0010	/* Termination level control */
+#define		CF284XEXTEND	0x0020	/* extended translation (284x cards) */	
+#define		CFCTRL_A	0x0020	/* BIOS displays Ctrl-A message */	
+#define		CFTERM_MENU	0x0040	/* BIOS displays termination menu */	
+#define		CFEXTEND	0x0080	/* extended translation enabled */
+#define		CFSCAMEN	0x0100	/* SCAM enable */
+#define		CFMSG_LEVEL	0x0600	/* BIOS Message Level */
+#define			CFMSG_VERBOSE	0x0000
+#define			CFMSG_SILENT	0x0200
+#define			CFMSG_DIAG	0x0400
+#define		CFBOOTCD	0x0800  /* Support Bootable CD-ROM */
+/*		UNUSED		0xff00	*/
+
+/*
+ * Host Adapter Control Bits
+ */
+	uint16_t adapter_control;	/* word 17 */	
+#define		CFAUTOTERM	0x0001	/* Perform Auto termination */
+#define		CFULTRAEN	0x0002	/* Ultra SCSI speed enable */
+#define		CF284XSELTO     0x0003	/* Selection timeout (284x cards) */
+#define		CF284XFIFO      0x000C	/* FIFO Threshold (284x cards) */
+#define		CFSTERM		0x0004	/* SCSI low byte termination */
+#define		CFWSTERM	0x0008	/* SCSI high byte termination */
+#define		CFSPARITY	0x0010	/* SCSI parity */
+#define		CF284XSTERM     0x0020	/* SCSI low byte term (284x cards) */	
+#define		CFMULTILUN	0x0020
+#define		CFRESETB	0x0040	/* reset SCSI bus at boot */
+#define		CFCLUSTERENB	0x0080	/* Cluster Enable */
+#define		CFBOOTCHAN	0x0300	/* probe this channel first */
+#define		CFBOOTCHANSHIFT 8
+#define		CFSEAUTOTERM	0x0400	/* Ultra2 Perform secondary Auto Term*/
+#define		CFSELOWTERM	0x0800	/* Ultra2 secondary low term */
+#define		CFSEHIGHTERM	0x1000	/* Ultra2 secondary high term */
+#define		CFENABLEDV	0x4000	/* Perform Domain Validation*/
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+	uint16_t brtime_id;		/* word 18 */
+#define		CFSCSIID	0x000f	/* host adapter SCSI ID */
+/*		UNUSED		0x00f0	*/
+#define		CFBRTIME	0xff00	/* bus release time */
+
+/*
+ * Maximum targets
+ */
+	uint16_t max_targets;		/* word 19 */	
+#define		CFMAXTARG	0x00ff	/* maximum targets */
+#define		CFBOOTLUN	0x0f00	/* Lun to boot from */
+#define		CFBOOTID	0xf000	/* Target to boot from */
+	uint16_t res_1[10];		/* words 20-29 */
+	uint16_t signature;		/* Signature == 0x250 */
+#define		CFSIGNATURE	0x250
+#define		CFSIGNATURE2	0x300
+	uint16_t checksum;		/* word 31 */
+};
+
+/****************************  Message Buffer *********************************/
+typedef enum {
+	MSG_TYPE_NONE			= 0x00,
+	MSG_TYPE_INITIATOR_MSGOUT	= 0x01,
+	MSG_TYPE_INITIATOR_MSGIN	= 0x02,
+	MSG_TYPE_TARGET_MSGOUT		= 0x03,
+	MSG_TYPE_TARGET_MSGIN		= 0x04
+} ahc_msg_type;
+
+typedef enum {
+	MSGLOOP_IN_PROG,
+	MSGLOOP_MSGCOMPLETE,
+	MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+TAILQ_HEAD(scb_tailq, scb);
+
+struct ahc_aic7770_softc {
+	/*
+	 * Saved register state used for chip_init().
+	 */
+	uint8_t busspd;
+	uint8_t bustime;
+};
+
+struct ahc_pci_softc {
+	/*
+	 * Saved register state used for chip_init().
+	 */
+	uint32_t  devconfig;
+	uint16_t  targcrccnt;
+	uint8_t   command;
+	uint8_t   csize_lattime;
+	uint8_t   optionmode;
+	uint8_t   crccontrol1;
+	uint8_t   dscommand0;
+	uint8_t   dspcistatus;
+	uint8_t   scbbaddr;
+	uint8_t   dff_thrsh;
+};
+
+union ahc_bus_softc {
+	struct ahc_aic7770_softc aic7770_softc;
+	struct ahc_pci_softc pci_softc;
+};
+
+typedef void (*ahc_bus_intr_t)(struct ahc_softc *);
+typedef int (*ahc_bus_chip_init_t)(struct ahc_softc *);
+typedef int (*ahc_bus_suspend_t)(struct ahc_softc *);
+typedef int (*ahc_bus_resume_t)(struct ahc_softc *);
+typedef void ahc_callback_t (void *);
+
+struct ahc_softc {
+	bus_space_tag_t           tag;
+	bus_space_handle_t        bsh;
+#ifndef __linux__
+	bus_dma_tag_t		  buffer_dmat;   /* dmat for buffer I/O */
+#endif
+	struct scb_data		 *scb_data;
+
+	struct scb		 *next_queued_scb;
+
+	/*
+	 * SCBs that have been sent to the controller
+	 */
+	LIST_HEAD(, scb)	  pending_scbs;
+
+	/*
+	 * Counting lock for deferring the release of additional
+	 * untagged transactions from the untagged_queues.  When
+	 * the lock is decremented to 0, all queues in the
+	 * untagged_queues array are run.
+	 */
+	u_int			  untagged_queue_lock;
+
+	/*
+	 * Per-target queue of untagged-transactions.  The
+	 * transaction at the head of the queue is the
+	 * currently pending untagged transaction for the
+	 * target.  The driver only allows a single untagged
+	 * transaction per target.
+	 */
+	struct scb_tailq	  untagged_queues[AHC_NUM_TARGETS];
+
+	/*
+	 * Bus attachment specific data.
+	 */
+	union ahc_bus_softc	  bus_softc;
+
+	/*
+	 * Platform specific data.
+	 */
+	struct ahc_platform_data *platform_data;
+
+	/*
+	 * Platform specific device information.
+	 */
+	ahc_dev_softc_t		  dev_softc;
+
+	/*
+	 * Bus specific device information.
+	 */
+	ahc_bus_intr_t		  bus_intr;
+
+	/*
+	 * Bus specific initialization required
+	 * after a chip reset.
+	 */
+	ahc_bus_chip_init_t	  bus_chip_init;
+
+	/*
+	 * Target mode related state kept on a per enabled lun basis.
+	 * Targets that are not enabled will have null entries.
+	 * As an initiator, we keep one target entry for our initiator
+	 * ID to store our sync/wide transfer settings.
+	 */
+	struct ahc_tmode_tstate  *enabled_targets[AHC_NUM_TARGETS];
+
+	/*
+	 * The black hole device responsible for handling requests for
+	 * disabled luns on enabled targets.
+	 */
+	struct ahc_tmode_lstate  *black_hole;
+
+	/*
+	 * Device instance currently on the bus awaiting a continue TIO
+	 * for a command that was not given the disconnect priveledge.
+	 */
+	struct ahc_tmode_lstate  *pending_device;
+
+	/*
+	 * Card characteristics
+	 */
+	ahc_chip		  chip;
+	ahc_feature		  features;
+	ahc_bug			  bugs;
+	ahc_flag		  flags;
+	struct seeprom_config	 *seep_config;
+
+	/* Values to store in the SEQCTL register for pause and unpause */
+	uint8_t			  unpause;
+	uint8_t			  pause;
+
+	/* Command Queues */
+	uint8_t			  qoutfifonext;
+	uint8_t			  qinfifonext;
+	uint8_t			 *qoutfifo;
+	uint8_t			 *qinfifo;
+
+	/* Critical Section Data */
+	struct cs		 *critical_sections;
+	u_int			  num_critical_sections;
+
+	/* Channel Names ('A', 'B', etc.) */
+	char			  channel;
+	char			  channel_b;
+
+	/* Initiator Bus ID */
+	uint8_t			  our_id;
+	uint8_t			  our_id_b;
+
+	/*
+	 * PCI error detection.
+	 */
+	int			  unsolicited_ints;
+
+	/*
+	 * Target incoming command FIFO.
+	 */
+	struct target_cmd	 *targetcmds;
+	uint8_t			  tqinfifonext;
+
+	/*
+	 * Cached copy of the sequencer control register.
+	 */
+	uint8_t			  seqctl;
+
+	/*
+	 * Incoming and outgoing message handling.
+	 */
+	uint8_t			  send_msg_perror;
+	ahc_msg_type		  msg_type;
+	uint8_t			  msgout_buf[12];/* Message we are sending */
+	uint8_t			  msgin_buf[12];/* Message we are receiving */
+	u_int			  msgout_len;	/* Length of message to send */
+	u_int			  msgout_index;	/* Current index in msgout */
+	u_int			  msgin_index;	/* Current index in msgin */
+
+	/*
+	 * Mapping information for data structures shared
+	 * between the sequencer and kernel.
+	 */
+	bus_dma_tag_t		  parent_dmat;
+	bus_dma_tag_t		  shared_data_dmat;
+	bus_dmamap_t		  shared_data_dmamap;
+	dma_addr_t		  shared_data_busaddr;
+
+	/*
+	 * Bus address of the one byte buffer used to
+	 * work-around a DMA bug for chips <= aic7880
+	 * in target mode.
+	 */
+	dma_addr_t		  dma_bug_buf;
+
+	/* Number of enabled target mode device on this card */
+	u_int			  enabled_luns;
+
+	/* Initialization level of this data structure */
+	u_int			  init_level;
+
+	/* PCI cacheline size. */
+	u_int			  pci_cachesize;
+
+	/*
+	 * Count of parity errors we have seen as a target.
+	 * We auto-disable parity error checking after seeing
+	 * AHC_PCI_TARGET_PERR_THRESH number of errors.
+	 */
+	u_int			  pci_target_perr_count;
+#define		AHC_PCI_TARGET_PERR_THRESH	10
+
+	/* Maximum number of sequencer instructions supported. */
+	u_int			  instruction_ram_size;
+
+	/* Per-Unit descriptive information */
+	const char		 *description;
+	char			 *name;
+	int			  unit;
+
+	/* Selection Timer settings */
+	int			  seltime;
+	int			  seltime_b;
+
+	uint16_t	 	  user_discenable;/* Disconnection allowed  */
+	uint16_t		  user_tagenable;/* Tagged Queuing allowed */
+};
+
+/************************ Active Device Information ***************************/
+typedef enum {
+	ROLE_UNKNOWN,
+	ROLE_INITIATOR,
+	ROLE_TARGET
+} role_t;
+
+struct ahc_devinfo {
+	int	 our_scsiid;
+	int	 target_offset;
+	uint16_t target_mask;
+	u_int	 target;
+	u_int	 lun;
+	char	 channel;
+	role_t	 role;		/*
+				 * Only guaranteed to be correct if not
+				 * in the busfree state.
+				 */
+};
+
+/****************************** PCI Structures ********************************/
+typedef int (ahc_device_setup_t)(struct ahc_softc *);
+
+struct ahc_pci_identity {
+	uint64_t		 full_id;
+	uint64_t		 id_mask;
+	const char		*name;
+	ahc_device_setup_t	*setup;
+};
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+	uint32_t		 full_id;
+	uint32_t		 id_mask;
+	const char		*name;
+	ahc_device_setup_t	*setup;
+};
+extern struct aic7770_identity aic7770_ident_table[];
+extern const int ahc_num_aic7770_devs;
+
+#define AHC_EISA_SLOT_OFFSET	0xc00
+#define AHC_EISA_IOSIZE		0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+
+/***************************** PCI Front End *********************************/
+const struct ahc_pci_identity	*ahc_find_pci_device(ahc_dev_softc_t);
+int			 ahc_pci_config(struct ahc_softc *,
+					const struct ahc_pci_identity *);
+int			 ahc_pci_test_register_access(struct ahc_softc *);
+#ifdef CONFIG_PM
+void			 ahc_pci_resume(struct ahc_softc *ahc);
+#endif
+
+/*************************** EISA/VL Front End ********************************/
+struct aic7770_identity *aic7770_find_device(uint32_t);
+int			 aic7770_config(struct ahc_softc *ahc,
+					struct aic7770_identity *,
+					u_int port);
+
+/************************** SCB and SCB queue management **********************/
+int		ahc_probe_scbs(struct ahc_softc *);
+void		ahc_qinfifo_requeue_tail(struct ahc_softc *ahc,
+					 struct scb *scb);
+int		ahc_match_scb(struct ahc_softc *ahc, struct scb *scb,
+			      int target, char channel, int lun,
+			      u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+struct ahc_softc	*ahc_alloc(void *platform_arg, char *name);
+int			 ahc_softc_init(struct ahc_softc *);
+void			 ahc_controller_info(struct ahc_softc *ahc, char *buf);
+int			 ahc_chip_init(struct ahc_softc *ahc);
+int			 ahc_init(struct ahc_softc *ahc);
+void			 ahc_intr_enable(struct ahc_softc *ahc, int enable);
+void			 ahc_pause_and_flushwork(struct ahc_softc *ahc);
+#ifdef CONFIG_PM
+int			 ahc_suspend(struct ahc_softc *ahc); 
+int			 ahc_resume(struct ahc_softc *ahc);
+#endif
+void			 ahc_set_unit(struct ahc_softc *, int);
+void			 ahc_set_name(struct ahc_softc *, char *);
+void			 ahc_free(struct ahc_softc *ahc);
+int			 ahc_reset(struct ahc_softc *ahc, int reinit);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+	SEARCH_COMPLETE,
+	SEARCH_COUNT,
+	SEARCH_REMOVE
+} ahc_search_action;
+int			ahc_search_qinfifo(struct ahc_softc *ahc, int target,
+					   char channel, int lun, u_int tag,
+					   role_t role, uint32_t status,
+					   ahc_search_action action);
+int			ahc_search_untagged_queues(struct ahc_softc *ahc,
+						   ahc_io_ctx_t ctx,
+						   int target, char channel,
+						   int lun, uint32_t status,
+						   ahc_search_action action);
+int			ahc_search_disc_list(struct ahc_softc *ahc, int target,
+					     char channel, int lun, u_int tag,
+					     int stop_on_first, int remove,
+					     int save_state);
+int			ahc_reset_channel(struct ahc_softc *ahc, char channel,
+					  int initiate_reset);
+
+/*************************** Utility Functions ********************************/
+void			ahc_compile_devinfo(struct ahc_devinfo *devinfo,
+					    u_int our_id, u_int target,
+					    u_int lun, char channel,
+					    role_t role);
+/************************** Transfer Negotiation ******************************/
+const struct ahc_syncrate*	ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+					  u_int *ppr_options, u_int maxsync);
+u_int			ahc_find_period(struct ahc_softc *ahc,
+					u_int scsirate, u_int maxsync);
+/*
+ * Negotiation types.  These are used to qualify if we should renegotiate
+ * even if our goal and current transport parameters are identical.
+ */
+typedef enum {
+	AHC_NEG_TO_GOAL,	/* Renegotiate only if goal and curr differ. */
+	AHC_NEG_IF_NON_ASYNC,	/* Renegotiate so long as goal is non-async. */
+	AHC_NEG_ALWAYS		/* Renegotiat even if goal is async. */
+} ahc_neg_type;
+int			ahc_update_neg_request(struct ahc_softc*,
+					       struct ahc_devinfo*,
+					       struct ahc_tmode_tstate*,
+					       struct ahc_initiator_tinfo*,
+					       ahc_neg_type);
+void			ahc_set_width(struct ahc_softc *ahc,
+				      struct ahc_devinfo *devinfo,
+				      u_int width, u_int type, int paused);
+void			ahc_set_syncrate(struct ahc_softc *ahc,
+					 struct ahc_devinfo *devinfo,
+					 const struct ahc_syncrate *syncrate,
+					 u_int period, u_int offset,
+					 u_int ppr_options,
+					 u_int type, int paused);
+typedef enum {
+	AHC_QUEUE_NONE,
+	AHC_QUEUE_BASIC,
+	AHC_QUEUE_TAGGED
+} ahc_queue_alg;
+
+/**************************** Target Mode *************************************/
+#ifdef AHC_TARGET_MODE
+void		ahc_send_lstate_events(struct ahc_softc *,
+				       struct ahc_tmode_lstate *);
+void		ahc_handle_en_lun(struct ahc_softc *ahc,
+				  struct cam_sim *sim, union ccb *ccb);
+cam_status	ahc_find_tmode_devs(struct ahc_softc *ahc,
+				    struct cam_sim *sim, union ccb *ccb,
+				    struct ahc_tmode_tstate **tstate,
+				    struct ahc_tmode_lstate **lstate,
+				    int notfound_failure);
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHC_DEBUG
+extern uint32_t ahc_debug;
+#define	AHC_SHOW_MISC		0x0001
+#define	AHC_SHOW_SENSE		0x0002
+#define AHC_DUMP_SEEPROM	0x0004
+#define AHC_SHOW_TERMCTL	0x0008
+#define AHC_SHOW_MEMORY		0x0010
+#define AHC_SHOW_MESSAGES	0x0020
+#define	AHC_SHOW_DV		0x0040
+#define AHC_SHOW_SELTO		0x0080
+#define AHC_SHOW_QFULL		0x0200
+#define AHC_SHOW_QUEUE		0x0400
+#define AHC_SHOW_TQIN		0x0800
+#define AHC_SHOW_MASKED_ERRORS	0x1000
+#define AHC_DEBUG_SEQUENCER	0x2000
+#endif
+void			ahc_print_devinfo(struct ahc_softc *ahc,
+					  struct ahc_devinfo *dev);
+void			ahc_dump_card_state(struct ahc_softc *ahc);
+int			ahc_print_register(const ahc_reg_parse_entry_t *table,
+					   u_int num_entries,
+					   const char *name,
+					   u_int address,
+					   u_int value,
+					   u_int *cur_column,
+					   u_int wrap_point);
+/******************************* SEEPROM *************************************/
+int		ahc_acquire_seeprom(struct ahc_softc *ahc,
+				    struct seeprom_descriptor *sd);
+void		ahc_release_seeprom(struct seeprom_descriptor *sd);
+#endif /* _AIC7XXX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx.reg b/drivers/scsi/aic7xxx/aic7xxx.reg
new file mode 100644
index 0000000..ba0b411
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx.reg
@@ -0,0 +1,1761 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#40 $"
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic7xxx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic7xxx driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book available from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * Registers marked "dont_generate_debug_code" are not (yet) referenced
+ * from the driver code, and this keyword inhibit generation
+ * of debug code for them.
+ *
+ * REG_PRETTY_PRINT config will complain if dont_generate_debug_code
+ * is added to the register which is referenced in the driver.
+ * Unreferenced register with no dont_generate_debug_code will result
+ * in dead code. No warning is issued.
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+register SCSISEQ {
+	address			0x000
+	access_mode RW
+	field	TEMODE		0x80
+	field	ENSELO		0x40
+	field	ENSELI		0x20
+	field	ENRSELI		0x10
+	field	ENAUTOATNO	0x08
+	field	ENAUTOATNI	0x04
+	field	ENAUTOATNP	0x02
+	field	SCSIRSTO	0x01
+}
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL0 {
+	address			0x001
+	access_mode RW
+	field	DFON		0x80
+	field	DFPEXP		0x40
+	field	FAST20		0x20
+	field	CLRSTCNT	0x10
+	field	SPIOEN		0x08
+	field	SCAMEN		0x04
+	field	CLRCHN		0x02
+}
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL1 {
+	address			0x002
+	access_mode RW
+	field	BITBUCKET	0x80
+	field	SWRAPEN		0x40
+	field	ENSPCHK		0x20
+	mask	STIMESEL	0x18
+	field	ENSTIMER	0x04
+	field	ACTNEGEN	0x02
+	field	STPWEN		0x01	/* Powered Termination */
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+register SCSISIGI {
+	address			0x003
+	access_mode RO
+	field	CDI		0x80
+	field	IOI		0x40
+	field	MSGI		0x20
+	field	ATNI		0x10
+	field	SELI		0x08
+	field	BSYI		0x04
+	field	REQI		0x02
+	field	ACKI		0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+	mask	PHASE_MASK	CDI|IOI|MSGI
+	mask	P_DATAOUT	0x00
+	mask	P_DATAIN	IOI
+	mask	P_DATAOUT_DT	P_DATAOUT|MSGI
+	mask	P_DATAIN_DT	P_DATAIN|MSGI
+	mask	P_COMMAND	CDI
+	mask	P_MESGOUT	CDI|MSGI
+	mask	P_STATUS	CDI|IOI
+	mask	P_MESGIN	CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Control 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.
+ */
+register SCSISIGO {
+	address			0x003
+	access_mode WO
+	field	CDO		0x80
+	field	IOO		0x40
+	field	MSGO		0x20
+	field	ATNO		0x10
+	field	SELO		0x08
+	field	BSYO		0x04
+	field	REQO		0x02
+	field	ACKO		0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+	mask	PHASE_MASK	CDI|IOI|MSGI
+	mask	P_DATAOUT	0x00
+	mask	P_DATAIN	IOI
+	mask	P_COMMAND	CDI
+	mask	P_MESGOUT	CDI|MSGI
+	mask	P_STATUS	CDI|IOI
+	mask	P_MESGIN	CDI|IOI|MSGI
+	dont_generate_debug_code
+}
+
+/* 
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset.  An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers.  Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+register SCSIRATE {
+	address			0x004
+	access_mode RW
+	field	WIDEXFER	0x80		/* Wide transfer control */
+	field	ENABLE_CRC	0x40		/* CRC for D-Phases */
+	field	SINGLE_EDGE	0x10		/* Disable DT Transfers */
+	mask	SXFR		0x70		/* Sync transfer rate */
+	mask	SXFR_ULTRA2	0x0f		/* Sync transfer rate */
+	mask	SOFS		0x0f		/* Sync offset */
+}
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+register SCSIID	{
+	address			0x005
+	access_mode RW
+	mask	TID		0xf0		/* Target ID mask */
+	mask	TWIN_TID	0x70
+	field	TWIN_CHNLB	0x80
+	mask	OID		0x0f		/* Our ID mask */
+	/*
+	 * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
+	 * The aic7890/91 allow an offset of up to 127 transfers in both wide
+	 * and narrow mode.
+	 */
+	alias	SCSIOFFSET
+	mask	SOFS_ULTRA2	0x7f		/* Sync offset U2 chips */
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latches used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode.  SCSIDATH can be used for the
+ * upper byte of a 16bit wide asynchronouse data phase transfer.
+ */
+register SCSIDATL {
+	address			0x006
+	access_mode RW
+	dont_generate_debug_code
+}
+
+register SCSIDATH {
+	address			0x007
+	access_mode RW
+}
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transferred
+ * across the SCSI bus.  The counter is decremented only once
+ * the data has been safely transferred.  SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */ 
+register STCNT {
+	address			0x008
+	size	3
+	access_mode RW
+	dont_generate_debug_code
+}
+
+/* ALT_MODE registers (Ultra2 and Ultra160 chips) */
+register SXFRCTL2 {
+	address			0x013
+	access_mode RW
+	field	AUTORSTDIS	0x10
+	field	CMDDMAEN	0x08
+	mask	ASYNC_SETUP	0x07
+}
+
+/* ALT_MODE register on Ultra160 chips */
+register OPTIONMODE {
+	address			0x008
+	access_mode RW
+	count		2
+	field	AUTORATEEN		0x80
+	field	AUTOACKEN		0x40
+	field	ATNMGMNTEN		0x20
+	field	BUSFREEREV		0x10
+	field	EXPPHASEDIS		0x08
+	field	SCSIDATL_IMGEN		0x04
+	field	AUTO_MSGOUT_DE		0x02
+	field	DIS_MSGIN_DUALEDGE	0x01
+	mask	OPTIONMODE_DEFAULTS	AUTO_MSGOUT_DE|DIS_MSGIN_DUALEDGE
+	dont_generate_debug_code
+}
+
+/* ALT_MODE register on Ultra160 chips */
+register TARGCRCCNT {
+	address			0x00a
+	size	2
+	access_mode RW
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+	address			0x00b
+	access_mode WO
+	field	CLRSELDO	0x40
+	field	CLRSELDI	0x20
+	field	CLRSELINGO	0x10
+	field	CLRSWRAP	0x08
+	field	CLRIOERR	0x08	/* Ultra2 Only */
+	field	CLRSPIORDY	0x02
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+register SSTAT0	{
+	address			0x00b
+	access_mode RO
+	field	TARGET		0x80	/* Board acting as target */
+	field	SELDO		0x40	/* Selection Done */
+	field	SELDI		0x20	/* Board has been selected */
+	field	SELINGO		0x10	/* Selection In Progress */
+	field	SWRAP		0x08	/* 24bit counter wrap */
+	field	IOERR		0x08	/* LVD Tranceiver mode changed */
+	field	SDONE		0x04	/* STCNT = 0x000000 */
+	field	SPIORDY		0x02	/* SCSI PIO Ready */
+	field	DMADONE		0x01	/* DMA transfer completed */
+}
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+	address			0x00c
+	access_mode WO
+	field	CLRSELTIMEO	0x80
+	field	CLRATNO		0x40
+	field	CLRSCSIRSTI	0x20
+	field	CLRBUSFREE	0x08
+	field	CLRSCSIPERR	0x04
+	field	CLRPHASECHG	0x02
+	field	CLRREQINIT	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+register SSTAT1	{
+	address			0x00c
+	access_mode RO
+	field	SELTO		0x80
+	field	ATNTARG 	0x40
+	field	SCSIRSTI	0x20
+	field	PHASEMIS	0x10
+	field	BUSFREE		0x08
+	field	SCSIPERR	0x04
+	field	PHASECHG	0x02
+	field	REQINIT		0x01
+}
+
+/*
+ * SCSI Status 2 (pp. 3-25,26)
+ */
+register SSTAT2 {
+	address			0x00d
+	access_mode RO
+	field	OVERRUN		0x80
+	field	SHVALID		0x40	/* Shadow Layer non-zero */
+	field	EXP_ACTIVE	0x10	/* SCSI Expander Active */
+	field	CRCVALERR	0x08	/* CRC doesn't match (U3 only) */
+	field	CRCENDERR	0x04	/* No terminal CRC packet (U3 only) */
+	field	CRCREQERR	0x02	/* Illegal CRC packet req (U3 only) */
+	field	DUAL_EDGE_ERR	0x01	/* Incorrect data phase (U3 only) */
+	mask	SFCNT		0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+	address			0x00e
+	access_mode RO
+	count		2
+	mask	SCSICNT		0xf0
+	mask	OFFCNT		0x0f
+	mask	U2OFFCNT	0x7f
+}
+
+/*
+ * SCSI ID for the aic7890/91 chips
+ */
+register SCSIID_ULTRA2 {
+	address			0x00f
+	access_mode RW
+	mask	TID		0xf0		/* Target ID mask */
+	mask	OID		0x0f		/* Our ID mask */
+	dont_generate_debug_code
+}
+
+/*
+ * SCSI Interrupt Mode 1 (p. 3-28)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+	address			0x010
+	access_mode RW
+	count		2
+	field	ENSELDO		0x40
+	field	ENSELDI		0x20
+	field	ENSELINGO	0x10
+	field	ENSWRAP		0x08
+	field	ENIOERR		0x08	/* LVD Tranceiver mode changes */
+	field	ENSDONE		0x04
+	field	ENSPIORDY	0x02
+	field	ENDMADONE	0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+	address			0x011
+	access_mode RW
+	field	ENSELTIMO	0x80
+	field	ENATNTARG	0x40
+	field	ENSCSIRST	0x20
+	field	ENPHASEMIS	0x10
+	field	ENBUSFREE	0x08
+	field	ENSCSIPERR	0x04
+	field	ENPHASECHG	0x02
+	field	ENREQINIT	0x01
+}
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+register SCSIBUSL {
+	address			0x012
+	access_mode RW
+}
+
+register SCSIBUSH {
+	address			0x013
+	access_mode RW
+}
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transferred on the SCSI bus.  They are counted up in the same
+ * manner as STCNT is counted down.  SHADDR should always be used
+ * to determine the address of the last byte transferred since HADDR
+ * can be skewed by write ahead.
+ */
+register SHADDR {
+	address			0x014
+	size	4
+	access_mode RO
+	dont_generate_debug_code
+}
+
+/*
+ * Selection Timeout Timer (p. 3-30)
+ */
+register SELTIMER {
+	address			0x018
+	access_mode RW
+	count		1
+	field	STAGE6		0x20
+	field	STAGE5		0x10
+	field	STAGE4		0x08
+	field	STAGE3		0x04
+	field	STAGE2		0x02
+	field	STAGE1		0x01
+	alias	TARGIDIN
+	dont_generate_debug_code
+}
+
+/*
+ * 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.
+ */
+register SELID {
+	address			0x019
+	access_mode RW
+	mask	SELID_MASK	0xf0
+	field	ONEBIT		0x08
+	dont_generate_debug_code
+}
+
+register SCAMCTL {
+	address			0x01a
+	access_mode RW
+	field	ENSCAMSELO	0x80
+	field	CLRSCAMSELID	0x40
+	field	ALTSTIM		0x20
+	field	DFLTTID		0x10
+	mask	SCAMLVL		0x03
+}
+
+/*
+ * Target Mode Selecting in ID bitmask (aic7890/91/96/97)
+ */
+register TARGID {
+	address			0x01b
+	size			2
+	access_mode RW
+	count		14
+	dont_generate_debug_code
+}
+
+/*
+ * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
+ * Indicates if external logic has been attached to the chip to
+ * perform the tasks of accessing a serial eeprom, testing termination
+ * strength, and performing cable detection.  On the aic7860, most of
+ * these features are handled on chip, but on the aic7855 an attached
+ * aic3800 does the grunt work.
+ */
+register SPIOCAP {
+	address			0x01b
+	access_mode RW
+	count		10
+	field	SOFT1		0x80
+	field	SOFT0		0x40
+	field	SOFTCMDEN	0x20	
+	field	EXT_BRDCTL	0x10	/* External Board control */
+	field	SEEPROM		0x08	/* External serial eeprom logic */
+	field	EEPROM		0x04	/* Writable external BIOS ROM */
+	field	ROM		0x02	/* Logic for accessing external ROM */
+	field	SSPIOCPS	0x01	/* Termination and cable detection */
+	dont_generate_debug_code
+}
+
+register BRDCTL	{
+	address			0x01d
+	count		11
+	field	BRDDAT7		0x80
+	field	BRDDAT6		0x40
+	field	BRDDAT5		0x20
+	field	BRDSTB		0x10
+	field	BRDCS		0x08
+	field	BRDRW		0x04
+	field	BRDCTL1		0x02
+	field	BRDCTL0		0x01
+	/* 7890 Definitions */
+	field	BRDDAT4		0x10
+	field	BRDDAT3		0x08
+	field	BRDDAT2		0x04
+	field	BRDRW_ULTRA2	0x02
+	field	BRDSTB_ULTRA2	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * 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.  
+ *
+ * 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.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to 
+ * read the serial EEPROM.
+ */
+register SEECTL {
+	address			0x01e
+	count		11
+	field	EXTARBACK	0x80
+	field	EXTARBREQ	0x40
+	field	SEEMS		0x20
+	field	SEERDY		0x10
+	field	SEECS		0x08
+	field	SEECK		0x04
+	field	SEEDO		0x02
+	field	SEEDI		0x01
+	dont_generate_debug_code
+}
+/*
+ * 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.
+ */
+register SBLKCTL {
+	address			0x01f
+	access_mode RW
+	field	DIAGLEDEN	0x80	/* Aic78X0 only */
+	field	DIAGLEDON	0x40	/* Aic78X0 only */
+	field	AUTOFLUSHDIS	0x20
+	field	SELBUSB		0x08
+	field	ENAB40		0x08	/* LVD transceiver active */
+	field	ENAB20		0x04	/* SE/HVD transceiver active */
+	field	SELWIDE		0x02
+	field	XCVR		0x01	/* External transceiver active */
+}
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+register SEQCTL {
+	address			0x060
+	access_mode RW
+	count		15
+	field	PERRORDIS	0x80
+	field	PAUSEDIS	0x40
+	field	FAILDIS		0x20
+	field	FASTMODE	0x10
+	field	BRKADRINTEN	0x08
+	field	STEP		0x04
+	field	SEQRESET	0x02
+	field	LOADRAM		0x01
+}
+
+/*
+ * 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 succession.  The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+register SEQRAM {
+	address			0x061
+	access_mode RW
+	count		2
+	dont_generate_debug_code
+}
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+register SEQADDR0 {
+	address			0x062
+	access_mode RW
+	dont_generate_debug_code
+}
+
+register SEQADDR1 {
+	address			0x063
+	access_mode RW
+	count		8
+	mask	SEQADDR1_MASK	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+register ACCUM {
+	address			0x064
+	access_mode RW
+	accumulator
+	dont_generate_debug_code
+}
+
+register SINDEX	{
+	address			0x065
+	access_mode RW
+	sindex
+	dont_generate_debug_code
+}
+
+register DINDEX {
+	address			0x066
+	access_mode RW
+	dont_generate_debug_code
+}
+
+register ALLONES {
+	address			0x069
+	access_mode RO
+	allones
+	dont_generate_debug_code
+}
+
+register ALLZEROS {
+	address			0x06a
+	access_mode RO
+	allzeros
+	dont_generate_debug_code
+}
+
+register NONE {
+	address			0x06a
+	access_mode WO
+	none
+	dont_generate_debug_code
+}
+
+register FLAGS {
+	address			0x06b
+	access_mode RO
+	count		18
+	field	ZERO		0x02
+	field	CARRY		0x01
+	dont_generate_debug_code
+}
+
+register SINDIR	{
+	address			0x06c
+	access_mode RO
+	dont_generate_debug_code
+}
+
+register DINDIR	 {
+	address			0x06d
+	access_mode WO
+	dont_generate_debug_code
+}
+
+register FUNCTION1 {
+	address			0x06e
+	access_mode RW
+}
+
+register STACK {
+	address			0x06f
+	access_mode RO
+	count		5
+	dont_generate_debug_code
+}
+
+const	STACK_SIZE	4
+
+/*
+ * Board Control (p. 3-43)
+ */
+register BCTL {
+	address			0x084
+	access_mode RW
+	field	ACE		0x08
+	field	ENABLE		0x01
+}
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+register DSCOMMAND0 {
+	address			0x084
+	access_mode RW
+	count		7
+	field	CACHETHEN	0x80	/* Cache Threshold enable */
+	field	DPARCKEN	0x40	/* Data Parity Check Enable */
+	field	MPARCKEN	0x20	/* Memory Parity Check Enable */
+	field	EXTREQLCK	0x10	/* External Request Lock */
+	/* aic7890/91/96/97 only */
+	field	INTSCBRAMSEL	0x08	/* Internal SCB RAM Select */
+	field	RAMPS		0x04	/* External SCB RAM Present */
+	field	USCBSIZE32	0x02	/* Use 32byte SCB Page Size */
+	field	CIOPARCKEN	0x01	/* Internal bus parity error enable */
+	dont_generate_debug_code
+}
+
+register DSCOMMAND1 {
+	address			0x085
+	access_mode RW
+	mask	DSLATT		0xfc	/* PCI latency timer (non-ultra2) */
+	field	HADDLDSEL1	0x02	/* Host Address Load Select Bits */
+	field	HADDLDSEL0	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Bus On/Off Time (p. 3-44) aic7770 only
+ */
+register BUSTIME {
+	address			0x085
+	access_mode RW
+	count		2
+	mask	BOFF		0xf0
+	mask	BON		0x0f
+	dont_generate_debug_code
+}
+
+/*
+ * Bus Speed (p. 3-45) aic7770 only
+ */
+register BUSSPD {
+	address			0x086
+	access_mode RW
+	count		2
+	mask	DFTHRSH		0xc0
+	mask	STBOFF		0x38
+	mask	STBON		0x07
+	mask	DFTHRSH_100	0xc0
+	mask	DFTHRSH_75	0x80
+	dont_generate_debug_code
+}
+
+/* aic7850/55/60/70/80/95 only */
+register DSPCISTATUS {
+	address			0x086
+	count		4
+	mask	DFTHRSH_100	0xc0
+	dont_generate_debug_code
+}
+
+/* aic7890/91/96/97 only */
+register HS_MAILBOX {
+	address			0x086
+	mask	HOST_MAILBOX	0xF0
+	mask	SEQ_MAILBOX	0x0F
+	mask	HOST_TQINPOS	0x80	/* Boundary at either 0 or 128 */
+	dont_generate_debug_code
+}
+
+const	HOST_MAILBOX_SHIFT	4
+const	SEQ_MAILBOX_SHIFT	0
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overall host control of the device.
+ */
+register HCNTRL {
+	address			0x087
+	access_mode RW
+	count		14
+	field	POWRDN		0x40
+	field	SWINT		0x10
+	field	IRQMS		0x08
+	field	PAUSE		0x04
+	field	INTEN		0x02
+	field	CHIPRST		0x01
+	field	CHIPRSTACK	0x01
+	dont_generate_debug_code
+}
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transferred across the host bus.
+ */
+register HADDR {
+	address			0x088
+	size	4
+	access_mode RW
+	dont_generate_debug_code
+}
+
+register HCNT {
+	address			0x08c
+	size	3
+	access_mode RW
+	dont_generate_debug_code
+}
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the SCBs into the SCBARRAY window.
+ */
+register SCBPTR {
+	address			0x090
+	access_mode RW
+	dont_generate_debug_code
+}
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+register INTSTAT {
+	address			0x091
+	access_mode RW
+	field	BRKADRINT 0x08
+	field	SCSIINT	  0x04
+	field	CMDCMPLT  0x02
+	field	SEQINT    0x01
+	mask	BAD_PHASE	SEQINT		/* unknown scsi bus phase */
+	mask	SEND_REJECT	0x10|SEQINT	/* sending a message reject */
+	mask	PROTO_VIOLATION	0x20|SEQINT	/* SCSI protocol violation */ 
+	mask	NO_MATCH	0x30|SEQINT	/* no cmd match for reconnect */
+	mask	IGN_WIDE_RES	0x40|SEQINT	/* Complex IGN Wide Res Msg */
+	mask	PDATA_REINIT	0x50|SEQINT	/*
+						 * Returned to data phase
+						 * that requires data
+						 * transfer pointers to be
+						 * recalculated from the
+						 * transfer residual.
+						 */
+	mask	HOST_MSG_LOOP	0x60|SEQINT	/*
+						 * The bus is ready for the
+						 * host to perform another
+						 * message transaction.  This
+						 * mechanism is used for things
+						 * like sync/wide negotiation
+						 * that require a kernel based
+						 * message state engine.
+						 */
+	mask	BAD_STATUS	0x70|SEQINT	/* Bad status from target */
+	mask	PERR_DETECTED	0x80|SEQINT	/*
+						 * Either the phase_lock
+						 * or inb_next routine has
+						 * noticed a parity error.
+						 */
+	mask	DATA_OVERRUN	0x90|SEQINT	/*
+						 * Target attempted to write
+						 * beyond the bounds of its
+						 * command.
+						 */
+	mask	MKMSG_FAILED	0xa0|SEQINT	/*
+						 * Target completed command
+						 * without honoring our ATN
+						 * request to issue a message. 
+						 */
+	mask	MISSED_BUSFREE	0xb0|SEQINT	/*
+						 * The sequencer never saw
+						 * the bus go free after
+						 * either a command complete
+						 * or disconnect message.
+						 */
+	mask	SCB_MISMATCH	0xc0|SEQINT	/*
+						 * Downloaded SCB's tag does
+						 * not match the entry we
+						 * intended to download.
+						 */
+	mask	NO_FREE_SCB	0xd0|SEQINT	/*
+						 * get_free_or_disc_scb failed.
+						 */
+	mask	OUT_OF_RANGE	0xe0|SEQINT
+
+	mask	SEQINT_MASK	0xf0|SEQINT	/* SEQINT Status Codes */
+	mask	INT_PEND  (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
+	dont_generate_debug_code
+}
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors.  You usually cannot recover from
+ * these without a full board reset.
+ */
+register ERROR {
+	address			0x092
+	access_mode RO
+	count		26
+	field	CIOPARERR	0x80	/* Ultra2 only */
+	field	PCIERRSTAT	0x40	/* PCI only */
+	field	MPARERR		0x20	/* PCI only */
+	field	DPARERR		0x10	/* PCI only */
+	field	SQPARERR	0x08
+	field	ILLOPCODE	0x04
+	field	ILLSADDR	0x02
+	field	ILLHADDR	0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+	address			0x092
+	access_mode WO
+	count		24
+	field	CLRPARERR	0x10	/* PCI only */
+	field	CLRBRKADRINT	0x08
+	field	CLRSCSIINT      0x04
+	field	CLRCMDINT 	0x02
+	field	CLRSEQINT 	0x01
+	dont_generate_debug_code
+}
+
+register DFCNTRL {
+	address			0x093
+	access_mode RW
+	field	PRELOADEN	0x80	/* aic7890 only */
+	field	WIDEODD		0x40
+	field	SCSIEN		0x20
+	field	SDMAEN		0x10
+	field	SDMAENACK	0x10
+	field	HDMAEN		0x08
+	field	HDMAENACK	0x08
+	field	DIRECTION	0x04
+	field	FIFOFLUSH	0x02
+	field	FIFORESET	0x01
+}
+
+register DFSTATUS {
+	address			0x094
+	access_mode RO
+	field	PRELOAD_AVAIL	0x80
+	field	DFCACHETH	0x40
+	field	FIFOQWDEMP	0x20
+	field	MREQPEND	0x10
+	field	HDONE		0x08
+	field	DFTHRESH	0x04
+	field	FIFOFULL	0x02
+	field	FIFOEMP		0x01
+}
+
+register DFWADDR {
+	address			0x95
+	access_mode RW
+	dont_generate_debug_code
+}
+
+register DFRADDR {
+	address			0x97
+	access_mode RW
+}
+
+register DFDAT {
+	address			0x099
+	access_mode RW
+	dont_generate_debug_code
+}
+
+/*
+ * 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
+ */
+register SCBCNT {
+	address			0x09a
+	access_mode RW
+	count		1
+	field	SCBAUTO		0x80
+	mask	SCBCNT_MASK	0x1f
+	dont_generate_debug_code
+}
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+register QINFIFO {
+	address			0x09b
+	access_mode RW
+	count		12
+	dont_generate_debug_code
+}
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+register QINCNT	{
+	address			0x09c
+	access_mode RO
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+	address			0x09d
+	access_mode WO
+	count		7
+	dont_generate_debug_code
+}
+
+register CRCCONTROL1 {
+	address			0x09d
+	access_mode RW
+	count		3
+	field	CRCONSEEN		0x80
+	field	CRCVALCHKEN		0x40
+	field	CRCENDCHKEN		0x20
+	field	CRCREQCHKEN		0x10
+	field	TARGCRCENDEN		0x08
+	field	TARGCRCCNTEN		0x04
+	dont_generate_debug_code
+}
+
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+	address			0x09e
+	access_mode RO
+}
+
+register SCSIPHASE {
+	address			0x09e
+	access_mode RO
+	field	STATUS_PHASE	0x20
+	field	COMMAND_PHASE	0x10
+	field	MSG_IN_PHASE	0x08
+	field	MSG_OUT_PHASE	0x04
+	field	DATA_IN_PHASE	0x02
+	field	DATA_OUT_PHASE	0x01
+	mask	DATA_PHASE_MASK	0x03
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+	address			0x09f
+	access_mode RW
+	count	    4
+	field	ALT_MODE	0x80
+	dont_generate_debug_code
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+	address		0x0a0
+	size		64
+
+	SCB_CDB_PTR {
+		size	4
+		alias	SCB_RESIDUAL_DATACNT
+		alias	SCB_CDB_STORE
+		dont_generate_debug_code
+	}
+	SCB_RESIDUAL_SGPTR {
+		size	4
+		dont_generate_debug_code
+	}
+	SCB_SCSI_STATUS {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_TARGET_PHASES {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_TARGET_DATA_DIR {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_TARGET_ITAG {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_DATAPTR {
+		size	4
+		dont_generate_debug_code
+	}
+	SCB_DATACNT {
+		/*
+		 * The last byte is really the high address bits for
+		 * the data address.
+		 */
+		size	4
+		field	SG_LAST_SEG		0x80	/* In the fourth byte */
+		mask	SG_HIGH_ADDR_BITS	0x7F	/* In the fourth byte */
+		dont_generate_debug_code
+	}
+	SCB_SGPTR {
+		size	4
+		field	SG_RESID_VALID	0x04	/* In the first byte */
+		field	SG_FULL_RESID	0x02	/* In the first byte */
+		field	SG_LIST_NULL	0x01	/* In the first byte */
+		dont_generate_debug_code
+	}
+	SCB_CONTROL {
+		size	1
+		field	TARGET_SCB			0x80
+		field	STATUS_RCVD			0x80
+		field	DISCENB				0x40
+		field	TAG_ENB				0x20
+		field	MK_MESSAGE			0x10
+		field	ULTRAENB			0x08
+		field	DISCONNECTED			0x04
+		mask	SCB_TAG_TYPE			0x03
+	}
+	SCB_SCSIID {
+		size	1
+		field	TWIN_CHNLB			0x80
+		mask	TWIN_TID			0x70
+		mask	TID				0xf0
+		mask	OID				0x0f
+	}
+	SCB_LUN {
+		field	SCB_XFERLEN_ODD			0x80
+		mask	LID				0x3f
+		size	1
+	}
+	SCB_TAG {
+		size	1
+	}
+	SCB_CDB_LEN {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_SCSIRATE {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_SCSIOFFSET {
+		size	1
+		count	1
+		dont_generate_debug_code
+	}
+	SCB_NEXT {
+		size	1
+		dont_generate_debug_code
+	}
+	SCB_64_SPARE {
+		size	16
+	}
+	SCB_64_BTT {
+		size	16
+		dont_generate_debug_code
+	}
+}
+
+const	SCB_UPLOAD_SIZE		32
+const	SCB_DOWNLOAD_SIZE	32
+const	SCB_DOWNLOAD_SIZE_64	48
+
+const	SG_SIZEOF	0x08		/* sizeof(struct ahc_dma) */
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+register SEECTL_2840 {
+	address			0x0c0
+	access_mode RW
+	count		2
+	field	CS_2840		0x04
+	field	CK_2840		0x02
+	field	DO_2840		0x01
+	dont_generate_debug_code
+}
+
+register STATUS_2840 {
+	address			0x0c1
+	access_mode RW
+	count		4
+	field	EEPROM_TF	0x80
+	mask	BIOS_SEL	0x60
+	mask	ADSEL		0x1e
+	field	DI_2840		0x01
+	dont_generate_debug_code
+}
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+register CCHADDR {
+	address			0x0E0
+	size 8
+	dont_generate_debug_code
+}
+
+register CCHCNT {
+	address			0x0E8
+	dont_generate_debug_code
+}
+
+register CCSGRAM {
+	address			0x0E9
+	dont_generate_debug_code
+}
+
+register CCSGADDR {
+	address			0x0EA
+	dont_generate_debug_code
+}
+
+register CCSGCTL {
+	address			0x0EB
+	field	CCSGDONE	0x80
+	field	CCSGEN		0x08
+	field	SG_FETCH_NEEDED 0x02	/* Bit used for software state */
+	field	CCSGRESET	0x01
+	dont_generate_debug_code
+}
+
+register CCSCBCNT {
+	address			0xEF
+	count		1
+	dont_generate_debug_code
+}
+
+register CCSCBCTL {
+	address			0x0EE
+	field	CCSCBDONE	0x80
+	field	ARRDONE		0x40	/* SCB Array prefetch done */
+	field	CCARREN		0x10
+	field	CCSCBEN		0x08
+	field	CCSCBDIR	0x04
+	field	CCSCBRESET	0x01
+	dont_generate_debug_code
+}
+
+register CCSCBADDR {
+	address			0x0ED
+	dont_generate_debug_code
+}
+
+register CCSCBRAM {
+	address			0xEC
+	dont_generate_debug_code
+}
+
+/*
+ * SCB bank address (7895/7896/97 only)
+ */
+register SCBBADDR {
+	address			0x0F0
+	access_mode RW
+	count		3
+	dont_generate_debug_code
+}
+
+register CCSCBPTR {
+	address			0x0F1
+	dont_generate_debug_code
+}
+
+register HNSCB_QOFF {
+	address			0x0F4
+	count		4
+	dont_generate_debug_code
+}
+
+register SNSCB_QOFF {
+	address			0x0F6
+	dont_generate_debug_code
+}
+
+register SDSCB_QOFF {
+	address			0x0F8
+	dont_generate_debug_code
+}
+
+register QOFF_CTLSTA {
+	address			0x0FA
+	field	SCB_AVAIL	0x40
+	field	SNSCB_ROLLOVER	0x20
+	field	SDSCB_ROLLOVER	0x10
+	mask	SCB_QSIZE	0x07
+	mask	SCB_QSIZE_256	0x06
+	dont_generate_debug_code
+}
+
+register DFF_THRSH {
+	address			0x0FB
+	mask	WR_DFTHRSH	0x70
+	mask	RD_DFTHRSH	0x07
+	mask	RD_DFTHRSH_MIN	0x00
+	mask	RD_DFTHRSH_25	0x01
+	mask	RD_DFTHRSH_50	0x02
+	mask	RD_DFTHRSH_63	0x03
+	mask	RD_DFTHRSH_75	0x04
+	mask	RD_DFTHRSH_85	0x05
+	mask	RD_DFTHRSH_90	0x06
+	mask	RD_DFTHRSH_MAX	0x07
+	mask	WR_DFTHRSH_MIN	0x00
+	mask	WR_DFTHRSH_25	0x10
+	mask	WR_DFTHRSH_50	0x20
+	mask	WR_DFTHRSH_63	0x30
+	mask	WR_DFTHRSH_75	0x40
+	mask	WR_DFTHRSH_85	0x50
+	mask	WR_DFTHRSH_90	0x60
+	mask	WR_DFTHRSH_MAX	0x70
+	count	4
+	dont_generate_debug_code
+}
+
+register SG_CACHE_PRE {
+	access_mode WO
+	address			0x0fc
+	mask	SG_ADDR_MASK	0xf8
+	field	LAST_SEG	0x02
+	field	LAST_SEG_DONE	0x01
+	dont_generate_debug_code
+}
+
+register SG_CACHE_SHADOW {
+	access_mode RO
+	address			0x0fc
+	mask	SG_ADDR_MASK	0xf8
+	field	LAST_SEG	0x02
+	field	LAST_SEG_DONE	0x01
+	dont_generate_debug_code
+}
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE).  The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space.  This should work regardless of
+ * whether the bios has been installed.
+ */
+
+scratch_ram {
+	address		0x020
+	size		58
+
+	/*
+	 * 1 byte per target starting at this address for configuration values
+	 */
+	BUSY_TARGETS {
+		alias		TARG_SCSIRATE
+		size		16
+		dont_generate_debug_code
+	}
+	/*
+	 * Bit vector of targets that have ULTRA enabled as set by
+	 * the BIOS.  The Sequencer relies on a per-SCB field to
+	 * control whether to enable Ultra transfers or not.  During
+	 * initialization, we read this field and reuse it for 2
+	 * entries in the busy target table.
+	 */
+	ULTRA_ENB {
+		alias		CMDSIZE_TABLE
+		size		2
+		count		2
+		dont_generate_debug_code
+	}
+	/*
+	 * Bit vector of targets that have disconnection disabled as set by
+	 * the BIOS.  The Sequencer relies in a per-SCB field to control the
+	 * disconnect priveldge.  During initialization, we read this field
+	 * and reuse it for 2 entries in the busy target table.
+	 */
+	DISC_DSB {
+		size		2
+		count		6
+		dont_generate_debug_code
+	}
+	CMDSIZE_TABLE_TAIL {
+		size		4
+	}
+	/*
+	 * Partial transfer past cacheline end to be
+	 * transferred using an extra S/G.
+	 */
+	MWI_RESIDUAL {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * SCBID of the next SCB to be started by the controller.
+	 */
+	NEXT_QUEUED_SCB {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * Single byte buffer used to designate the type or message
+	 * to send to a target.
+	 */
+	MSG_OUT {
+		size		1
+		dont_generate_debug_code
+	}
+	/* Parameters for DMA Logic */
+	DMAPARAMS {
+		size		1
+		count		12
+		field	PRELOADEN	0x80
+		field	WIDEODD		0x40
+		field	SCSIEN		0x20
+		field	SDMAEN		0x10
+		field	SDMAENACK	0x10
+		field	HDMAEN		0x08
+		field	HDMAENACK	0x08
+		field	DIRECTION	0x04	/* Set indicates PCI->SCSI */
+		field	FIFOFLUSH	0x02
+		field	FIFORESET	0x01
+		dont_generate_debug_code
+	}
+	SEQ_FLAGS {
+		size		1
+		field	NOT_IDENTIFIED		0x80
+		field	NO_CDB_SENT		0x40
+		field	TARGET_CMD_IS_TAGGED	0x40
+		field	DPHASE			0x20
+		/* Target flags */
+		field	TARG_CMD_PENDING	0x10
+		field	CMDPHASE_PENDING	0x08
+		field	DPHASE_PENDING		0x04
+		field	SPHASE_PENDING		0x02
+		field	NO_DISCONNECT		0x01
+	}
+	/*
+	 * Temporary storage for the
+	 * target/channel/lun of a
+	 * reconnecting target
+	 */
+	SAVED_SCSIID {
+		size		1
+		dont_generate_debug_code
+	}
+	SAVED_LUN {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * The last bus phase as seen by the sequencer. 
+	 */
+	LASTPHASE {
+		size		1
+		field	CDI		0x80
+		field	IOI		0x40
+		field	MSGI		0x20
+		mask	PHASE_MASK	CDI|IOI|MSGI
+		mask	P_DATAOUT	0x00
+		mask	P_DATAIN	IOI
+		mask	P_COMMAND	CDI
+		mask	P_MESGOUT	CDI|MSGI
+		mask	P_STATUS	CDI|IOI
+		mask	P_MESGIN	CDI|IOI|MSGI
+		mask	P_BUSFREE	0x01
+	}
+	/*
+	 * head of list of SCBs awaiting
+	 * selection
+	 */
+	WAITING_SCBH {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * head of list of SCBs that are
+	 * disconnected.  Used for SCB
+	 * paging.
+	 */
+	DISCONNECTED_SCBH {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * head of list of SCBs that are
+	 * not in use.  Used for SCB paging.
+	 */
+	FREE_SCBH {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * head of list of SCBs that have
+	 * completed but have not been
+	 * put into the qoutfifo.
+	 */
+	COMPLETE_SCBH {
+		size		1
+	}
+	/*
+	 * Address of the hardware scb array in the host.
+	 */
+	HSCB_ADDR {
+		size		4
+		dont_generate_debug_code
+	}
+	/*
+	 * Base address of our shared data with the kernel driver in host
+	 * memory.  This includes the qoutfifo and target mode
+	 * incoming command queue.
+	 */
+	SHARED_DATA_ADDR {
+		size		4
+		dont_generate_debug_code
+	}
+	KERNEL_QINPOS {
+		size		1
+		dont_generate_debug_code
+	}
+	QINPOS {
+		size		1
+		dont_generate_debug_code
+	}
+	QOUTPOS {
+		size		1
+		dont_generate_debug_code
+	}
+	/*
+	 * Kernel and sequencer offsets into the queue of
+	 * incoming target mode command descriptors.  The
+	 * queue is full when the KERNEL_TQINPOS == TQINPOS.
+	 */
+	KERNEL_TQINPOS {
+		size		1
+		dont_generate_debug_code
+	}
+	TQINPOS {
+		size		1
+		dont_generate_debug_code
+	}
+	ARG_1 {
+		size		1
+		count		1
+		mask	SEND_MSG		0x80
+		mask	SEND_SENSE		0x40
+		mask	SEND_REJ		0x20
+		mask	MSGOUT_PHASEMIS		0x10
+		mask	EXIT_MSG_LOOP		0x08
+		mask	CONT_MSG_LOOP		0x04
+		mask	CONT_TARG_SESSION	0x02
+		alias	RETURN_1
+		dont_generate_debug_code
+	}
+	ARG_2 {
+		size		1
+		alias	RETURN_2
+		dont_generate_debug_code
+	}
+
+	/*
+	 * Snapshot of MSG_OUT taken after each message is sent.
+	 */
+	LAST_MSG {
+		size		1
+		alias	TARG_IMMEDIATE_SCB
+		dont_generate_debug_code
+	}
+
+	/*
+	 * Sequences the kernel driver has okayed for us.  This allows
+	 * the driver to do things like prevent initiator or target
+	 * operations.
+	 */
+	SCSISEQ_TEMPLATE {
+		size		1
+		field	ENSELO		0x40
+		field	ENSELI		0x20
+		field	ENRSELI		0x10
+		field	ENAUTOATNO	0x08
+		field	ENAUTOATNI	0x04
+		field	ENAUTOATNP	0x02
+		dont_generate_debug_code
+	}
+}
+
+scratch_ram {
+	address		0x056
+	size		4
+	/*
+	 * These scratch ram locations are initialized by the 274X BIOS.
+	 * We reuse them after capturing the BIOS settings during
+	 * initialization.
+	 */
+
+	/*
+	 * The initiator specified tag for this target mode transaction.
+	 */
+	HA_274_BIOSGLOBAL {
+		size	1
+		field	HA_274_EXTENDED_TRANS	0x01
+		alias	INITIATOR_TAG
+		count		1
+		dont_generate_debug_code
+	}
+
+	SEQ_FLAGS2 {
+		size	1
+		field	SCB_DMA			0x01
+		field	TARGET_MSG_PENDING	0x02
+		dont_generate_debug_code
+	}
+}
+
+scratch_ram {
+	address		0x05a
+	size		6
+	/*
+	 * These are reserved registers in the card's scratch ram on the 2742.
+	 * The EISA configuraiton chip is mapped here.  On Rev E. of the
+	 * aic7770, the sequencer can use this area for scratch, but the
+	 * host cannot directly access these registers.  On later chips, this
+	 * area can be read and written by both the host and the sequencer.
+	 * Even on later chips, many of these locations are initialized by
+	 * the BIOS.
+	 */
+	SCSICONF {
+		size		1
+		count		12
+		field	TERM_ENB	0x80
+		field	RESET_SCSI	0x40
+		field	ENSPCHK		0x20
+		mask	HSCSIID		0x07	/* our SCSI ID */
+		mask	HWSCSIID	0x0f	/* our SCSI ID if Wide Bus */
+		dont_generate_debug_code
+	}
+	INTDEF {
+		address		0x05c
+		size		1
+		count		1
+		field	EDGE_TRIG	0x80
+		mask	VECTOR		0x0f
+		dont_generate_debug_code
+	}
+	HOSTCONF {
+		address		0x05d
+		size		1
+		count		1
+		dont_generate_debug_code
+	}
+	HA_274_BIOSCTRL	{
+		address		0x05f
+		size		1
+		count		1
+		mask	BIOSMODE		0x30
+		mask	BIOSDISABLED		0x30	
+		field	CHANNEL_B_PRIMARY	0x08
+		dont_generate_debug_code
+	}
+}
+
+scratch_ram {
+	address		0x070
+	size		16
+
+	/*
+	 * Per target SCSI offset values for Ultra2 controllers.
+	 */
+	TARG_OFFSET {
+		size		16
+		count		1
+		dont_generate_debug_code
+	}
+}
+
+const TID_SHIFT		4
+const SCB_LIST_NULL	0xff
+const TARGET_CMD_CMPLT	0xfe
+
+const CCSGADDR_MAX	0x80
+const CCSGRAM_MAXSEGS	16
+
+/* WDTR Message values */
+const BUS_8_BIT			0x00
+const BUS_16_BIT		0x01
+const BUS_32_BIT		0x02
+
+/* Offset maximums */
+const MAX_OFFSET_8BIT		0x0f
+const MAX_OFFSET_16BIT		0x08
+const MAX_OFFSET_ULTRA2		0x7f
+const MAX_OFFSET		0x7f
+const HOST_MSG			0xff
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT	0x05
+
+const STATUS_BUSY		0x08
+const STATUS_QUEUE_FULL	0x28
+const TARGET_DATA_IN		1
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+/* Offsets into the SCBID array where different data is stored */
+const QOUTFIFO_OFFSET download
+const QINFIFO_OFFSET download
+const CACHESIZE_MASK download
+const INVERTED_CACHESIZE_MASK download
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq
new file mode 100644
index 0000000..e60041e
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx.seq
@@ -0,0 +1,2399 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#58 $"
+PATCH_ARG_LIST = "struct ahc_softc *ahc"
+PREFIX = "ahc_"
+
+#include "aic7xxx.reg"
+#include "scsi_message.h"
+
+/*
+ * A few words on the waiting SCB list:
+ * After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration.  The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time.  This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection.  The solution used here is to 
+ * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
+ * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
+ * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
+ * this list every time a request sense occurs or after completing a non-tagged
+ * command for which a second SCB has been queued.  The sequencer will
+ * automatically consume the entries.
+ */
+
+bus_free_sel:
+	/*
+	 * Turn off the selection hardware.  We need to reset the
+	 * selection request in order to perform a new selection.
+	 */
+	and	SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP;
+	and	SIMODE1, ~ENBUSFREE;
+poll_for_work:
+	call	clear_target_state;
+	and	SXFRCTL0, ~SPIOEN;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		clr	SCSIBUSL;
+	}
+	test	SCSISEQ, ENSELO	jnz poll_for_selection;
+	if ((ahc->features & AHC_TWIN) != 0) {
+		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+		test	SCSISEQ, ENSELO		jnz poll_for_selection;
+	}
+	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+poll_for_work_loop:
+	if ((ahc->features & AHC_TWIN) != 0) {
+		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+	}
+	test	SSTAT0, SELDO|SELDI	jnz selection;
+test_queue:
+	/* Has the driver posted any work for us? */
+BEGIN_CRITICAL;
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+	} else {
+		mov	A, QINPOS;
+		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
+	}
+	mov	ARG_1, NEXT_QUEUED_SCB;
+
+	/*
+	 * We have at least one queued SCB now and we don't have any 
+	 * SCBs in the list of SCBs awaiting selection.  Allocate a
+	 * card SCB for the host's SCB and get to work on it.
+	 */
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		mov	ALLZEROS	call	get_free_or_disc_scb;
+	} else {
+		/* In the non-paging case, the SCBID == hardware SCB index */
+		mov	SCBPTR, ARG_1;
+	}
+	or	SEQ_FLAGS2, SCB_DMA;
+END_CRITICAL;
+dma_queued_scb:
+	/*
+	 * DMA the SCB from host ram into the current SCB location.
+	 */
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	ARG_1	call dma_scb;
+	/*
+	 * Check one last time to see if this SCB was canceled
+	 * before we completed the DMA operation.  If it was,
+	 * the QINFIFO next pointer will not match our saved
+	 * value.
+	 */
+	mov	A, ARG_1;
+BEGIN_CRITICAL;
+	cmp	NEXT_QUEUED_SCB, A jne abort_qinscb;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		cmp	SCB_TAG, A je . + 2;
+		mvi	SCB_MISMATCH call set_seqint;
+	}
+	mov	NEXT_QUEUED_SCB, SCB_NEXT;
+	mov	SCB_NEXT,WAITING_SCBH;
+	mov	WAITING_SCBH, SCBPTR;
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		mov	NONE, SNSCB_QOFF;
+	} else {
+		inc	QINPOS;
+	}
+	and	SEQ_FLAGS2, ~SCB_DMA;
+END_CRITICAL;
+start_waiting:
+	/*
+	 * Start the first entry on the waiting SCB list.
+	 */
+	mov	SCBPTR, WAITING_SCBH;
+	call	start_selection;
+
+poll_for_selection:
+	/*
+	 * Twin channel devices cannot handle things like SELTO
+	 * interrupts on the "background" channel.  So, while
+	 * selecting, keep polling the current channel until
+	 * either a selection or reselection occurs.
+	 */
+	test	SSTAT0, SELDO|SELDI	jz poll_for_selection;
+
+selection:
+	/*
+	 * We aren't expecting a bus free, so interrupt
+	 * the kernel driver if it happens.
+	 */
+	mvi	CLRSINT1,CLRBUSFREE;
+	if ((ahc->features & AHC_DT) == 0) {
+		or	SIMODE1, ENBUSFREE;
+	}
+
+	/*
+	 * Guard against a bus free after (re)selection
+	 * but prior to enabling the busfree interrupt.  SELDI
+	 * and SELDO will be cleared in that case.
+	 */
+	test	SSTAT0, SELDI|SELDO	jz bus_free_sel;
+	test	SSTAT0,SELDO	jnz select_out;
+select_in:
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+			test	SSTAT0, TARGET	jz initiator_reselect;
+		}
+		mvi	CLRSINT0, CLRSELDI;
+
+		/*
+		 * We've just been selected.  Assert BSY and
+		 * setup the phase for receiving messages
+		 * from the target.
+		 */
+		mvi	SCSISIGO, P_MESGOUT|BSYO;
+
+		/*
+		 * Setup the DMA for sending the identify and
+		 * command information.
+		 */
+		mvi	SEQ_FLAGS, CMDPHASE_PENDING;
+
+		mov     A, TQINPOS;
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mvi	DINDEX, CCHADDR;
+			mvi	SHARED_DATA_ADDR call set_32byte_addr;
+			mvi	CCSCBCTL, CCSCBRESET;
+		} else {
+			mvi	DINDEX, HADDR;
+			mvi	SHARED_DATA_ADDR call set_32byte_addr;
+			mvi	DFCNTRL, FIFORESET;
+		}
+
+		/* Initiator that selected us */
+		and	SAVED_SCSIID, SELID_MASK, SELID;
+		/* The Target ID we were selected at */
+		if ((ahc->features & AHC_MULTI_TID) != 0) {
+			and	A, OID, TARGIDIN;
+		} else if ((ahc->features & AHC_ULTRA2) != 0) {
+			and	A, OID, SCSIID_ULTRA2;
+		} else {
+			and	A, OID, SCSIID;
+		}
+		or	SAVED_SCSIID, A;
+		if ((ahc->features & AHC_TWIN) != 0) {
+			test 	SBLKCTL, SELBUSB jz . + 2;
+			or	SAVED_SCSIID, TWIN_CHNLB;
+		}
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, SAVED_SCSIID;
+		} else {
+			mov	DFDAT, SAVED_SCSIID;
+		}
+
+		/*
+		 * If ATN isn't asserted, the target isn't interested
+		 * in talking to us.  Go directly to bus free.
+		 * XXX SCSI-1 may require us to assume lun 0 if
+		 * ATN is false.
+		 */
+		test	SCSISIGI, ATNI	jz	target_busfree;
+
+		/*
+		 * Watch ATN closely now as we pull in messages from the
+		 * initiator.  We follow the guidlines from section 6.5
+		 * of the SCSI-2 spec for what messages are allowed when.
+		 */
+		call	target_inb;
+
+		/*
+		 * Our first message must be one of IDENTIFY, ABORT, or
+		 * BUS_DEVICE_RESET.
+		 */
+		test	DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
+		/* Store for host */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, DINDEX;
+		} else {
+			mov	DFDAT, DINDEX;
+		}
+		and	SAVED_LUN, MSG_IDENTIFY_LUNMASK, DINDEX;
+
+		/* Remember for disconnection decision */
+		test	DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
+		/* XXX Honor per target settings too */
+		or	SEQ_FLAGS, NO_DISCONNECT;
+
+		test	SCSISIGI, ATNI	jz	ident_messages_done;
+		call	target_inb;
+		/*
+		 * If this is a tagged request, the tagged message must
+		 * immediately follow the identify.  We test for a valid
+		 * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
+		 * < MSG_IGN_WIDE_RESIDUE.
+		 */
+		add	A, -MSG_SIMPLE_Q_TAG, DINDEX;
+		jnc	ident_messages_done_msg_pending;
+		add	A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
+		jc	ident_messages_done_msg_pending;
+
+		/* Store for host */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, DINDEX;
+		} else {
+			mov	DFDAT, DINDEX;
+		}
+		
+		/*
+		 * If the initiator doesn't feel like providing a tag number,
+		 * we've got a failed selection and must transition to bus
+		 * free.
+		 */
+		test	SCSISIGI, ATNI	jz	target_busfree;
+
+		/*
+		 * Store the tag for the host.
+		 */
+		call	target_inb;
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, DINDEX;
+		} else {
+			mov	DFDAT, DINDEX;
+		}
+		mov	INITIATOR_TAG, DINDEX;
+		or	SEQ_FLAGS, TARGET_CMD_IS_TAGGED;
+
+ident_messages_done:
+		/* Terminate the ident list */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mvi	CCSCBRAM, SCB_LIST_NULL;
+		} else {
+			mvi	DFDAT, SCB_LIST_NULL;
+		}
+		or	SEQ_FLAGS, TARG_CMD_PENDING;
+		test	SEQ_FLAGS2, TARGET_MSG_PENDING
+			jnz target_mesgout_pending;
+		test	SCSISIGI, ATNI jnz target_mesgout_continue;
+		jmp	target_ITloop;
+
+
+ident_messages_done_msg_pending:
+		or	SEQ_FLAGS2, TARGET_MSG_PENDING;
+		jmp	ident_messages_done;
+
+		/*
+		 * Pushed message loop to allow the kernel to
+		 * run it's own target mode message state engine.
+		 */
+host_target_message_loop:
+		mvi	HOST_MSG_LOOP call set_seqint;
+		cmp	RETURN_1, EXIT_MSG_LOOP	je target_ITloop;
+		test	SSTAT0, SPIORDY jz .;
+		jmp	host_target_message_loop;
+	}
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target yet.
+ */
+initiator_reselect:
+	/* XXX test for and handle ONE BIT condition */
+	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+	and	SAVED_SCSIID, SELID_MASK, SELID;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		and	A, OID, SCSIID_ULTRA2;
+	} else {
+		and	A, OID, SCSIID;
+	}
+	or	SAVED_SCSIID, A;
+	if ((ahc->features & AHC_TWIN) != 0) {
+		test	SBLKCTL, SELBUSB	jz . + 2;
+		or	SAVED_SCSIID, TWIN_CHNLB;
+	}
+	mvi	CLRSINT0, CLRSELDI;
+	jmp	ITloop;
+}
+
+abort_qinscb:
+	call	add_scb_to_free_list;
+	jmp	poll_for_work_loop;
+
+start_selection:
+	/*
+	 * If bus reset interrupts have been disabled (from a previous
+	 * reset), re-enable them now.  Resets are only of interest
+	 * when we have outstanding transactions, so we can safely
+	 * defer re-enabling the interrupt until, as an initiator,
+	 * we start sending out transactions again.
+	 */
+	test	SIMODE1, ENSCSIRST	jnz . + 3;
+	mvi	CLRSINT1, CLRSCSIRSTI;
+	or	SIMODE1, ENSCSIRST;
+	if ((ahc->features & AHC_TWIN) != 0) {
+		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+		test	SCB_SCSIID, TWIN_CHNLB jz . + 2;
+		or	SINDEX, SELBUSB;
+		mov	SBLKCTL,SINDEX;		/* select channel */
+	}
+initialize_scsiid:
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		mov	SCSIID_ULTRA2, SCB_SCSIID;
+	} else if ((ahc->features & AHC_TWIN) != 0) {
+		and	SCSIID, TWIN_TID|OID, SCB_SCSIID;
+	} else {
+		mov	SCSIID, SCB_SCSIID;
+	}
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		mov	SINDEX, SCSISEQ_TEMPLATE;
+		test	SCB_CONTROL, TARGET_SCB jz . + 2;
+		or	SINDEX, TEMODE;
+		mov	SCSISEQ, SINDEX ret;
+	} else {
+		mov	SCSISEQ, SCSISEQ_TEMPLATE ret;
+	}
+
+/*
+ * Initialize transfer settings with SCB provided settings.
+ */
+set_transfer_settings:
+	if ((ahc->features & AHC_ULTRA) != 0) {
+		test	SCB_CONTROL, ULTRAENB jz . + 2;
+		or	SXFRCTL0, FAST20;
+	} 
+	/*
+	 * Initialize SCSIRATE with the appropriate value for this target.
+	 */
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		bmov	SCSIRATE, SCB_SCSIRATE, 2 ret;
+	} else {
+		mov	SCSIRATE, SCB_SCSIRATE ret;
+	}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * We carefully toggle SPIOEN to allow us to return the 
+ * message byte we receive so it can be checked prior to
+ * driving REQ on the bus for the next byte.
+ */
+target_inb:
+	/*
+	 * Drive REQ on the bus by enabling SCSI PIO.
+	 */
+	or	SXFRCTL0, SPIOEN;
+	/* Wait for the byte */
+	test	SSTAT0, SPIORDY jz .;
+	/* Prevent our read from triggering another REQ */
+	and	SXFRCTL0, ~SPIOEN;
+	/* Save latched contents */
+	mov	DINDEX, SCSIDATL ret;
+}
+
+/*
+ * After the selection, remove this SCB from the "waiting SCB"
+ * list.  This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH.  Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
+select_out:
+	/* Turn off the selection hardware */
+	and	SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
+	mov	SCBPTR, WAITING_SCBH;
+	mov	WAITING_SCBH,SCB_NEXT;
+	mov	SAVED_SCSIID, SCB_SCSIID;
+	and	SAVED_LUN, LID, SCB_LUN;
+	call	set_transfer_settings;
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		test	SSTAT0, TARGET	jz initiator_select;
+
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+
+		/*
+		 * Put tag in connonical location since not
+		 * all connections have an SCB.
+		 */
+		mov	INITIATOR_TAG, SCB_TARGET_ITAG;
+
+		/*
+		 * We've just re-selected an initiator.
+		 * Assert BSY and setup the phase for
+		 * sending our identify messages.
+		 */
+		mvi	P_MESGIN|BSYO call change_phase;
+		mvi	CLRSINT0, CLRSELDO;
+
+		/*
+		 * Start out with a simple identify message.
+		 */
+		or	SAVED_LUN, MSG_IDENTIFYFLAG call target_outb;
+
+		/*
+		 * If we are the result of a tagged command, send
+		 * a simple Q tag and the tag id.
+		 */
+		test	SCB_CONTROL, TAG_ENB	jz . + 3;
+		mvi	MSG_SIMPLE_Q_TAG call target_outb;
+		mov	SCB_TARGET_ITAG call target_outb;
+target_synccmd:
+		/*
+		 * Now determine what phases the host wants us
+		 * to go through.
+		 */
+		mov	SEQ_FLAGS, SCB_TARGET_PHASES;
+		
+		test	SCB_CONTROL, MK_MESSAGE	jz target_ITloop;
+		mvi	P_MESGIN|BSYO call change_phase;
+		jmp	host_target_message_loop;
+target_ITloop:
+		/*
+		 * Start honoring ATN signals now that
+		 * we properly identified ourselves.
+		 */
+		test	SCSISIGI, ATNI			jnz target_mesgout;
+		test	SEQ_FLAGS, CMDPHASE_PENDING	jnz target_cmdphase;
+		test	SEQ_FLAGS, DPHASE_PENDING	jnz target_dphase;
+		test	SEQ_FLAGS, SPHASE_PENDING	jnz target_sphase;
+
+		/*
+		 * No more work to do.  Either disconnect or not depending
+		 * on the state of NO_DISCONNECT.
+		 */
+		test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; 
+		mvi	TARG_IMMEDIATE_SCB, SCB_LIST_NULL;
+		call	complete_target_cmd;
+		if ((ahc->flags & AHC_PAGESCBS) != 0) {
+			mov	ALLZEROS	call	get_free_or_disc_scb;
+		}
+		cmp	TARG_IMMEDIATE_SCB, SCB_LIST_NULL je .;
+		mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+		mov	TARG_IMMEDIATE_SCB call dma_scb;
+		call	set_transfer_settings;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+		jmp	target_synccmd;
+
+target_mesgout:
+		mvi	SCSISIGO, P_MESGOUT|BSYO;
+target_mesgout_continue:
+		call	target_inb;
+target_mesgout_pending:
+		and	SEQ_FLAGS2, ~TARGET_MSG_PENDING;
+		/* Local Processing goes here... */
+		jmp	host_target_message_loop;
+		
+target_disconnect:
+		mvi	P_MESGIN|BSYO call change_phase;
+		test	SEQ_FLAGS, DPHASE	jz . + 2;
+		mvi	MSG_SAVEDATAPOINTER call target_outb;
+		mvi	MSG_DISCONNECT call target_outb;
+
+target_busfree_wait:
+		/* Wait for preceding I/O session to complete. */
+		test	SCSISIGI, ACKI jnz .;
+target_busfree:
+		and	SIMODE1, ~ENBUSFREE;
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			clr	SCSIBUSL;
+		}
+		clr	SCSISIGO;
+		mvi	LASTPHASE, P_BUSFREE;
+		call	complete_target_cmd;
+		jmp	poll_for_work;
+
+target_cmdphase:
+		/*
+		 * The target has dropped ATN (doesn't want to abort or BDR)
+		 * and we believe this selection to be valid.  If the ring
+		 * buffer for new commands is full, return busy or queue full.
+		 */
+		if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+			and	A, HOST_TQINPOS, HS_MAILBOX;
+		} else {
+			mov	A, KERNEL_TQINPOS;
+		}
+		cmp	TQINPOS, A jne tqinfifo_has_space;
+		mvi	P_STATUS|BSYO call change_phase;
+		test	SEQ_FLAGS, TARGET_CMD_IS_TAGGED jz . + 3;
+		mvi	STATUS_QUEUE_FULL call target_outb;
+		jmp	target_busfree_wait;
+		mvi	STATUS_BUSY call target_outb;
+		jmp	target_busfree_wait;
+tqinfifo_has_space:	
+		mvi	P_COMMAND|BSYO call change_phase;
+		call	target_inb;
+		mov	A, DINDEX;
+		/* Store for host */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, A;
+		} else {
+			mov	DFDAT, A;
+		}
+
+		/*
+		 * Determine the number of bytes to read
+		 * based on the command group code via table lookup.
+		 * We reuse the first 8 bytes of the TARG_SCSIRATE
+		 * BIOS array for this table. Count is one less than
+		 * the total for the command since we've already fetched
+		 * the first byte.
+		 */
+		shr	A, CMD_GROUP_CODE_SHIFT;
+		add	SINDEX, CMDSIZE_TABLE, A;
+		mov	A, SINDIR;
+
+		test	A, 0xFF jz command_phase_done;
+		or	SXFRCTL0, SPIOEN;
+command_loop:
+		test	SSTAT0, SPIORDY jz .;
+		cmp	A, 1 jne . + 2;
+		and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, SCSIDATL;
+		} else {
+			mov	DFDAT, SCSIDATL;
+		}
+		dec	A;
+		test	A, 0xFF jnz command_loop;
+
+command_phase_done:
+		and	SEQ_FLAGS, ~CMDPHASE_PENDING;
+		jmp	target_ITloop;
+
+target_dphase:
+		/*
+		 * Data phases on the bus are from the
+		 * perspective of the initiator.  The dma
+		 * code looks at LASTPHASE to determine the
+		 * data direction of the DMA.  Toggle it for
+		 * target transfers.
+		 */
+		xor	LASTPHASE, IOI, SCB_TARGET_DATA_DIR;
+		or	SCB_TARGET_DATA_DIR, BSYO call change_phase;
+		jmp	p_data;
+
+target_sphase:
+		mvi	P_STATUS|BSYO call change_phase;
+		mvi	LASTPHASE, P_STATUS;
+		mov	SCB_SCSI_STATUS call target_outb;
+		/* XXX Watch for ATN or parity errors??? */
+		mvi	SCSISIGO, P_MESGIN|BSYO;
+		/* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
+		mov	ALLZEROS call target_outb;
+		jmp	target_busfree_wait;
+	
+complete_target_cmd:
+		test	SEQ_FLAGS, TARG_CMD_PENDING	jnz . + 2;
+		mov	SCB_TAG jmp complete_post;
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			/* Set the valid byte */
+			mvi	CCSCBADDR, 24;
+			mov	CCSCBRAM, ALLONES;
+			mvi	CCHCNT, 28;
+			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			test	CCSCBCTL, CCSCBDONE jz .;
+			clr	CCSCBCTL;
+		} else {
+			/* Set the valid byte */
+			or	DFCNTRL, FIFORESET;
+			mvi	DFWADDR, 3; /* Third 64bit word or byte 24 */
+			mov	DFDAT, ALLONES;
+			mvi	28	call set_hcnt;
+			or	DFCNTRL, HDMAEN|FIFOFLUSH;
+			call	dma_finish;
+		}
+		inc	TQINPOS;
+		mvi	INTSTAT,CMDCMPLT ret;
+	}
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+initiator_select:
+	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+	/*
+	 * As soon as we get a successful selection, the target
+	 * should go into the message out phase since we have ATN
+	 * asserted.
+	 */
+	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
+	mvi	SEQ_FLAGS, NO_CDB_SENT;
+	mvi	CLRSINT0, CLRSELDO;
+
+	/*
+	 * Main loop for information transfer phases.  Wait for the
+	 * target to assert REQ before checking MSG, C/D and I/O for
+	 * the bus phase.
+	 */
+mesgin_phasemis:
+ITloop:
+	call	phase_lock;
+
+	mov	A, LASTPHASE;
+
+	test	A, ~P_DATAIN	jz p_data;
+	cmp	A,P_COMMAND	je p_command;
+	cmp	A,P_MESGOUT	je p_mesgout;
+	cmp	A,P_STATUS	je p_status;
+	cmp	A,P_MESGIN	je p_mesgin;
+
+	mvi	BAD_PHASE call set_seqint;
+	jmp	ITloop;			/* Try reading the bus again. */
+
+await_busfree:
+	and	SIMODE1, ~ENBUSFREE;
+	mov	NONE, SCSIDATL;		/* Ack the last byte */
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		clr	SCSIBUSL;	/* Prevent bit leakage durint SELTO */
+	}
+	and	SXFRCTL0, ~SPIOEN;
+	mvi	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT;
+	test	SSTAT1,REQINIT|BUSFREE	jz .;
+	test	SSTAT1, BUSFREE jnz poll_for_work;
+	mvi	MISSED_BUSFREE call set_seqint;
+}
+	
+clear_target_state:
+	/*
+	 * We assume that the kernel driver may reset us
+	 * at any time, even in the middle of a DMA, so
+	 * clear DFCNTRL too.
+	 */
+	clr	DFCNTRL;
+	or	SXFRCTL0, CLRSTCNT|CLRCHN;
+
+	/*
+	 * We don't know the target we will connect to,
+	 * so default to narrow transfers to avoid
+	 * parity problems.
+	 */
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		bmov	SCSIRATE, ALLZEROS, 2;
+	} else {
+		clr	SCSIRATE;
+		if ((ahc->features & AHC_ULTRA) != 0) {
+			and	SXFRCTL0, ~(FAST20);
+		}
+	}
+	mvi	LASTPHASE, P_BUSFREE;
+	/* clear target specific flags */
+	mvi	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
+
+sg_advance:
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+	adc	SCB_RESIDUAL_SGPTR[1],A;
+	adc	SCB_RESIDUAL_SGPTR[2],A;
+	adc	SCB_RESIDUAL_SGPTR[3],A ret;
+
+if ((ahc->features & AHC_CMD_CHAN) != 0) {
+disable_ccsgen:
+	test	CCSGCTL, CCSGEN jz return;
+	test	CCSGCTL, CCSGDONE jz .;
+disable_ccsgen_fetch_done:
+	clr	CCSGCTL;
+	test	CCSGCTL, CCSGEN jnz .;
+	ret;
+idle_loop:
+	/*
+	 * Do we need any more segments for this transfer?
+	 */
+	test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz return;
+
+	/* Did we just finish fetching segs? */
+	cmp	CCSGCTL, CCSGEN|CCSGDONE je idle_sgfetch_complete;
+
+	/* Are we actively fetching segments? */
+	test	CCSGCTL, CCSGEN jnz return;
+
+	/*
+	 * Do we have any prefetch left???
+	 */
+	cmp	CCSGADDR, SG_PREFETCH_CNT jne idle_sg_avail;
+
+	/*
+	 * Need to fetch segments, but we can only do that
+	 * if the command channel is completely idle.  Make
+	 * sure we don't have an SCB prefetch going on.
+	 */
+	test	CCSCBCTL, CCSCBEN jnz return;
+
+	/*
+	 * We fetch a "cacheline aligned" and sized amount of data
+	 * so we don't end up referencing a non-existent page.
+	 * Cacheline aligned is in quotes because the kernel will
+	 * set the prefetch amount to a reasonable level if the
+	 * cacheline size is unknown.
+	 */
+	mvi	CCHCNT, SG_PREFETCH_CNT;
+	and	CCHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+	bmov	CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
+	mvi	CCSGCTL, CCSGEN|CCSGRESET ret;
+idle_sgfetch_complete:
+	call	disable_ccsgen_fetch_done;
+	and	CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+idle_sg_avail:
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		/* Does the hardware have space for another SG entry? */
+		test	DFSTATUS, PRELOAD_AVAIL jz return;
+		bmov 	HADDR, CCSGRAM, 7;
+		bmov	SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+			mov	SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
+		}
+		call	sg_advance;
+		mov	SINDEX, SCB_RESIDUAL_SGPTR[0];
+		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+		or	SINDEX, LAST_SEG;
+		mov	SG_CACHE_PRE, SINDEX;
+		/* Load the segment */
+		or	DFCNTRL, PRELOADEN;
+	}
+	ret;
+}
+
+if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+/*
+ * Calculate the trailing portion of this S/G segment that cannot
+ * be transferred using memory write and invalidate PCI transactions.  
+ * XXX Can we optimize this for PCI writes only???
+ */
+calc_mwi_residual:
+	/*
+	 * If the ending address is on a cacheline boundary,
+	 * there is no need for an extra segment.
+	 */
+	mov	A, HCNT[0];
+	add	A, A, HADDR[0];
+	and	A, CACHESIZE_MASK;
+	test	A, 0xFF jz return;
+
+	/*
+	 * If the transfer is less than a cachline,
+	 * there is no need for an extra segment.
+	 */
+	test	HCNT[1], 0xFF	jnz calc_mwi_residual_final;
+	test	HCNT[2], 0xFF	jnz calc_mwi_residual_final;
+	add	NONE, INVERTED_CACHESIZE_MASK, HCNT[0];
+	jnc	return;
+
+calc_mwi_residual_final:
+	mov	MWI_RESIDUAL, A;
+	not	A;
+	inc	A;
+	add	HCNT[0], A;
+	adc	HCNT[1], -1;
+	adc	HCNT[2], -1 ret;
+}
+
+p_data:
+	test	SEQ_FLAGS,NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed;
+	mvi	PROTO_VIOLATION call set_seqint;
+p_data_allowed:
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+	} else {
+		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+	}
+	test	LASTPHASE, IOI jnz . + 2;
+	or	DMAPARAMS, DIRECTION;
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		/* We don't have any valid S/G elements */
+		mvi	CCSGADDR, SG_PREFETCH_CNT;
+	}
+	test	SEQ_FLAGS, DPHASE	jz data_phase_initialize;
+
+	/*
+	 * If we re-enter the data phase after going through another
+	 * phase, our transfer location has almost certainly been
+	 * corrupted by the interveining, non-data, transfers.  Ask
+	 * the host driver to fix us up based on the transfer residual.
+	 */
+	mvi	PDATA_REINIT	call set_seqint;
+	jmp	data_phase_loop;
+
+data_phase_initialize:
+	/* We have seen a data phase for the first time */
+	or	SEQ_FLAGS, DPHASE;
+
+	/*
+	 * Initialize the DMA address and counter from the SCB.
+	 * Also set SCB_RESIDUAL_SGPTR, including the LAST_SEG
+	 * flag in the highest byte of the data count.  We cannot
+	 * modify the saved values in the SCB until we see a save
+	 * data pointers message.
+	 */
+	if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+		/* The lowest address byte must be loaded last. */
+		mov	SCB_DATACNT[3] call set_hhaddr;
+	}
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_DATAPTR, 7;
+		bmov	SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_DATAPTR	call bcopy_7;
+		mvi	DINDEX, SCB_RESIDUAL_DATACNT + 3;
+		mvi	SCB_DATACNT + 3 call bcopy_5;
+	}
+	if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+		call	calc_mwi_residual;
+	}
+	and	SCB_RESIDUAL_SGPTR[0], ~SG_FULL_RESID;
+
+	if ((ahc->features & AHC_ULTRA2) == 0) {
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			bmov	STCNT, HCNT, 3;
+		} else {
+			call	set_stcnt_from_hcnt;
+		}
+	}
+
+data_phase_loop:
+	/* Guard against overruns */
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_inbounds;
+
+	/*
+	 * Turn on `Bit Bucket' mode, wait until the target takes
+	 * us to another phase, and then notify the host.
+	 */
+	and	DMAPARAMS, DIRECTION;
+	mov	DFCNTRL, DMAPARAMS;
+	or	SXFRCTL1,BITBUCKET;
+	if ((ahc->features & AHC_DT) == 0) {
+		test	SSTAT1,PHASEMIS	jz .;
+	} else {
+		test	SCSIPHASE, DATA_PHASE_MASK jnz .;
+	}
+	and	SXFRCTL1, ~BITBUCKET;
+	mvi	DATA_OVERRUN call set_seqint;
+	jmp	ITloop;
+
+data_phase_inbounds:
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		mov	SINDEX, SCB_RESIDUAL_SGPTR[0];
+		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+		or	SINDEX, LAST_SEG;
+		mov	SG_CACHE_PRE, SINDEX;
+		mov	DFCNTRL, DMAPARAMS;
+ultra2_dma_loop:
+		call	idle_loop;
+		/*
+		 * The transfer is complete if either the last segment
+		 * completes or the target changes phase.
+		 */
+		test	SG_CACHE_SHADOW, LAST_SEG_DONE jnz ultra2_dmafinish;
+		if ((ahc->features & AHC_DT) == 0) {
+			if ((ahc->flags & AHC_TARGETROLE) != 0) {
+				 /*
+				  * As a target, we control the phases,
+				  * so ignore PHASEMIS.
+				  */
+				test	SSTAT0, TARGET jnz ultra2_dma_loop;
+			}
+			if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+				test	SSTAT1,PHASEMIS	jz ultra2_dma_loop;
+			}
+		} else {
+			test	DFCNTRL, SCSIEN jnz ultra2_dma_loop;
+		}
+
+ultra2_dmafinish:
+		/*
+		 * The transfer has terminated either due to a phase
+		 * change, and/or the completion of the last segment.
+		 * We have two goals here.  Do as much other work
+		 * as possible while the data fifo drains on a read
+		 * and respond as quickly as possible to the standard
+		 * messages (save data pointers/disconnect and command
+		 * complete) that usually follow a data phase.
+		 */
+		if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+			/*
+			 * On chips with broken auto-flush, start
+			 * the flushing process now.  We'll poke
+			 * the chip from time to time to keep the
+			 * flush process going as we complete the
+			 * data phase.
+			 */
+			or	DFCNTRL, FIFOFLUSH;
+		}
+		/*
+		 * We assume that, even though data may still be
+		 * transferring to the host, that the SCSI side of
+		 * the DMA engine is now in a static state.  This
+		 * allows us to update our notion of where we are
+		 * in this transfer.
+		 *
+		 * If, by chance, we stopped before being able
+		 * to fetch additional segments for this transfer,
+		 * yet the last S/G was completely exhausted,
+		 * call our idle loop until it is able to load
+		 * another segment.  This will allow us to immediately
+		 * pickup on the next segment on the next data phase.
+		 *
+		 * If we happened to stop on the last segment, then
+		 * our residual information is still correct from
+		 * the idle loop and there is no need to perform
+		 * any fixups.
+		 */
+ultra2_ensure_sg:
+		test	SG_CACHE_SHADOW, LAST_SEG jz ultra2_shvalid;
+		/* Record if we've consumed all S/G entries */
+		test	SSTAT2, SHVALID	jnz residuals_correct;
+		or	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+		jmp	residuals_correct;
+
+ultra2_shvalid:
+		test	SSTAT2, SHVALID	jnz sgptr_fixup;
+		call	idle_loop;
+		jmp	ultra2_ensure_sg;
+
+sgptr_fixup:
+		/*
+		 * Fixup the residual next S/G pointer.  The S/G preload
+		 * feature of the chip allows us to load two elements
+		 * in addition to the currently active element.  We
+		 * store the bottom byte of the next S/G pointer in
+		 * the SG_CACEPTR register so we can restore the
+		 * correct value when the DMA completes.  If the next
+		 * sg ptr value has advanced to the point where higher
+		 * bytes in the address have been affected, fix them
+		 * too.
+		 */
+		test	SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+		test	SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+		add	SCB_RESIDUAL_SGPTR[1], -1;
+		adc	SCB_RESIDUAL_SGPTR[2], -1; 
+		adc	SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+		and	SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+		/* We are not the last seg */
+		and	SCB_RESIDUAL_DATACNT[3], ~SG_LAST_SEG;
+residuals_correct:
+		/*
+		 * Go ahead and shut down the DMA engine now.
+		 * In the future, we'll want to handle end of
+		 * transfer messages prior to doing this, but this
+		 * requires similar restructuring for pre-ULTRA2
+		 * controllers.
+		 */
+		test	DMAPARAMS, DIRECTION jnz ultra2_fifoempty;
+ultra2_fifoflush:
+		if ((ahc->features & AHC_DT) == 0) {
+			if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+				/*
+				 * On Rev A of the aic7890, the autoflush
+				 * feature doesn't function correctly.
+				 * Perform an explicit manual flush.  During
+				 * a manual flush, the FIFOEMP bit becomes
+				 * true every time the PCI FIFO empties
+				 * regardless of the state of the SCSI FIFO.
+				 * It can take up to 4 clock cycles for the
+				 * SCSI FIFO to get data into the PCI FIFO
+				 * and for FIFOEMP to de-assert.  Here we
+				 * guard against this condition by making
+				 * sure the FIFOEMP bit stays on for 5 full
+				 * clock cycles.
+				 */
+				or	DFCNTRL, FIFOFLUSH;
+				test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+				test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+				test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+				test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+			}
+			test	DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+		} else {
+			/*
+			 * We enable the auto-ack feature on DT capable
+			 * controllers.  This means that the controller may
+			 * have already transferred some overrun bytes into
+			 * the data FIFO and acked them on the bus.  The only
+			 * way to detect this situation is to wait for
+			 * LAST_SEG_DONE to come true on a completed transfer
+			 * and then test to see if the data FIFO is non-empty.
+			 */
+			test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL
+				jz ultra2_wait_fifoemp;
+			test	SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
+			/*
+			 * FIFOEMP can lag LAST_SEG_DONE.  Wait a few
+			 * clocks before calling this an overrun.
+			 */
+			test	DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
+			test	DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
+			test	DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
+			/* Overrun */
+			jmp	data_phase_loop;
+ultra2_wait_fifoemp:
+			test	DFSTATUS, FIFOEMP jz .;
+		}
+ultra2_fifoempty:
+		/* Don't clobber an inprogress host data transfer */
+		test	DFSTATUS, MREQPEND	jnz ultra2_fifoempty;
+ultra2_dmahalt:
+		and     DFCNTRL, ~(SCSIEN|HDMAEN);
+		test	DFCNTRL, SCSIEN|HDMAEN jnz .;
+		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+			/*
+			 * Keep HHADDR cleared for future, 32bit addressed
+			 * only, DMA operations.
+			 *
+			 * Due to bayonette style S/G handling, our residual
+			 * data must be "fixed up" once the transfer is halted.
+			 * Here we fixup the HSHADDR stored in the high byte
+			 * of the residual data cnt.  By postponing the fixup,
+			 * we can batch the clearing of HADDR with the fixup.
+			 * If we halted on the last segment, the residual is
+			 * already correct.   If we are not on the last
+			 * segment, copy the high address directly from HSHADDR.
+			 * We don't need to worry about maintaining the
+			 * SG_LAST_SEG flag as it will always be false in the
+			 * case where an update is required.
+			 */
+			or	DSCOMMAND1, HADDLDSEL0;
+			test	SG_CACHE_SHADOW, LAST_SEG jnz . + 2;
+			mov	SCB_RESIDUAL_DATACNT[3], SHADDR;
+			clr	HADDR;
+			and	DSCOMMAND1, ~HADDLDSEL0;
+		}
+	} else {
+		/* If we are the last SG block, tell the hardware. */
+		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+		  && ahc->pci_cachesize != 0) {
+			test	MWI_RESIDUAL, 0xFF jnz dma_mid_sg;
+		}
+		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg;
+		if ((ahc->flags & AHC_TARGETROLE) != 0) {
+			test	SSTAT0, TARGET jz dma_last_sg;
+			if ((ahc->bugs & AHC_TMODE_WIDEODD_BUG) != 0) {
+				test	DMAPARAMS, DIRECTION jz dma_mid_sg;
+			}
+		}
+dma_last_sg:
+		and	DMAPARAMS, ~WIDEODD;
+dma_mid_sg:
+		/* Start DMA data transfer. */
+		mov	DFCNTRL, DMAPARAMS;
+dma_loop:
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			call	idle_loop;
+		}
+		test	SSTAT0,DMADONE	jnz dma_dmadone;
+		test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
+dma_phasemis:
+		/*
+		 * We will be "done" DMAing when the transfer count goes to
+		 * zero, or the target changes the phase (in light of this,
+		 * it makes sense that the DMA circuitry doesn't ACK when
+		 * PHASEMIS is active).  If we are doing a SCSI->Host transfer,
+		 * the data FIFO should be flushed auto-magically on STCNT=0
+		 * or a phase change, so just wait for FIFO empty status.
+		 */
+dma_checkfifo:
+		test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
+dma_fifoflush:
+		test	DFSTATUS,FIFOEMP	jz dma_fifoflush;
+dma_fifoempty:
+		/* Don't clobber an inprogress host data transfer */
+		test	DFSTATUS, MREQPEND	jnz dma_fifoempty;
+
+		/*
+		 * Now shut off the DMA and make sure that the DMA
+		 * hardware has actually stopped.  Touching the DMA
+		 * counters, etc. while a DMA is active will result
+		 * in an ILLSADDR exception.
+		 */
+dma_dmadone:
+		and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+		/*
+		 * Some revisions of the aic78XX have a problem where, if the
+		 * data fifo is full, but the PCI input latch is not empty, 
+		 * HDMAEN cannot be cleared.  The fix used here is to drain
+		 * the prefetched but unused data from the data fifo until
+		 * there is space for the input latch to drain.
+		 */
+		if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+			mov	NONE, DFDAT;
+		}
+		test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+
+		/* See if we have completed this last segment */
+		test	STCNT[0], 0xff	jnz data_phase_finish;
+		test	STCNT[1], 0xff	jnz data_phase_finish;
+		test	STCNT[2], 0xff	jnz data_phase_finish;
+
+		/*
+		 * Advance the scatter-gather pointers if needed 
+		 */
+		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+		  && ahc->pci_cachesize != 0) {
+			test	MWI_RESIDUAL, 0xFF jz no_mwi_resid;
+			/*
+			 * Reload HADDR from SHADDR and setup the
+			 * count to be the size of our residual.
+			 */
+			if ((ahc->features & AHC_CMD_CHAN) != 0) {
+				bmov	HADDR, SHADDR, 4;
+				mov	HCNT, MWI_RESIDUAL;
+				bmov	HCNT[1], ALLZEROS, 2;
+			} else {
+				mvi	DINDEX, HADDR;
+				mvi	SHADDR call bcopy_4;
+				mov	MWI_RESIDUAL call set_hcnt;
+			}
+			clr	MWI_RESIDUAL;
+			jmp	sg_load_done;
+no_mwi_resid:
+		}
+		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz sg_load;
+		or	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+		jmp	data_phase_finish;
+sg_load:
+		/*
+		 * Load the next SG element's data address and length
+		 * into the DMA engine.  If we don't have hardware
+		 * to perform a prefetch, we'll have to fetch the
+		 * segment from host memory first.
+		 */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			/* Wait for the idle loop to complete */
+			test	CCSGCTL, CCSGEN jz . + 3;
+			call	idle_loop;
+			test	CCSGCTL, CCSGEN jnz . - 1;
+			bmov 	HADDR, CCSGRAM, 7;
+			/*
+			 * Workaround for flaky external SCB RAM
+			 * on certain aic7895 setups.  It seems
+			 * unable to handle direct transfers from
+			 * S/G ram to certain SCB locations.
+			 */
+			mov	SINDEX, CCSGRAM;
+			mov	SCB_RESIDUAL_DATACNT[3], SINDEX;
+		} else {
+			if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+				mov	ALLZEROS call set_hhaddr;
+			}
+			mvi	DINDEX, HADDR;
+			mvi	SCB_RESIDUAL_SGPTR	call bcopy_4;
+
+			mvi	SG_SIZEOF	call set_hcnt;
+
+			or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+			call	dma_finish;
+
+			mvi	DINDEX, HADDR;
+			call	dfdat_in_7;
+			mov	SCB_RESIDUAL_DATACNT[3], DFDAT;
+		}
+
+		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+			mov	SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
+
+			/*
+			 * The lowest address byte must be loaded
+			 * last as it triggers the computation of
+			 * some items in the PCI block.  The ULTRA2
+			 * chips do this on PRELOAD.
+			 */
+			mov	HADDR, HADDR;
+		}
+		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+		  && ahc->pci_cachesize != 0) {
+			call calc_mwi_residual;
+		}
+
+		/* Point to the new next sg in memory */
+		call	sg_advance;
+
+sg_load_done:
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			bmov	STCNT, HCNT, 3;
+		} else {
+			call	set_stcnt_from_hcnt;
+		}
+
+		if ((ahc->flags & AHC_TARGETROLE) != 0) {
+			test	SSTAT0, TARGET jnz data_phase_loop;
+		}
+	}
+data_phase_finish:
+	/*
+	 * If the target has left us in data phase, loop through
+	 * the dma code again.  In the case of ULTRA2 adapters,
+	 * we should only loop if there is a data overrun.  For
+	 * all other adapters, we'll loop after each S/G element
+	 * is loaded as well as if there is an overrun.
+	 */
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		test	SSTAT0, TARGET jnz data_phase_done;
+	}
+	if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+		test	SSTAT1, REQINIT jz .;
+		if ((ahc->features & AHC_DT) == 0) {
+			test	SSTAT1,PHASEMIS	jz data_phase_loop;
+		} else {
+			test	SCSIPHASE, DATA_PHASE_MASK jnz data_phase_loop;
+		}
+	}
+
+data_phase_done:
+	/*
+	 * After a DMA finishes, save the SG and STCNT residuals back into
+	 * the SCB.  We use STCNT instead of HCNT, since it's a reflection
+	 * of how many bytes were transferred on the SCSI (as opposed to the
+	 * host) bus.
+	 */
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		/* Kill off any pending prefetch */
+		call	disable_ccsgen;
+	}
+
+	if ((ahc->features & AHC_ULTRA2) == 0) {
+		/*
+		 * Clear the high address byte so that all other DMA
+		 * operations, which use 32bit addressing, can assume
+		 * HHADDR is 0.
+		 */
+		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+			mov	ALLZEROS call set_hhaddr;
+		}
+	}
+
+	/*
+	 * Update our residual information before the information is
+	 * lost by some other type of SCSI I/O (e.g. PIO).  If we have
+	 * transferred all data, no update is needed.
+	 *
+	 */
+	test	SCB_RESIDUAL_SGPTR, SG_LIST_NULL jnz residual_update_done;
+	if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+	  && ahc->pci_cachesize != 0) {
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			test	MWI_RESIDUAL, 0xFF jz bmov_resid;
+		}
+		mov	A, MWI_RESIDUAL;
+		add	SCB_RESIDUAL_DATACNT[0], A, STCNT[0];
+		clr	A;
+		adc	SCB_RESIDUAL_DATACNT[1], A, STCNT[1];
+		adc	SCB_RESIDUAL_DATACNT[2], A, STCNT[2];
+		clr	MWI_RESIDUAL;
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			jmp	. + 2;
+bmov_resid:
+			bmov	SCB_RESIDUAL_DATACNT, STCNT, 3;
+		}
+	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	SCB_RESIDUAL_DATACNT, STCNT, 3;
+	} else {
+		mov	SCB_RESIDUAL_DATACNT[0], STCNT[0];
+		mov	SCB_RESIDUAL_DATACNT[1], STCNT[1];
+		mov	SCB_RESIDUAL_DATACNT[2], STCNT[2];
+	}
+residual_update_done:
+	/*
+	 * Since we've been through a data phase, the SCB_RESID* fields
+	 * are now initialized.  Clear the full residual flag.
+	 */
+	and	SCB_SGPTR[0], ~SG_FULL_RESID;
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		/* Clear the channel in case we return to data phase later */
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	}
+
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		test	SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+		and	SEQ_FLAGS, ~DPHASE_PENDING;
+		/*
+		 * For data-in phases, wait for any pending acks from the
+		 * initiator before changing phase.  We only need to
+		 * send Ignore Wide Residue messages for data-in phases.
+		 */
+		test	DFCNTRL, DIRECTION jz target_ITloop;
+		test	SSTAT1, REQINIT	jnz .;
+		test	SCB_LUN, SCB_XFERLEN_ODD jz target_ITloop;
+		test	SCSIRATE, WIDEXFER jz target_ITloop;
+		/*
+		 * Issue an Ignore Wide Residue Message.
+		 */
+		mvi	P_MESGIN|BSYO call change_phase;
+		mvi	MSG_IGN_WIDE_RESIDUE call target_outb;
+		mvi	1 call target_outb;
+		jmp	target_ITloop;
+	} else {
+		jmp	ITloop;
+	}
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Command phase.  Set up the DMA registers and let 'er rip.
+ */
+p_command:
+	test	SEQ_FLAGS, NOT_IDENTIFIED jz p_command_okay;
+	mvi	PROTO_VIOLATION call set_seqint;
+p_command_okay:
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		bmov	HCNT[0], SCB_CDB_LEN,  1;
+		bmov	HCNT[1], ALLZEROS, 2;
+		mvi	SG_CACHE_PRE, LAST_SEG;
+	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	STCNT[0], SCB_CDB_LEN, 1;
+		bmov	STCNT[1], ALLZEROS, 2;
+	} else {
+		mov	STCNT[0], SCB_CDB_LEN;
+		clr	STCNT[1];
+		clr	STCNT[2];
+	}
+	add	NONE, -13, SCB_CDB_LEN;
+	mvi	SCB_CDB_STORE jnc p_command_embedded;
+p_command_from_host:
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		bmov	HADDR[0], SCB_CDB_PTR, 4;
+		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+	} else {
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			bmov	HADDR[0], SCB_CDB_PTR, 4;
+			bmov	HCNT, STCNT, 3;
+		} else {
+			mvi	DINDEX, HADDR;
+			mvi	SCB_CDB_PTR call bcopy_4;
+			mov	SCB_CDB_LEN call set_hcnt;
+		}
+		mvi	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
+	}
+	jmp	p_command_xfer;
+p_command_embedded:
+	/*
+	 * The data fifo seems to require 4 byte aligned
+	 * transfers from the sequencer.  Force this to
+	 * be the case by clearing HADDR[0] even though
+	 * we aren't going to touch host memory.
+	 */
+	clr	HADDR[0];
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		mvi	DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
+		bmov	DFDAT, SCB_CDB_STORE, 12; 
+	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			/*
+			 * On the 7895 the data FIFO will
+			 * get corrupted if you try to dump
+			 * data from external SCB memory into
+			 * the FIFO while it is enabled.  So,
+			 * fill the fifo and then enable SCSI
+			 * transfers.
+			 */
+			mvi	DFCNTRL, (DIRECTION|FIFORESET);
+		} else {
+			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+		}
+		bmov	DFDAT, SCB_CDB_STORE, 12; 
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFOFLUSH);
+		} else {
+			or	DFCNTRL, FIFOFLUSH;
+		}
+	} else {
+		mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+		call	copy_to_fifo_6;
+		call	copy_to_fifo_6;
+		or	DFCNTRL, FIFOFLUSH;
+	}
+p_command_xfer:
+	and	SEQ_FLAGS, ~NO_CDB_SENT;
+	if ((ahc->features & AHC_DT) == 0) {
+		test	SSTAT0, SDONE jnz . + 2;
+		test    SSTAT1, PHASEMIS jz . - 1;
+		/*
+		 * Wait for our ACK to go-away on it's own
+		 * instead of being killed by SCSIEN getting cleared.
+		 */
+		test	SCSISIGI, ACKI jnz .;
+	} else {
+		test	DFCNTRL, SCSIEN jnz .;
+	}
+	test	SSTAT0, SDONE jnz p_command_successful;
+	/*
+	 * Don't allow a data phase if the command
+	 * was not fully transferred.
+	 */
+	or	SEQ_FLAGS, NO_CDB_SENT;
+p_command_successful:
+	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
+	jmp	ITloop;
+
+/*
+ * Status phase.  Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+p_status:
+	test	SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
+p_status_okay:
+	mov	SCB_SCSI_STATUS, SCSIDATL;
+	or	SCB_CONTROL, STATUS_RCVD;
+	jmp	ITloop;
+
+/*
+ * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target.  The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte.  This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own.  If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ * 
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in response to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+	/* Turn on ATN for the retry */
+	if ((ahc->features & AHC_DT) == 0) {
+		or	SCSISIGO, ATNO, LASTPHASE;
+	} else {
+		mvi	SCSISIGO, ATNO;
+	}
+p_mesgout:
+	mov	SINDEX, MSG_OUT;
+	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+	test	SCB_CONTROL,MK_MESSAGE	jnz host_message_loop;
+p_mesgout_identify:
+	or	SINDEX, MSG_IDENTIFYFLAG|DISCENB, SAVED_LUN;
+	test	SCB_CONTROL, DISCENB jnz . + 2;
+	and	SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
+ */
+p_mesgout_tag:
+	test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
+	mov	SCSIDATL, SINDEX;	/* Send the identify message */
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
+	and	SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
+	mov	SCB_TAG	jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
+	jmp	host_message_loop;
+
+p_mesgout_onebyte:
+	mvi	CLRSINT1, CLRATNO;
+	mov	SCSIDATL, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGOUT	je p_mesgout_retry;
+
+p_mesgout_done:
+	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
+	mov	LAST_MSG, MSG_OUT;
+	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
+	jmp	ITloop;
+
+/*
+ * Message in phase.  Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+	mvi	ACCUM		call inb_first;	/* read the 1st message byte */
+
+	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
+	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
+	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
+	cmp	ALLZEROS,A		je mesgin_complete;
+	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
+	cmp	A,MSG_IGN_WIDE_RESIDUE	je mesgin_ign_wide_residue;
+	cmp	A,MSG_NOOP		je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine.  To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop.  Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+	mvi	HOST_MSG_LOOP call set_seqint;
+	call	phase_lock;
+	cmp	RETURN_1, EXIT_MSG_LOOP	je ITloop + 1;
+	jmp	host_message_loop;
+
+mesgin_ign_wide_residue:
+if ((ahc->features & AHC_WIDE) != 0) {
+	test	SCSIRATE, WIDEXFER jz mesgin_reject;
+	/* Pull the residue byte */
+	mvi	ARG_1	call inb_next;
+	cmp	ARG_1, 0x01 jne mesgin_reject;
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+	test	SCB_LUN, SCB_XFERLEN_ODD jnz mesgin_done;
+	mvi	IGN_WIDE_RES call set_seqint;
+	jmp	mesgin_done;
+}
+
+mesgin_proto_violation:
+	mvi	PROTO_VIOLATION call set_seqint;
+	jmp	mesgin_done;
+mesgin_reject:
+	mvi	MSG_MESSAGE_REJECT	call mk_mesg;
+mesgin_done:
+	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
+	jmp	ITloop;
+
+/*
+ * We received a "command complete" message.  Put the SCB_TAG into the QOUTFIFO,
+ * and trigger a completion interrupt.  Before doing so, check to see if there
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
+ * process this information.  In the case of a non zero status byte, we 
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved.  If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command, requeue
+ * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting 
+ * RETURN_1 to SEND_SENSE.
+ */
+mesgin_complete:
+
+	/*
+	 * If ATN is raised, we still want to give the target a message.
+	 * Perhaps there was a parity error on this last message byte.
+	 * Either way, the target should take us to message out phase
+	 * and then attempt to complete the command again.  We should use a
+	 * critical section here to guard against a timeout triggering
+	 * for this command and setting ATN while we are still processing
+	 * the completion.
+	test	SCSISIGI, ATNI jnz mesgin_done;
+	 */
+
+	/*
+	 * If we are identified and have successfully sent the CDB,
+	 * any status will do.  Optimize this fast path.
+	 */
+	test	SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation;
+	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted; 
+
+	/*
+	 * If the target never sent an identify message but instead went
+	 * to mesgin to give an invalid message, let the host abort us.
+	 */
+	test	SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation;
+
+	/*
+	 * If we recevied good status but never successfully sent the
+	 * cdb, abort the command.
+	 */
+	test	SCB_SCSI_STATUS,0xff	jnz complete_accepted;
+	test	SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;
+
+complete_accepted:
+	/*
+	 * See if we attempted to deliver a message but the target ingnored us.
+	 */
+	test	SCB_CONTROL, MK_MESSAGE jz . + 2;
+	mvi	MKMSG_FAILED call set_seqint;
+
+	/*
+	 * Check for residuals
+	 */
+	test	SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */
+	test	SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+	test	SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+check_status:
+	test	SCB_SCSI_STATUS,0xff	jz complete;	/* Good Status? */
+upload_scb:
+	or	SCB_SGPTR, SG_RESID_VALID;
+	mvi	DMAPARAMS, FIFORESET;
+	mov	SCB_TAG		call dma_scb;
+	test	SCB_SCSI_STATUS, 0xff	jz complete;	/* Just a residual? */
+	mvi	BAD_STATUS call set_seqint;		/* let driver know */
+	cmp	RETURN_1, SEND_SENSE	jne complete;
+	call	add_scb_to_free_list;
+	jmp	await_busfree;
+complete:
+	mov	SCB_TAG call complete_post;
+	jmp	await_busfree;
+}
+
+complete_post:
+	/* Post the SCBID in SINDEX and issue an interrupt */
+	call	add_scb_to_free_list;
+	mov	ARG_1, SINDEX;
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		mov	A, SDSCB_QOFF;
+	} else {
+		mov	A, QOUTPOS;
+	}
+	mvi	QOUTFIFO_OFFSET call post_byte_setup;
+	mov	ARG_1 call post_byte;
+	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
+		inc 	QOUTPOS;
+	}
+	mvi	INTSTAT,CMDCMPLT ret;
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Is it a disconnect message?  Set a flag in the SCB to remind us
+ * and await the bus going free.  If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselection.
+ */
+mesgin_disconnect:
+	/*
+	 * If ATN is raised, we still want to give the target a message.
+	 * Perhaps there was a parity error on this last message byte
+	 * or we want to abort this command.  Either way, the target
+	 * should take us to message out phase and then attempt to
+	 * disconnect again.
+	 * XXX - Wait for more testing.
+	test	SCSISIGI, ATNI jnz mesgin_done;
+	 */
+	test	SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT
+		jnz mesgin_proto_violation;
+	or	SCB_CONTROL,DISCONNECTED;
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		call	add_scb_to_disc_list;
+	}
+	test	SCB_CONTROL, TAG_ENB jnz await_busfree;
+	mov	ARG_1, SCB_TAG;
+	and	SAVED_LUN, LID, SCB_LUN;
+	mov	SCB_SCSIID	call set_busy_target;
+	jmp	await_busfree;
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them.  This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ * Ack the message as soon as possible.  For chips without S/G pipelining,
+ * we can only ack the message after SHADDR has been saved.  On these
+ * chips, SHADDR increments with every bus transaction, even PIO.
+ */
+mesgin_sdptrs:
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
+		test	SEQ_FLAGS, DPHASE	jz ITloop;
+	} else {
+		test	SEQ_FLAGS, DPHASE	jz mesgin_done;
+	}
+
+	/*
+	 * If we are asked to save our position at the end of the
+	 * transfer, just mark us at the end rather than perform a
+	 * full save.
+	 */
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz mesgin_sdptrs_full;
+	or	SCB_SGPTR, SG_LIST_NULL;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		jmp	ITloop;
+	} else {
+		jmp	mesgin_done;
+	}
+
+mesgin_sdptrs_full:
+
+	/*
+	 * The SCB_SGPTR becomes the next one we'll download,
+	 * and the SCB_DATAPTR becomes the current SHADDR.
+	 * Use the residual number since STCNT is corrupted by
+	 * any message transfer.
+	 */
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	SCB_DATAPTR, SHADDR, 4;
+		if ((ahc->features & AHC_ULTRA2) == 0) {
+			mov	NONE,SCSIDATL;	/*dummy read from latch to ACK*/
+		}
+		bmov	SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8;
+	} else {
+		mvi	DINDEX, SCB_DATAPTR;
+		mvi	SHADDR call bcopy_4;
+		mov	NONE,SCSIDATL;	/*dummy read from latch to ACK*/
+		mvi	SCB_RESIDUAL_DATACNT call bcopy_8;
+	}
+	jmp	ITloop;
+
+/*
+ * Restore pointers message?  Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.  We also reset/reallocate the FIFO to make
+ * sure we have a clean start for the next data or command phase.
+ */
+mesgin_rdptrs:
+	and	SEQ_FLAGS, ~DPHASE;		/*
+						 * We'll reload them
+						 * the next time through
+						 * the dataphase.
+						 */
+	or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	jmp	mesgin_done;
+
+/*
+ * Index into our Busy Target table.  SINDEX and DINDEX are modified
+ * upon return.  SCBPTR may be modified by this action.
+ */
+set_busy_target:
+	shr	DINDEX, 4, SINDEX;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		mov	SCBPTR, SAVED_LUN;
+		add	DINDEX, SCB_64_BTT;
+	} else {
+		add	DINDEX, BUSY_TARGETS;
+	}
+	mov	DINDIR, ARG_1 ret;
+
+/*
+ * Identify message?  For a reconnecting target, this tells us the lun
+ * that the reconnection is for - find the correct SCB and switch to it,
+ * clearing the "disconnected" bit so we don't "find" it by accident later.
+ */
+mesgin_identify:
+	/*
+	 * Determine whether a target is using tagged or non-tagged
+	 * transactions by first looking at the transaction stored in
+	 * the busy target array.  If there is no untagged transaction
+	 * for this target or the transaction is for a different lun, then
+	 * this must be a tagged transaction.
+	 */
+	shr	SINDEX, 4, SAVED_SCSIID;
+	and	SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		add	SINDEX, SCB_64_BTT;
+		mov	SCBPTR, SAVED_LUN;
+		if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+			add	NONE, -SCB_64_BTT, SINDEX;
+			jc	. + 2;
+			mvi	INTSTAT, OUT_OF_RANGE;
+			nop;
+			add	NONE, -(SCB_64_BTT + 16), SINDEX;
+			jnc	. + 2;
+			mvi	INTSTAT, OUT_OF_RANGE;
+			nop;
+		}
+	} else {
+		add	SINDEX, BUSY_TARGETS;
+		if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+			add	NONE, -BUSY_TARGETS, SINDEX;
+			jc	. + 2;
+			mvi	INTSTAT, OUT_OF_RANGE;
+			nop;
+			add	NONE, -(BUSY_TARGETS + 16), SINDEX;
+			jnc	. + 2;
+			mvi	INTSTAT, OUT_OF_RANGE;
+			nop;
+		}
+	}
+	mov	ARG_1, SINDIR;
+	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		mov	ARG_1 call findSCB;
+	} else {
+		mov	SCBPTR, ARG_1;
+	}
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		jmp setup_SCB_id_lun_okay;
+	} else {
+		/*
+		 * We only allow one untagged command per-target
+		 * at a time.  So, if the lun doesn't match, look
+		 * for a tag message.
+		 */
+		and	A, LID, SCB_LUN;
+		cmp	SAVED_LUN, A	je setup_SCB_id_lun_okay;
+		if ((ahc->flags & AHC_PAGESCBS) != 0) {
+			/*
+			 * findSCB removes the SCB from the
+			 * disconnected list, so we must replace
+			 * it there should this SCB be for another
+			 * lun.
+			 */
+			call	cleanup_scb;
+		}
+	}
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB.  With SCB paging, we must search for non-tagged
+ * transactions since the SCB may exist in any slot.  If we're not
+ * using SCB paging, we can use the tag as the direct index to the
+ * SCB.
+ */
+snoop_tag:
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x80;
+	}
+	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
+	call	phase_lock;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x1;
+	}
+	cmp	LASTPHASE, P_MESGIN	jne not_found;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x2;
+	}
+	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		mvi	ARG_1	call inb_next;	/* tag value */
+		mov	ARG_1	call findSCB;
+	} else {
+		mvi	ARG_1	call inb_next;	/* tag value */
+		mov	SCBPTR, ARG_1;
+	}
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x4;
+	}
+	mov	A, SCB_SCSIID;
+	cmp	SAVED_SCSIID, A	jne not_found_cleanup_scb;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x8;
+	}
+setup_SCB_id_okay:
+	and	A, LID, SCB_LUN;
+	cmp	SAVED_LUN, A	jne not_found_cleanup_scb;
+setup_SCB_id_lun_okay:
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x10;
+	}
+	test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
+	and	SCB_CONTROL,~DISCONNECTED;
+	test	SCB_CONTROL, TAG_ENB	jnz setup_SCB_tagged;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		mov	A, SCBPTR;
+	}
+	mvi	ARG_1, SCB_LIST_NULL;
+	mov	SAVED_SCSIID	call	set_busy_target;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		mov	SCBPTR, A;
+	}
+setup_SCB_tagged:
+	clr	SEQ_FLAGS;	/* make note of IDENTIFY */
+	call	set_transfer_settings;
+	/* See if the host wants to send a message upon reconnection */
+	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+	mvi	HOST_MSG	call mk_mesg;
+	jmp	mesgin_done;
+
+not_found_cleanup_scb:
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		call	cleanup_scb;
+	}
+not_found:
+	mvi	NO_MATCH call set_seqint;
+	jmp	mesgin_done;
+
+mk_mesg:
+	if ((ahc->features & AHC_DT) == 0) {
+		or	SCSISIGO, ATNO, LASTPHASE;
+	} else {
+		mvi	SCSISIGO, ATNO;
+	}
+	mov	MSG_OUT,SINDEX ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * According to Adaptec's documentation, an ACK is not sent on input from
+ * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
+ * latched (the usual way), then read the data byte directly off the bus
+ * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
+ * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
+ * spec guarantees that the target will hold the data byte on the bus until
+ * we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called.  inb_{first,next}
+ * use the same calling convention as inb.
+ */
+inb_next_wait_perr:
+	mvi	PERR_DETECTED call set_seqint;
+	jmp	inb_next_wait;
+inb_next:
+	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
+inb_next_wait:
+	/*
+	 * If there is a parity error, wait for the kernel to
+	 * see the interrupt and prepare our message response
+	 * before continuing.
+	 */
+	test	SSTAT1, REQINIT	jz inb_next_wait;
+	test	SSTAT1, SCSIPERR jnz inb_next_wait_perr;
+inb_next_check_phase:
+	and	LASTPHASE, PHASE_MASK, SCSISIGI;
+	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+	mov	DINDEX,SINDEX;
+	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
+inb_last:
+	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
+}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * Change to a new phase.  If we are changing the state of the I/O signal,
+ * from out to in, wait an additional data release delay before continuing.
+ */
+change_phase:
+	/* Wait for preceding I/O session to complete. */
+	test	SCSISIGI, ACKI jnz .;
+
+	/* Change the phase */
+	and	DINDEX, IOI, SCSISIGI;
+	mov	SCSISIGO, SINDEX;
+	and	A, IOI, SINDEX;
+
+	/*
+	 * If the data direction has changed, from
+	 * out (initiator driving) to in (target driving),
+	 * we must wait at least a data release delay plus
+	 * the normal bus settle delay. [SCSI III SPI 10.11.0]
+	 */
+	cmp 	DINDEX, A je change_phase_wait;
+	test	SINDEX, IOI jz change_phase_wait;
+	call	change_phase_wait;
+change_phase_wait:
+	nop;
+	nop;
+	nop;
+	nop ret;
+
+/*
+ * Send a byte to an initiator in Automatic PIO mode.
+ */
+target_outb:
+	or	SXFRCTL0, SPIOEN;
+	test	SSTAT0, SPIORDY	jz .;
+	mov	SCSIDATL, SINDEX;
+	test	SSTAT0, SPIORDY	jz .;
+	and	SXFRCTL0, ~SPIOEN ret;
+}
+	
+/*
+ * Locate a disconnected SCB by SCBID.  Upon return, SCBPTR and SINDEX will
+ * be set to the position of the SCB.  If the SCB cannot be found locally,
+ * it will be paged in from host memory.  RETURN_2 stores the address of the
+ * preceding SCB in the disconnected list which can be used to speed up
+ * removal of the found SCB from the disconnected list.
+ */
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL;
+findSCB:
+	mov	A, SINDEX;			/* Tag passed in SINDEX */
+	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je findSCB_notFound;
+	mov	SCBPTR, DISCONNECTED_SCBH;	/* Initialize SCBPTR */
+	mvi	ARG_2, SCB_LIST_NULL;		/* Head of list */
+	jmp	findSCB_loop;
+findSCB_next:
+	cmp	SCB_NEXT, SCB_LIST_NULL je findSCB_notFound;
+	mov	ARG_2, SCBPTR;
+	mov	SCBPTR,SCB_NEXT;
+findSCB_loop:
+	cmp	SCB_TAG, A	jne findSCB_next;
+rem_scb_from_disc_list:
+	cmp	ARG_2, SCB_LIST_NULL	je rHead;
+	mov	DINDEX, SCB_NEXT;
+	mov	SINDEX, SCBPTR;
+	mov	SCBPTR, ARG_2;
+	mov	SCB_NEXT, DINDEX;
+	mov	SCBPTR, SINDEX ret;
+rHead:
+	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
+END_CRITICAL;
+findSCB_notFound:
+	/*
+	 * We didn't find it.  Page in the SCB.
+	 */
+	mov	ARG_1, A; /* Save tag */
+	mov	ALLZEROS call get_free_or_disc_scb;
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	ARG_1	jmp dma_scb;
+}
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SHARED_DATA_ADDR.
+ */
+post_byte_setup:
+	mov	ARG_2, SINDEX;
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SHARED_DATA_ADDR call	set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSCBCTL, CCSCBRESET ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SHARED_DATA_ADDR call	set_1byte_addr;
+		mvi	1	call set_hcnt;
+		mvi	DFCNTRL, FIFORESET ret;
+	}
+
+post_byte:
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	CCSCBRAM, SINDEX, 1;
+		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+		test	CCSCBCTL, CCSCBDONE jz .;
+		clr	CCSCBCTL ret;
+	} else {
+		mov	DFDAT, SINDEX;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		jmp	dma_finish;
+	}
+
+phase_lock_perr:
+	mvi	PERR_DETECTED call set_seqint;
+phase_lock:     
+	/*
+	 * If there is a parity error, wait for the kernel to
+	 * see the interrupt and prepare our message response
+	 * before continuing.
+	 */
+	test	SSTAT1, REQINIT jz phase_lock;
+	test	SSTAT1, SCSIPERR jnz phase_lock_perr;
+phase_lock_latch_phase:
+	if ((ahc->features & AHC_DT) == 0) {
+		and	SCSISIGO, PHASE_MASK, SCSISIGI;
+	}
+	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+if ((ahc->features & AHC_CMD_CHAN) == 0) {
+set_hcnt:
+	mov	HCNT[0], SINDEX;
+clear_hcnt:
+	clr	HCNT[1];
+	clr	HCNT[2] ret;
+
+set_stcnt_from_hcnt:
+	mov	STCNT[0], HCNT[0];
+	mov	STCNT[1], HCNT[1];
+	mov	STCNT[2], HCNT[2] ret;
+
+bcopy_8:
+	mov	DINDIR, SINDIR;
+bcopy_7:
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR;
+bcopy_5:
+	mov	DINDIR, SINDIR;
+bcopy_4:
+	mov	DINDIR, SINDIR;
+bcopy_3:
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR ret;
+}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_32byte_addr:
+	shr	ARG_2, 3, A;
+	shl	A, 5;
+	jmp	set_1byte_addr;
+}
+
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 64byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_64byte_addr:
+	shr	ARG_2, 2, A;
+	shl	A, 6;
+
+/*
+ * Setup addr assuming that A + (ARG_2 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+	add     DINDIR, A, SINDIR;
+	mov     A, ARG_2;
+	adc	DINDIR, A, SINDIR;
+	clr	A;
+	adc	DINDIR, A, SINDIR;
+	adc	DINDIR, A, SINDIR ret;
+
+/*
+ * Either post or fetch an SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
+dma_scb:
+	mov	A, SINDEX;
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	HSCB_ADDR call set_64byte_addr;
+		mov	CCSCBPTR, SCBPTR;
+		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			mvi	CCHCNT, SCB_DOWNLOAD_SIZE_64;
+		} else {
+			mvi	CCHCNT, SCB_DOWNLOAD_SIZE;
+		}
+		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+		jmp	dma_scb_finish;
+dma_scb_tohost:
+		mvi	CCHCNT, SCB_UPLOAD_SIZE;
+		if ((ahc->features & AHC_ULTRA2) == 0) {
+			mvi	CCSCBCTL, CCSCBRESET;
+			bmov	CCSCBRAM, SCB_BASE, SCB_UPLOAD_SIZE;
+			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			test	CCSCBCTL, CCSCBDONE jz .;
+		} else if ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0) {
+			mvi	CCSCBCTL, CCARREN|CCSCBRESET;
+			cmp	CCSCBCTL, ARRDONE|CCARREN jne .;
+			mvi	CCHCNT, SCB_UPLOAD_SIZE;
+			mvi	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			cmp	CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
+		} else {
+			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+		}
+dma_scb_finish:
+		clr	CCSCBCTL;
+		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
+		ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	HSCB_ADDR call set_64byte_addr;
+		mvi	SCB_DOWNLOAD_SIZE call set_hcnt;
+		mov	DFCNTRL, DMAPARAMS;
+		test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
+		/* Fill it with the SCB data */
+copy_scb_tofifo:
+		mvi	SINDEX, SCB_BASE;
+		add	A, SCB_DOWNLOAD_SIZE, SINDEX;
+copy_scb_tofifo_loop:
+		call	copy_to_fifo_8;
+		cmp	SINDEX, A jne copy_scb_tofifo_loop;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		jmp	dma_finish;
+dma_scb_fromhost:
+		mvi	DINDEX, SCB_BASE;
+		if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+			/*
+			 * The PCI module will only issue a PCI
+			 * retry if the data FIFO is empty.  If the
+			 * host disconnects in the middle of a
+			 * transfer, we must empty the fifo of all
+			 * available data to force the chip to
+			 * continue the transfer.  This does not
+			 * happen for SCSI transfers as the SCSI module
+			 * will drain the FIFO as data are made available.
+			 * When the hang occurs, we know that a multiple
+			 * of 8 bytes is in the FIFO because the PCI
+			 * module has an 8 byte input latch that only
+			 * dumps to the FIFO when HCNT == 0 or the
+			 * latch is full.
+			 */
+			clr	A;
+			/* Wait for at least 8 bytes of data to arrive. */
+dma_scb_hang_fifo:
+			test	DFSTATUS, FIFOQWDEMP jnz dma_scb_hang_fifo;
+dma_scb_hang_wait:
+			test	DFSTATUS, MREQPEND jnz dma_scb_hang_wait;
+			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
+			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
+			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
+			/*
+			 * The PCI module no longer intends to perform
+			 * a PCI transaction.  Drain the fifo.
+			 */
+dma_scb_hang_dma_drain_fifo:
+			not	A, HCNT;
+			add	A, SCB_DOWNLOAD_SIZE+SCB_BASE+1;
+			and	A, ~0x7;
+			mov	DINDIR,DFDAT;
+			cmp	DINDEX, A jne . - 1;
+			cmp	DINDEX, SCB_DOWNLOAD_SIZE+SCB_BASE
+				je	dma_finish_nowait;
+			/* Restore A as the lines left to transfer. */
+			add	A, -SCB_BASE, DINDEX;
+			shr	A, 3;
+			jmp	dma_scb_hang_fifo;
+dma_scb_hang_dma_done:
+			and	DFCNTRL, ~HDMAEN;
+			test	DFCNTRL, HDMAEN jnz .;
+			add	SEQADDR0, A;
+		} else {
+			call	dma_finish;
+		}
+		call	dfdat_in_8;
+		call	dfdat_in_8;
+		call	dfdat_in_8;
+dfdat_in_8:
+		mov	DINDIR,DFDAT;
+dfdat_in_7:
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+dfdat_in_2:
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT ret;
+	}
+
+copy_to_fifo_8:
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+copy_to_fifo_6:
+	mov	DFDAT,SINDIR;
+copy_to_fifo_5:
+	mov	DFDAT,SINDIR;
+copy_to_fifo_4:
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR ret;
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+dma_finish:
+	test	DFSTATUS,HDONE	jz dma_finish;
+dma_finish_nowait:
+	/* Turn off DMA */
+	and	DFCNTRL, ~HDMAEN;
+	test	DFCNTRL, HDMAEN jnz .;
+	ret;
+
+/*
+ * Restore an SCB that failed to match an incoming reselection
+ * to the correct/safe state.  If the SCB is for a disconnected
+ * transaction, it must be returned to the disconnected list.
+ * If it is not in the disconnected state, it must be free.
+ */
+cleanup_scb:
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		test	SCB_CONTROL,DISCONNECTED jnz add_scb_to_disc_list;
+	}
+add_scb_to_free_list:
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL;
+		mov	SCB_NEXT, FREE_SCBH;
+		mvi	SCB_TAG, SCB_LIST_NULL;
+		mov	FREE_SCBH, SCBPTR ret;
+END_CRITICAL;
+	} else {
+		mvi	SCB_TAG, SCB_LIST_NULL ret;
+	}
+
+if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+set_hhaddr:
+	or	DSCOMMAND1, HADDLDSEL0;
+	and	HADDR, SG_HIGH_ADDR_BITS, SINDEX;
+	and	DSCOMMAND1, ~HADDLDSEL0 ret;
+}
+
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+get_free_or_disc_scb:
+BEGIN_CRITICAL;
+	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
+	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
+return_error:
+	mvi	NO_FREE_SCB call set_seqint;
+	mvi	SINDEX, SCB_LIST_NULL	ret;
+dequeue_disc_scb:
+	mov	SCBPTR, DISCONNECTED_SCBH;
+	mov	DISCONNECTED_SCBH, SCB_NEXT;
+END_CRITICAL;
+	mvi	DMAPARAMS, FIFORESET;
+	mov	SCB_TAG	jmp dma_scb;
+BEGIN_CRITICAL;
+dequeue_free_scb:
+	mov	SCBPTR, FREE_SCBH;
+	mov	FREE_SCBH, SCB_NEXT ret;
+END_CRITICAL;
+
+add_scb_to_disc_list:
+/*
+ * Link this SCB into the DISCONNECTED list.  This list holds the
+ * candidates for paging out an SCB if one is needed for a new command.
+ * Modifying the disconnected list is a critical(pause dissabled) section.
+ */
+BEGIN_CRITICAL;
+	mov	SCB_NEXT, DISCONNECTED_SCBH;
+	mov	DISCONNECTED_SCBH, SCBPTR ret;
+END_CRITICAL;
+}
+set_seqint:
+	mov	INTSTAT, SINDEX;
+	nop;
+return:
+	ret;
diff --git a/drivers/scsi/aic7xxx/aic7xxx_93cx6.c b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
new file mode 100644
index 0000000..9e85a7e
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
@@ -0,0 +1,324 @@
+/*
+ * Interface for the 93C66/56/46/26/06 serial eeprom parts.
+ *
+ * Copyright (c) 1995, 1996 Daniel M. Eischen
+ * 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,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * 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.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.c#19 $
+ */
+
+/*
+ *   The instruction set of the 93C66/56/46/26/06 chips are 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 precede
+ *                                               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.
+ *    **Note: There are 8 address bits for the 93C56/66 chips unlike
+ *	      the 93C46/26/06 chips which have 6 address bits.
+ *
+ *   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.
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+/*
+ * Right now, we only have to read the SEEPROM.  But we make it easier to
+ * add other 93Cx6 functions.
+ */
+struct seeprom_cmd {
+  	uint8_t len;
+ 	uint8_t bits[11];
+};
+
+/* Short opcodes for the c46 */
+static const struct seeprom_cmd seeprom_ewen = {9, {1, 0, 0, 1, 1, 0, 0, 0, 0}};
+static const struct seeprom_cmd seeprom_ewds = {9, {1, 0, 0, 0, 0, 0, 0, 0, 0}};
+
+/* Long opcodes for the C56/C66 */
+static const struct seeprom_cmd seeprom_long_ewen = {11, {1, 0, 0, 1, 1, 0, 0, 0, 0}};
+static const struct seeprom_cmd seeprom_long_ewds = {11, {1, 0, 0, 0, 0, 0, 0, 0, 0}};
+
+/* Common opcodes */
+static const struct seeprom_cmd seeprom_write = {3, {1, 0, 1}};
+static const struct seeprom_cmd seeprom_read  = {3, {1, 1, 0}};
+
+/*
+ * Wait for the SEERDY to go high; about 800 ns.
+ */
+#define CLOCK_PULSE(sd, rdy)				\
+	while ((SEEPROM_STATUS_INB(sd) & rdy) == 0) {	\
+		;  /* Do nothing */			\
+	}						\
+	(void)SEEPROM_INB(sd);	/* Clear clock */
+
+/*
+ * Send a START condition and the given command
+ */
+static void
+send_seeprom_cmd(struct seeprom_descriptor *sd, const struct seeprom_cmd *cmd)
+{
+	uint8_t temp;
+	int i = 0;
+
+	/* Send chip select for one clock cycle. */
+	temp = sd->sd_MS ^ sd->sd_CS;
+	SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+	CLOCK_PULSE(sd, sd->sd_RDY);
+
+	for (i = 0; i < cmd->len; i++) {
+		if (cmd->bits[i] != 0)
+			temp ^= sd->sd_DO;
+		SEEPROM_OUTB(sd, temp);
+		CLOCK_PULSE(sd, sd->sd_RDY);
+		SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+		CLOCK_PULSE(sd, sd->sd_RDY);
+		if (cmd->bits[i] != 0)
+			temp ^= sd->sd_DO;
+	}
+}
+
+/*
+ * Clear CS put the chip in the reset state, where it can wait for new commands.
+ */
+static void
+reset_seeprom(struct seeprom_descriptor *sd)
+{
+	uint8_t temp;
+
+	temp = sd->sd_MS;
+	SEEPROM_OUTB(sd, temp);
+	CLOCK_PULSE(sd, sd->sd_RDY);
+	SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+	CLOCK_PULSE(sd, sd->sd_RDY);
+	SEEPROM_OUTB(sd, temp);
+	CLOCK_PULSE(sd, sd->sd_RDY);
+}
+
+/*
+ * Read the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ */
+int
+ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+		 u_int start_addr, u_int count)
+{
+	int i = 0;
+	u_int k = 0;
+	uint16_t v;
+	uint8_t temp;
+
+	/*
+	 * Read the requested registers of the seeprom.  The loop
+	 * will range from 0 to count-1.
+	 */
+	for (k = start_addr; k < count + start_addr; k++) {
+		/*
+		 * Now we're ready to send the read command followed by the
+		 * address of the 16-bit register we want to read.
+		 */
+		send_seeprom_cmd(sd, &seeprom_read);
+
+		/* Send the 6 or 8 bit address (MSB first, LSB last). */
+		temp = sd->sd_MS ^ sd->sd_CS;
+		for (i = (sd->sd_chip - 1); i >= 0; i--) {
+			if ((k & (1 << i)) != 0)
+				temp ^= sd->sd_DO;
+			SEEPROM_OUTB(sd, temp);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			if ((k & (1 << i)) != 0)
+				temp ^= sd->sd_DO;
+		}
+
+		/*
+		 * 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.
+		 */
+		v = 0;
+		for (i = 16; i >= 0; i--) {
+			SEEPROM_OUTB(sd, temp);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			v <<= 1;
+			if (SEEPROM_DATA_INB(sd) & sd->sd_DI)
+				v |= 1;
+			SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+		}
+
+		buf[k - start_addr] = v;
+
+		/* Reset the chip select for the next command cycle. */
+		reset_seeprom(sd);
+	}
+#ifdef AHC_DUMP_EEPROM
+	printk("\nSerial EEPROM:\n\t");
+	for (k = 0; k < count; k = k + 1) {
+		if (((k % 8) == 0) && (k != 0)) {
+			printk(KERN_CONT "\n\t");
+		}
+		printk(KERN_CONT " 0x%x", buf[k]);
+	}
+	printk(KERN_CONT "\n");
+#endif
+	return (1);
+}
+
+/*
+ * Write the serial EEPROM and return 1 if successful and 0 if
+ * not successful.
+ */
+int
+ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+		  u_int start_addr, u_int count)
+{
+	const struct seeprom_cmd *ewen, *ewds;
+	uint16_t v;
+	uint8_t temp;
+	int i, k;
+
+	/* Place the chip into write-enable mode */
+	if (sd->sd_chip == C46) {
+		ewen = &seeprom_ewen;
+		ewds = &seeprom_ewds;
+	} else if (sd->sd_chip == C56_66) {
+		ewen = &seeprom_long_ewen;
+		ewds = &seeprom_long_ewds;
+	} else {
+		printk("ahc_write_seeprom: unsupported seeprom type %d\n",
+		       sd->sd_chip);
+		return (0);
+	}
+
+	send_seeprom_cmd(sd, ewen);
+	reset_seeprom(sd);
+
+	/* Write all requested data out to the seeprom. */
+	temp = sd->sd_MS ^ sd->sd_CS;
+	for (k = start_addr; k < count + start_addr; k++) {
+		/* Send the write command */
+		send_seeprom_cmd(sd, &seeprom_write);
+
+		/* Send the 6 or 8 bit address (MSB first). */
+		for (i = (sd->sd_chip - 1); i >= 0; i--) {
+			if ((k & (1 << i)) != 0)
+				temp ^= sd->sd_DO;
+			SEEPROM_OUTB(sd, temp);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			if ((k & (1 << i)) != 0)
+				temp ^= sd->sd_DO;
+		}
+
+		/* Write the 16 bit value, MSB first */
+		v = buf[k - start_addr];
+		for (i = 15; i >= 0; i--) {
+			if ((v & (1 << i)) != 0)
+				temp ^= sd->sd_DO;
+			SEEPROM_OUTB(sd, temp);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			if ((v & (1 << i)) != 0)
+				temp ^= sd->sd_DO;
+		}
+
+		/* Wait for the chip to complete the write */
+		temp = sd->sd_MS;
+		SEEPROM_OUTB(sd, temp);
+		CLOCK_PULSE(sd, sd->sd_RDY);
+		temp = sd->sd_MS ^ sd->sd_CS;
+		do {
+			SEEPROM_OUTB(sd, temp);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+		} while ((SEEPROM_DATA_INB(sd) & sd->sd_DI) == 0);
+
+		reset_seeprom(sd);
+	}
+
+	/* Put the chip back into write-protect mode */
+	send_seeprom_cmd(sd, ewds);
+	reset_seeprom(sd);
+
+	return (1);
+}
+
+int
+ahc_verify_cksum(struct seeprom_config *sc)
+{
+	int i;
+	int maxaddr;
+	uint32_t checksum;
+	uint16_t *scarray;
+
+	maxaddr = (sizeof(*sc)/2) - 1;
+	checksum = 0;
+	scarray = (uint16_t *)sc;
+
+	for (i = 0; i < maxaddr; i++)
+		checksum = checksum + scarray[i];
+	if (checksum == 0
+	 || (checksum & 0xFFFF) != sc->checksum) {
+		return (0);
+	} else {
+		return(1);
+	}
+}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_93cx6.h b/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
new file mode 100644
index 0000000..859c43c
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
@@ -0,0 +1,102 @@
+/*
+ * Interface to the 93C46/56 serial EEPROM that is used to store BIOS
+ * settings for the aic7xxx based adaptec SCSI controllers.  It can
+ * also be used for 93C26 and 93C06 serial EEPROMS.
+ *
+ * Copyright (c) 1994, 1995, 2000 Justin T. Gibbs.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.h#12 $
+ *
+ * $FreeBSD$
+ */
+#ifndef _AIC7XXX_93CX6_H_
+#define _AIC7XXX_93CX6_H_
+
+typedef enum {
+	C46 = 6,
+	C56_66 = 8
+} seeprom_chip_t;
+
+struct seeprom_descriptor {
+	struct ahc_softc *sd_ahc;
+	u_int sd_control_offset;
+	u_int sd_status_offset;
+	u_int sd_dataout_offset;
+	seeprom_chip_t sd_chip;
+	uint16_t sd_MS;
+	uint16_t sd_RDY;
+	uint16_t sd_CS;
+	uint16_t sd_CK;
+	uint16_t sd_DO;
+	uint16_t sd_DI;
+};
+
+/*
+ * This function will read count 16-bit words from the serial EEPROM and
+ * return their value in buf.  The port address of the aic7xxx serial EEPROM
+ * control register is passed in as offset.  The following parameters are
+ * also passed in:
+ *
+ *   CS  - Chip select
+ *   CK  - Clock
+ *   DO  - Data out
+ *   DI  - Data in
+ *   RDY - SEEPROM ready
+ *   MS  - Memory port mode select
+ *
+ *  A failed read attempt returns 0, and a successful read returns 1.
+ */
+
+#define	SEEPROM_INB(sd) \
+	ahc_inb(sd->sd_ahc, sd->sd_control_offset)
+#define	SEEPROM_OUTB(sd, value)					\
+do {								\
+	ahc_outb(sd->sd_ahc, sd->sd_control_offset, value);	\
+	ahc_flush_device_writes(sd->sd_ahc);			\
+} while(0)
+
+#define	SEEPROM_STATUS_INB(sd) \
+	ahc_inb(sd->sd_ahc, sd->sd_status_offset)
+#define	SEEPROM_DATA_INB(sd) \
+	ahc_inb(sd->sd_ahc, sd->sd_dataout_offset)
+
+int ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+		     u_int start_addr, u_int count);
+int ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+		      u_int start_addr, u_int count);
+int ahc_verify_cksum(struct seeprom_config *sc);
+
+#endif /* _AIC7XXX_93CX6_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c
new file mode 100644
index 0000000..64ab9ea
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_core.c
@@ -0,0 +1,7971 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2002 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#155 $
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
+#endif
+
+/***************************** Lookup Tables **********************************/
+static const char *const ahc_chip_names[] = {
+	"NONE",
+	"aic7770",
+	"aic7850",
+	"aic7855",
+	"aic7859",
+	"aic7860",
+	"aic7870",
+	"aic7880",
+	"aic7895",
+	"aic7895C",
+	"aic7890/91",
+	"aic7896/97",
+	"aic7892",
+	"aic7899"
+};
+static const u_int num_chip_names = ARRAY_SIZE(ahc_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahc_hard_error_entry {
+        uint8_t errno;
+	const char *errmesg;
+};
+
+static const struct ahc_hard_error_entry ahc_hard_errors[] = {
+	{ ILLHADDR,	"Illegal Host Access" },
+	{ ILLSADDR,	"Illegal Sequencer Address referenced" },
+	{ ILLOPCODE,	"Illegal Opcode in sequencer program" },
+	{ SQPARERR,	"Sequencer Parity Error" },
+	{ DPARERR,	"Data-path Parity Error" },
+	{ MPARERR,	"Scratch or SCB Memory Parity Error" },
+	{ PCIERRSTAT,	"PCI Error detected" },
+	{ CIOPARERR,	"CIOBUS Parity Error" },
+};
+static const u_int num_errors = ARRAY_SIZE(ahc_hard_errors);
+
+static const struct ahc_phase_table_entry ahc_phase_table[] =
+{
+	{ P_DATAOUT,	MSG_NOOP,		"in Data-out phase"	},
+	{ P_DATAIN,	MSG_INITIATOR_DET_ERR,	"in Data-in phase"	},
+	{ P_DATAOUT_DT,	MSG_NOOP,		"in DT Data-out phase"	},
+	{ P_DATAIN_DT,	MSG_INITIATOR_DET_ERR,	"in DT Data-in phase"	},
+	{ P_COMMAND,	MSG_NOOP,		"in Command phase"	},
+	{ P_MESGOUT,	MSG_NOOP,		"in Message-out phase"	},
+	{ P_STATUS,	MSG_INITIATOR_DET_ERR,	"in Status phase"	},
+	{ P_MESGIN,	MSG_PARITY_ERROR,	"in Message-in phase"	},
+	{ P_BUSFREE,	MSG_NOOP,		"while idle"		},
+	{ 0,		MSG_NOOP,		"in unknown phase"	}
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = ARRAY_SIZE(ahc_phase_table) - 1;
+
+/*
+ * Valid SCSIRATE values.  (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsixfer reg.
+ */
+static const struct ahc_syncrate ahc_syncrates[] =
+{
+      /* ultra2    fast/ultra  period     rate */
+	{ 0x42,      0x000,      9,      "80.0" },
+	{ 0x03,      0x000,     10,      "40.0" },
+	{ 0x04,      0x000,     11,      "33.0" },
+	{ 0x05,      0x100,     12,      "20.0" },
+	{ 0x06,      0x110,     15,      "16.0" },
+	{ 0x07,      0x120,     18,      "13.4" },
+	{ 0x08,      0x000,     25,      "10.0" },
+	{ 0x19,      0x010,     31,      "8.0"  },
+	{ 0x1a,      0x020,     37,      "6.67" },
+	{ 0x1b,      0x030,     43,      "5.7"  },
+	{ 0x1c,      0x040,     50,      "5.0"  },
+	{ 0x00,      0x050,     56,      "4.4"  },
+	{ 0x00,      0x060,     62,      "4.0"  },
+	{ 0x00,      0x070,     68,      "3.6"  },
+	{ 0x00,      0x000,      0,      NULL   }
+};
+
+/* Our Sequencer Program */
+#include "aic7xxx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void		ahc_force_renegotiation(struct ahc_softc *ahc,
+						struct ahc_devinfo *devinfo);
+static struct ahc_tmode_tstate*
+			ahc_alloc_tstate(struct ahc_softc *ahc,
+					 u_int scsi_id, char channel);
+#ifdef AHC_TARGET_MODE
+static void		ahc_free_tstate(struct ahc_softc *ahc,
+					u_int scsi_id, char channel, int force);
+#endif
+static const struct ahc_syncrate*
+			ahc_devlimited_syncrate(struct ahc_softc *ahc,
+					        struct ahc_initiator_tinfo *,
+						u_int *period,
+						u_int *ppr_options,
+						role_t role);
+static void		ahc_update_pending_scbs(struct ahc_softc *ahc);
+static void		ahc_fetch_devinfo(struct ahc_softc *ahc,
+					  struct ahc_devinfo *devinfo);
+static void		ahc_scb_devinfo(struct ahc_softc *ahc,
+					struct ahc_devinfo *devinfo,
+					struct scb *scb);
+static void		ahc_assert_atn(struct ahc_softc *ahc);
+static void		ahc_setup_initiator_msgout(struct ahc_softc *ahc,
+						   struct ahc_devinfo *devinfo,
+						   struct scb *scb);
+static void		ahc_build_transfer_msg(struct ahc_softc *ahc,
+					       struct ahc_devinfo *devinfo);
+static void		ahc_construct_sdtr(struct ahc_softc *ahc,
+					   struct ahc_devinfo *devinfo,
+					   u_int period, u_int offset);
+static void		ahc_construct_wdtr(struct ahc_softc *ahc,
+					   struct ahc_devinfo *devinfo,
+					   u_int bus_width);
+static void		ahc_construct_ppr(struct ahc_softc *ahc,
+					  struct ahc_devinfo *devinfo,
+					  u_int period, u_int offset,
+					  u_int bus_width, u_int ppr_options);
+static void		ahc_clear_msg_state(struct ahc_softc *ahc);
+static void		ahc_handle_proto_violation(struct ahc_softc *ahc);
+static void		ahc_handle_message_phase(struct ahc_softc *ahc);
+typedef enum {
+	AHCMSG_1B,
+	AHCMSG_2B,
+	AHCMSG_EXT
+} ahc_msgtype;
+static int		ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
+				     u_int msgval, int full);
+static int		ahc_parse_msg(struct ahc_softc *ahc,
+				      struct ahc_devinfo *devinfo);
+static int		ahc_handle_msg_reject(struct ahc_softc *ahc,
+					      struct ahc_devinfo *devinfo);
+static void		ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
+						struct ahc_devinfo *devinfo);
+static void		ahc_reinitialize_dataptrs(struct ahc_softc *ahc);
+static void		ahc_handle_devreset(struct ahc_softc *ahc,
+					    struct ahc_devinfo *devinfo,
+					    cam_status status, char *message,
+					    int verbose_level);
+#ifdef AHC_TARGET_MODE
+static void		ahc_setup_target_msgin(struct ahc_softc *ahc,
+					       struct ahc_devinfo *devinfo,
+					       struct scb *scb);
+#endif
+
+static bus_dmamap_callback_t	ahc_dmamap_cb; 
+static void		ahc_build_free_scb_list(struct ahc_softc *ahc);
+static int		ahc_init_scbdata(struct ahc_softc *ahc);
+static void		ahc_fini_scbdata(struct ahc_softc *ahc);
+static void		ahc_qinfifo_requeue(struct ahc_softc *ahc,
+					    struct scb *prev_scb,
+					    struct scb *scb);
+static int		ahc_qinfifo_count(struct ahc_softc *ahc);
+static u_int		ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
+						   u_int prev, u_int scbptr);
+static void		ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
+static u_int		ahc_rem_wscb(struct ahc_softc *ahc,
+				     u_int scbpos, u_int prev);
+static void		ahc_reset_current_bus(struct ahc_softc *ahc);
+#ifdef AHC_DUMP_SEQ
+static void		ahc_dumpseq(struct ahc_softc *ahc);
+#endif
+static int		ahc_loadseq(struct ahc_softc *ahc);
+static int		ahc_check_patch(struct ahc_softc *ahc,
+					const struct patch **start_patch,
+					u_int start_instr, u_int *skip_addr);
+static void		ahc_download_instr(struct ahc_softc *ahc,
+					   u_int instrptr, uint8_t *dconsts);
+#ifdef AHC_TARGET_MODE
+static void		ahc_queue_lstate_event(struct ahc_softc *ahc,
+					       struct ahc_tmode_lstate *lstate,
+					       u_int initiator_id,
+					       u_int event_type,
+					       u_int event_arg);
+static void		ahc_update_scsiid(struct ahc_softc *ahc,
+					  u_int targid_mask);
+static int		ahc_handle_target_cmd(struct ahc_softc *ahc,
+					      struct target_cmd *cmd);
+#endif
+
+static u_int		ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl);
+static void		ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl);
+static void		ahc_busy_tcl(struct ahc_softc *ahc,
+				     u_int tcl, u_int busyid);
+
+/************************** SCB and SCB queue management **********************/
+static void		ahc_run_untagged_queues(struct ahc_softc *ahc);
+static void		ahc_run_untagged_queue(struct ahc_softc *ahc,
+					       struct scb_tailq *queue);
+
+/****************************** Initialization ********************************/
+static void		 ahc_alloc_scbs(struct ahc_softc *ahc);
+static void		 ahc_shutdown(void *arg);
+
+/*************************** Interrupt Services *******************************/
+static void		ahc_clear_intstat(struct ahc_softc *ahc);
+static void		ahc_run_qoutfifo(struct ahc_softc *ahc);
+#ifdef AHC_TARGET_MODE
+static void		ahc_run_tqinfifo(struct ahc_softc *ahc, int paused);
+#endif
+static void		ahc_handle_brkadrint(struct ahc_softc *ahc);
+static void		ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat);
+static void		ahc_handle_scsiint(struct ahc_softc *ahc,
+					   u_int intstat);
+static void		ahc_clear_critical_section(struct ahc_softc *ahc);
+
+/***************************** Error Recovery *********************************/
+static void		ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+static int		ahc_abort_scbs(struct ahc_softc *ahc, int target,
+				       char channel, int lun, u_int tag,
+				       role_t role, uint32_t status);
+static void		ahc_calc_residual(struct ahc_softc *ahc,
+					  struct scb *scb);
+
+/*********************** Untagged Transaction Routines ************************/
+static inline void	ahc_freeze_untagged_queues(struct ahc_softc *ahc);
+static inline void	ahc_release_untagged_queues(struct ahc_softc *ahc);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static inline void
+ahc_freeze_untagged_queues(struct ahc_softc *ahc)
+{
+	if ((ahc->flags & AHC_SCB_BTT) == 0)
+		ahc->untagged_queue_lock++;
+}
+
+/*
+ * Allow the next untagged transaction for this target or target lun
+ * to be executed.  We use a counting semaphore to allow the lock
+ * to be acquired recursively.  Once the count drops to zero, the
+ * transaction queues will be run.
+ */
+static inline void
+ahc_release_untagged_queues(struct ahc_softc *ahc)
+{
+	if ((ahc->flags & AHC_SCB_BTT) == 0) {
+		ahc->untagged_queue_lock--;
+		if (ahc->untagged_queue_lock == 0)
+			ahc_run_untagged_queues(ahc);
+	}
+}
+
+/************************* Sequencer Execution Control ************************/
+/*
+ * Work around any chip bugs related to halting sequencer execution.
+ * On Ultra2 controllers, we must clear the CIOBUS stretch signal by
+ * reading a register that will set this signal and deassert it.
+ * Without this workaround, if the chip is paused, by an interrupt or
+ * manual pause while accessing scb ram, accesses to certain registers
+ * will hang the system (infinite pci retries).
+ */
+static void
+ahc_pause_bug_fix(struct ahc_softc *ahc)
+{
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		(void)ahc_inb(ahc, CCSCBCTL);
+}
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+int
+ahc_is_paused(struct ahc_softc *ahc)
+{
+	return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop.  The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register.  The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+void
+ahc_pause(struct ahc_softc *ahc)
+{
+	ahc_outb(ahc, HCNTRL, ahc->pause);
+
+	/*
+	 * Since the sequencer can disable pausing in a critical section, we
+	 * must loop until it actually stops.
+	 */
+	while (ahc_is_paused(ahc) == 0)
+		;
+
+	ahc_pause_bug_fix(ahc);
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted.  If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+void
+ahc_unpause(struct ahc_softc *ahc)
+{
+	if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
+		ahc_outb(ahc, HCNTRL, ahc->unpause);
+}
+
+/************************** Memory mapping routines ***************************/
+static struct ahc_dma_seg *
+ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr)
+{
+	int sg_index;
+
+	sg_index = (sg_busaddr - scb->sg_list_phys)/sizeof(struct ahc_dma_seg);
+	/* sg_list_phys points to entry 1, not 0 */
+	sg_index++;
+
+	return (&scb->sg_list[sg_index]);
+}
+
+static uint32_t
+ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg)
+{
+	int sg_index;
+
+	/* sg_list_phys points to entry 1, not 0 */
+	sg_index = sg - &scb->sg_list[1];
+
+	return (scb->sg_list_phys + (sg_index * sizeof(*scb->sg_list)));
+}
+
+static uint32_t
+ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
+{
+	return (ahc->scb_data->hscb_busaddr
+		+ (sizeof(struct hardware_scb) * index));
+}
+
+static void
+ahc_sync_scb(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+	ahc_dmamap_sync(ahc, ahc->scb_data->hscb_dmat,
+			ahc->scb_data->hscb_dmamap,
+			/*offset*/(scb->hscb - ahc->hscbs) * sizeof(*scb->hscb),
+			/*len*/sizeof(*scb->hscb), op);
+}
+
+void
+ahc_sync_sglist(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+	if (scb->sg_count == 0)
+		return;
+
+	ahc_dmamap_sync(ahc, ahc->scb_data->sg_dmat, scb->sg_map->sg_dmamap,
+			/*offset*/(scb->sg_list - scb->sg_map->sg_vaddr)
+				* sizeof(struct ahc_dma_seg),
+			/*len*/sizeof(struct ahc_dma_seg) * scb->sg_count, op);
+}
+
+#ifdef AHC_TARGET_MODE
+static uint32_t
+ahc_targetcmd_offset(struct ahc_softc *ahc, u_int index)
+{
+	return (((uint8_t *)&ahc->targetcmds[index]) - ahc->qoutfifo);
+}
+#endif
+
+/*********************** Miscellaneous Support Functions ***********************/
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static void
+ahc_update_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+	uint32_t sgptr;
+
+	sgptr = ahc_le32toh(scb->hscb->sgptr);
+	if ((sgptr & SG_RESID_VALID) != 0)
+		ahc_calc_residual(ahc, scb);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+struct ahc_initiator_tinfo *
+ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
+		    u_int remote_id, struct ahc_tmode_tstate **tstate)
+{
+	/*
+	 * Transfer data structures are stored from the perspective
+	 * of the target role.  Since the parameters for a connection
+	 * in the initiator role to a given target are the same as
+	 * when the roles are reversed, we pretend we are the target.
+	 */
+	if (channel == 'B')
+		our_id += 8;
+	*tstate = ahc->enabled_targets[our_id];
+	return (&(*tstate)->transinfo[remote_id]);
+}
+
+uint16_t
+ahc_inw(struct ahc_softc *ahc, u_int port)
+{
+	uint16_t r = ahc_inb(ahc, port+1) << 8;
+	return r | ahc_inb(ahc, port);
+}
+
+void
+ahc_outw(struct ahc_softc *ahc, u_int port, u_int value)
+{
+	ahc_outb(ahc, port, value & 0xFF);
+	ahc_outb(ahc, port+1, (value >> 8) & 0xFF);
+}
+
+uint32_t
+ahc_inl(struct ahc_softc *ahc, u_int port)
+{
+	return ((ahc_inb(ahc, port))
+	      | (ahc_inb(ahc, port+1) << 8)
+	      | (ahc_inb(ahc, port+2) << 16)
+	      | (ahc_inb(ahc, port+3) << 24));
+}
+
+void
+ahc_outl(struct ahc_softc *ahc, u_int port, uint32_t value)
+{
+	ahc_outb(ahc, port, (value) & 0xFF);
+	ahc_outb(ahc, port+1, ((value) >> 8) & 0xFF);
+	ahc_outb(ahc, port+2, ((value) >> 16) & 0xFF);
+	ahc_outb(ahc, port+3, ((value) >> 24) & 0xFF);
+}
+
+uint64_t
+ahc_inq(struct ahc_softc *ahc, u_int port)
+{
+	return ((ahc_inb(ahc, port))
+	      | (ahc_inb(ahc, port+1) << 8)
+	      | (ahc_inb(ahc, port+2) << 16)
+	      | (ahc_inb(ahc, port+3) << 24)
+	      | (((uint64_t)ahc_inb(ahc, port+4)) << 32)
+	      | (((uint64_t)ahc_inb(ahc, port+5)) << 40)
+	      | (((uint64_t)ahc_inb(ahc, port+6)) << 48)
+	      | (((uint64_t)ahc_inb(ahc, port+7)) << 56));
+}
+
+void
+ahc_outq(struct ahc_softc *ahc, u_int port, uint64_t value)
+{
+	ahc_outb(ahc, port, value & 0xFF);
+	ahc_outb(ahc, port+1, (value >> 8) & 0xFF);
+	ahc_outb(ahc, port+2, (value >> 16) & 0xFF);
+	ahc_outb(ahc, port+3, (value >> 24) & 0xFF);
+	ahc_outb(ahc, port+4, (value >> 32) & 0xFF);
+	ahc_outb(ahc, port+5, (value >> 40) & 0xFF);
+	ahc_outb(ahc, port+6, (value >> 48) & 0xFF);
+	ahc_outb(ahc, port+7, (value >> 56) & 0xFF);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+struct scb *
+ahc_get_scb(struct ahc_softc *ahc)
+{
+	struct scb *scb;
+
+	if ((scb = SLIST_FIRST(&ahc->scb_data->free_scbs)) == NULL) {
+		ahc_alloc_scbs(ahc);
+		scb = SLIST_FIRST(&ahc->scb_data->free_scbs);
+		if (scb == NULL)
+			return (NULL);
+	}
+	SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);
+	return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+void
+ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct hardware_scb *hscb;
+
+	hscb = scb->hscb;
+	/* Clean up for the next user */
+	ahc->scb_data->scbindex[hscb->tag] = NULL;
+	scb->flags = SCB_FREE;
+	hscb->control = 0;
+
+	SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle);
+
+	/* Notify the OSM that a resource is now available. */
+	ahc_platform_scb_free(ahc, scb);
+}
+
+struct scb *
+ahc_lookup_scb(struct ahc_softc *ahc, u_int tag)
+{
+	struct scb* scb;
+
+	scb = ahc->scb_data->scbindex[tag];
+	if (scb != NULL)
+		ahc_sync_scb(ahc, scb,
+			     BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+	return (scb);
+}
+
+static void
+ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct hardware_scb *q_hscb;
+	u_int  saved_tag;
+
+	/*
+	 * Our queuing method is a bit tricky.  The card
+	 * knows in advance which HSCB to download, and we
+	 * can't disappoint it.  To achieve this, the next
+	 * SCB to download is saved off in ahc->next_queued_scb.
+	 * When we are called to queue "an arbitrary scb",
+	 * we copy the contents of the incoming HSCB to the one
+	 * the sequencer knows about, swap HSCB pointers and
+	 * finally assign the SCB to the tag indexed location
+	 * in the scb_array.  This makes sure that we can still
+	 * locate the correct SCB by SCB_TAG.
+	 */
+	q_hscb = ahc->next_queued_scb->hscb;
+	saved_tag = q_hscb->tag;
+	memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+	if ((scb->flags & SCB_CDB32_PTR) != 0) {
+		q_hscb->shared_data.cdb_ptr =
+		    ahc_htole32(ahc_hscb_busaddr(ahc, q_hscb->tag)
+			      + offsetof(struct hardware_scb, cdb32));
+	}
+	q_hscb->tag = saved_tag;
+	q_hscb->next = scb->hscb->tag;
+
+	/* Now swap HSCB pointers. */
+	ahc->next_queued_scb->hscb = scb->hscb;
+	scb->hscb = q_hscb;
+
+	/* Now define the mapping from tag to SCB in the scbindex */
+	ahc->scb_data->scbindex[scb->hscb->tag] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+void
+ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+	ahc_swap_with_next_hscb(ahc, scb);
+
+	if (scb->hscb->tag == SCB_LIST_NULL
+	 || scb->hscb->next == SCB_LIST_NULL)
+		panic("Attempt to queue invalid SCB tag %x:%x\n",
+		      scb->hscb->tag, scb->hscb->next);
+
+	/*
+	 * Setup data "oddness".
+	 */
+	scb->hscb->lun &= LID;
+	if (ahc_get_transfer_length(scb) & 0x1)
+		scb->hscb->lun |= SCB_XFERLEN_ODD;
+
+	/*
+	 * Keep a history of SCBs we've downloaded in the qinfifo.
+	 */
+	ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+
+	/*
+	 * Make sure our data is consistent from the
+	 * perspective of the adapter.
+	 */
+	ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+	/* Tell the adapter about the newly queued SCB */
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+	} else {
+		if ((ahc->features & AHC_AUTOPAUSE) == 0)
+			ahc_pause(ahc);
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+		if ((ahc->features & AHC_AUTOPAUSE) == 0)
+			ahc_unpause(ahc);
+	}
+}
+
+struct scsi_sense_data *
+ahc_get_sense_buf(struct ahc_softc *ahc, struct scb *scb)
+{
+	int offset;
+
+	offset = scb - ahc->scb_data->scbarray;
+	return (&ahc->scb_data->sense[offset]);
+}
+
+static uint32_t
+ahc_get_sense_bufaddr(struct ahc_softc *ahc, struct scb *scb)
+{
+	int offset;
+
+	offset = scb - ahc->scb_data->scbarray;
+	return (ahc->scb_data->sense_busaddr
+	      + (offset * sizeof(struct scsi_sense_data)));
+}
+
+/************************** Interrupt Processing ******************************/
+static void
+ahc_sync_qoutfifo(struct ahc_softc *ahc, int op)
+{
+	ahc_dmamap_sync(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+			/*offset*/0, /*len*/256, op);
+}
+
+static void
+ahc_sync_tqinfifo(struct ahc_softc *ahc, int op)
+{
+#ifdef AHC_TARGET_MODE
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+				ahc->shared_data_dmamap,
+				ahc_targetcmd_offset(ahc, 0),
+				sizeof(struct target_cmd) * AHC_TMODE_CMDS,
+				op);
+	}
+#endif
+}
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHC_RUN_QOUTFIFO 0x1
+#define AHC_RUN_TQINFIFO 0x2
+static u_int
+ahc_check_cmdcmpltqueues(struct ahc_softc *ahc)
+{
+	u_int retval;
+
+	retval = 0;
+	ahc_dmamap_sync(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+			/*offset*/ahc->qoutfifonext, /*len*/1,
+			BUS_DMASYNC_POSTREAD);
+	if (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL)
+		retval |= AHC_RUN_QOUTFIFO;
+#ifdef AHC_TARGET_MODE
+	if ((ahc->flags & AHC_TARGETROLE) != 0
+	 && (ahc->flags & AHC_TQINFIFO_BLOCKED) == 0) {
+		ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+				ahc->shared_data_dmamap,
+				ahc_targetcmd_offset(ahc, ahc->tqinfifofnext),
+				/*len*/sizeof(struct target_cmd),
+				BUS_DMASYNC_POSTREAD);
+		if (ahc->targetcmds[ahc->tqinfifonext].cmd_valid != 0)
+			retval |= AHC_RUN_TQINFIFO;
+	}
+#endif
+	return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+int
+ahc_intr(struct ahc_softc *ahc)
+{
+	u_int	intstat;
+
+	if ((ahc->pause & INTEN) == 0) {
+		/*
+		 * Our interrupt is not enabled on the chip
+		 * and may be disabled for re-entrancy reasons,
+		 * so just return.  This is likely just a shared
+		 * interrupt.
+		 */
+		return (0);
+	}
+	/*
+	 * Instead of directly reading the interrupt status register,
+	 * infer the cause of the interrupt by checking our in-core
+	 * completion queues.  This avoids a costly PCI bus read in
+	 * most cases.
+	 */
+	if ((ahc->flags & (AHC_ALL_INTERRUPTS|AHC_EDGE_INTERRUPT)) == 0
+	 && (ahc_check_cmdcmpltqueues(ahc) != 0))
+		intstat = CMDCMPLT;
+	else {
+		intstat = ahc_inb(ahc, INTSTAT);
+	}
+
+	if ((intstat & INT_PEND) == 0) {
+#if AHC_PCI_CONFIG > 0
+		if (ahc->unsolicited_ints > 500) {
+			ahc->unsolicited_ints = 0;
+			if ((ahc->chip & AHC_PCI) != 0
+			 && (ahc_inb(ahc, ERROR) & PCIERRSTAT) != 0)
+				ahc->bus_intr(ahc);
+		}
+#endif
+		ahc->unsolicited_ints++;
+		return (0);
+	}
+	ahc->unsolicited_ints = 0;
+
+	if (intstat & CMDCMPLT) {
+		ahc_outb(ahc, CLRINT, CLRCMDINT);
+
+		/*
+		 * Ensure that the chip sees that we've cleared
+		 * this interrupt before we walk the output fifo.
+		 * Otherwise, we may, due to posted bus writes,
+		 * clear the interrupt after we finish the scan,
+		 * and after the sequencer has added new entries
+		 * and asserted the interrupt again.
+		 */
+		ahc_flush_device_writes(ahc);
+		ahc_run_qoutfifo(ahc);
+#ifdef AHC_TARGET_MODE
+		if ((ahc->flags & AHC_TARGETROLE) != 0)
+			ahc_run_tqinfifo(ahc, /*paused*/FALSE);
+#endif
+	}
+
+	/*
+	 * Handle statuses that may invalidate our cached
+	 * copy of INTSTAT separately.
+	 */
+	if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0) {
+		/* Hot eject.  Do nothing */
+	} else if (intstat & BRKADRINT) {
+		ahc_handle_brkadrint(ahc);
+	} else if ((intstat & (SEQINT|SCSIINT)) != 0) {
+
+		ahc_pause_bug_fix(ahc);
+
+		if ((intstat & SEQINT) != 0)
+			ahc_handle_seqint(ahc, intstat);
+
+		if ((intstat & SCSIINT) != 0)
+			ahc_handle_scsiint(ahc, intstat);
+	}
+	return (1);
+}
+
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+static void
+ahc_restart(struct ahc_softc *ahc)
+{
+	uint8_t	sblkctl;
+
+	ahc_pause(ahc);
+
+	/* No more pending messages. */
+	ahc_clear_msg_state(ahc);
+
+	ahc_outb(ahc, SCSISIGO, 0);		/* De-assert BSY */
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);	/* No message to send */
+	ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+	ahc_outb(ahc, LASTPHASE, P_BUSFREE);
+	ahc_outb(ahc, SAVED_SCSIID, 0xFF);
+	ahc_outb(ahc, SAVED_LUN, 0xFF);
+
+	/*
+	 * Ensure that the sequencer's idea of TQINPOS
+	 * matches our own.  The sequencer increments TQINPOS
+	 * only after it sees a DMA complete and a reset could
+	 * occur before the increment leaving the kernel to believe
+	 * the command arrived but the sequencer to not.
+	 */
+	ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+
+	/* Always allow reselection */
+	ahc_outb(ahc, SCSISEQ,
+		 ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		/* Ensure that no DMA operations are in progress */
+		ahc_outb(ahc, CCSCBCNT, 0);
+		ahc_outb(ahc, CCSGCTL, 0);
+		ahc_outb(ahc, CCSCBCTL, 0);
+	}
+	/*
+	 * If we were in the process of DMA'ing SCB data into
+	 * an SCB, replace that SCB on the free list.  This prevents
+	 * an SCB leak.
+	 */
+	if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
+		ahc_add_curscb_to_free_list(ahc);
+		ahc_outb(ahc, SEQ_FLAGS2,
+			 ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
+	}
+
+	/*
+	 * Clear any pending sequencer interrupt.  It is no
+	 * longer relevant since we're resetting the Program
+	 * Counter.
+	 */
+	ahc_outb(ahc, CLRINT, CLRSEQINT);
+
+	ahc_outb(ahc, MWI_RESIDUAL, 0);
+	ahc_outb(ahc, SEQCTL, ahc->seqctl);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+
+	/*
+	 * Take the LED out of diagnostic mode on PM resume, too
+	 */
+	sblkctl = ahc_inb(ahc, SBLKCTL);
+	ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
+
+	ahc_unpause(ahc);
+}
+
+/************************* Input/Output Queues ********************************/
+static void
+ahc_run_qoutfifo(struct ahc_softc *ahc)
+{
+	struct scb *scb;
+	u_int  scb_index;
+
+	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+	while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
+
+		scb_index = ahc->qoutfifo[ahc->qoutfifonext];
+		if ((ahc->qoutfifonext & 0x03) == 0x03) {
+			u_int modnext;
+
+			/*
+			 * Clear 32bits of QOUTFIFO at a time
+			 * so that we don't clobber an incoming
+			 * byte DMA to the array on architectures
+			 * that only support 32bit load and store
+			 * operations.
+			 */
+			modnext = ahc->qoutfifonext & ~0x3;
+			*((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
+			ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+					ahc->shared_data_dmamap,
+					/*offset*/modnext, /*len*/4,
+					BUS_DMASYNC_PREREAD);
+		}
+		ahc->qoutfifonext++;
+
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printk("%s: WARNING no command for scb %d "
+			       "(cmdcmplt)\nQOUTPOS = %d\n",
+			       ahc_name(ahc), scb_index,
+			       (ahc->qoutfifonext - 1) & 0xFF);
+			continue;
+		}
+
+		/*
+		 * Save off the residual
+		 * if there is one.
+		 */
+		ahc_update_residual(ahc, scb);
+		ahc_done(ahc, scb);
+	}
+}
+
+static void
+ahc_run_untagged_queues(struct ahc_softc *ahc)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
+}
+
+static void
+ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
+{
+	struct scb *scb;
+
+	if (ahc->untagged_queue_lock != 0)
+		return;
+
+	if ((scb = TAILQ_FIRST(queue)) != NULL
+	 && (scb->flags & SCB_ACTIVE) == 0) {
+		scb->flags |= SCB_ACTIVE;
+		ahc_queue_scb(ahc, scb);
+	}
+}
+
+/************************* Interrupt Handling *********************************/
+static void
+ahc_handle_brkadrint(struct ahc_softc *ahc)
+{
+	/*
+	 * We upset the sequencer :-(
+	 * Lookup the error message
+	 */
+	int i;
+	int error;
+
+	error = ahc_inb(ahc, ERROR);
+	for (i = 0; error != 1 && i < num_errors; i++)
+		error >>= 1;
+	printk("%s: brkadrint, %s at seqaddr = 0x%x\n",
+	       ahc_name(ahc), ahc_hard_errors[i].errmesg,
+	       ahc_inb(ahc, SEQADDR0) |
+	       (ahc_inb(ahc, SEQADDR1) << 8));
+
+	ahc_dump_card_state(ahc);
+
+	/* Tell everyone that this HBA is no longer available */
+	ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+		       CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+		       CAM_NO_HBA);
+
+	/* Disable all interrupt sources by resetting the controller */
+	ahc_shutdown(ahc);
+}
+
+static void
+ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
+{
+	struct scb *scb;
+	struct ahc_devinfo devinfo;
+	
+	ahc_fetch_devinfo(ahc, &devinfo);
+
+	/*
+	 * Clear the upper byte that holds SEQINT status
+	 * codes and clear the SEQINT bit. We will unpause
+	 * the sequencer, if appropriate, after servicing
+	 * the request.
+	 */
+	ahc_outb(ahc, CLRINT, CLRSEQINT);
+	switch (intstat & SEQINT_MASK) {
+	case BAD_STATUS:
+	{
+		u_int  scb_index;
+		struct hardware_scb *hscb;
+
+		/*
+		 * Set the default return value to 0 (don't
+		 * send sense).  The sense code will change
+		 * this if needed.
+		 */
+		ahc_outb(ahc, RETURN_1, 0);
+
+		/*
+		 * The sequencer will notify us when a command
+		 * has an error that would be of interest to
+		 * the kernel.  This allows us to leave the sequencer
+		 * running in the common case of command completes
+		 * without error.  The sequencer will already have
+		 * dma'd the SCB back up to us, so we can reference
+		 * the in kernel copy directly.
+		 */
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			ahc_print_devinfo(ahc, &devinfo);
+			printk("ahc_intr - referenced scb "
+			       "not valid during seqint 0x%x scb(%d)\n",
+			       intstat, scb_index);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+			goto unpause;
+		}
+
+		hscb = scb->hscb; 
+
+		/* Don't want to clobber the original sense code */
+		if ((scb->flags & SCB_SENSE) != 0) {
+			/*
+			 * Clear the SCB_SENSE Flag and have
+			 * the sequencer do a normal command
+			 * complete.
+			 */
+			scb->flags &= ~SCB_SENSE;
+			ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+			break;
+		}
+		ahc_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+		/* Freeze the queue until the client sees the error. */
+		ahc_freeze_devq(ahc, scb);
+		ahc_freeze_scb(scb);
+		ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
+		switch (hscb->shared_data.status.scsi_status) {
+		case SCSI_STATUS_OK:
+			printk("%s: Interrupted for status of 0???\n",
+			       ahc_name(ahc));
+			break;
+		case SCSI_STATUS_CMD_TERMINATED:
+		case SCSI_STATUS_CHECK_COND:
+		{
+			struct ahc_dma_seg *sg;
+			struct scsi_sense *sc;
+			struct ahc_initiator_tinfo *targ_info;
+			struct ahc_tmode_tstate *tstate;
+			struct ahc_transinfo *tinfo;
+#ifdef AHC_DEBUG
+			if (ahc_debug & AHC_SHOW_SENSE) {
+				ahc_print_path(ahc, scb);
+				printk("SCB %d: requests Check Status\n",
+				       scb->hscb->tag);
+			}
+#endif
+
+			if (ahc_perform_autosense(scb) == 0)
+				break;
+
+			targ_info = ahc_fetch_transinfo(ahc,
+							devinfo.channel,
+							devinfo.our_scsiid,
+							devinfo.target,
+							&tstate);
+			tinfo = &targ_info->curr;
+			sg = scb->sg_list;
+			sc = (struct scsi_sense *)(&hscb->shared_data.cdb); 
+			/*
+			 * Save off the residual if there is one.
+			 */
+			ahc_update_residual(ahc, scb);
+#ifdef AHC_DEBUG
+			if (ahc_debug & AHC_SHOW_SENSE) {
+				ahc_print_path(ahc, scb);
+				printk("Sending Sense\n");
+			}
+#endif
+			sg->addr = ahc_get_sense_bufaddr(ahc, scb);
+			sg->len = ahc_get_sense_bufsize(ahc, scb);
+			sg->len |= AHC_DMA_LAST_SEG;
+
+			/* Fixup byte order */
+			sg->addr = ahc_htole32(sg->addr);
+			sg->len = ahc_htole32(sg->len);
+
+			sc->opcode = REQUEST_SENSE;
+			sc->byte2 = 0;
+			if (tinfo->protocol_version <= SCSI_REV_2
+			 && SCB_GET_LUN(scb) < 8)
+				sc->byte2 = SCB_GET_LUN(scb) << 5;
+			sc->unused[0] = 0;
+			sc->unused[1] = 0;
+			sc->length = sg->len;
+			sc->control = 0;
+
+			/*
+			 * We can't allow the target to disconnect.
+			 * This will be an untagged transaction and
+			 * having the target disconnect will make this
+			 * transaction indestinguishable from outstanding
+			 * tagged transactions.
+			 */
+			hscb->control = 0;
+
+			/*
+			 * This request sense could be because the
+			 * the device lost power or in some other
+			 * way has lost our transfer negotiations.
+			 * Renegotiate if appropriate.  Unit attention
+			 * errors will be reported before any data
+			 * phases occur.
+			 */
+			if (ahc_get_residual(scb) 
+			 == ahc_get_transfer_length(scb)) {
+				ahc_update_neg_request(ahc, &devinfo,
+						       tstate, targ_info,
+						       AHC_NEG_IF_NON_ASYNC);
+			}
+			if (tstate->auto_negotiate & devinfo.target_mask) {
+				hscb->control |= MK_MESSAGE;
+				scb->flags &= ~SCB_NEGOTIATE;
+				scb->flags |= SCB_AUTO_NEGOTIATE;
+			}
+			hscb->cdb_len = sizeof(*sc);
+			hscb->dataptr = sg->addr; 
+			hscb->datacnt = sg->len;
+			hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
+			hscb->sgptr = ahc_htole32(hscb->sgptr);
+			scb->sg_count = 1;
+			scb->flags |= SCB_SENSE;
+			ahc_qinfifo_requeue_tail(ahc, scb);
+			ahc_outb(ahc, RETURN_1, SEND_SENSE);
+			/*
+			 * Ensure we have enough time to actually
+			 * retrieve the sense.
+			 */
+			ahc_scb_timer_reset(scb, 5 * 1000000);
+			break;
+		}
+		default:
+			break;
+		}
+		break;
+	}
+	case NO_MATCH:
+	{
+		/* Ensure we don't leave the selection hardware on */
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+		printk("%s:%c:%d: no active SCB for reconnecting "
+		       "target - issuing BUS DEVICE RESET\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target);
+		printk("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+		       "ARG_1 == 0x%x ACCUM = 0x%x\n",
+		       ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+		       ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+		printk("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+		       "SINDEX == 0x%x\n",
+		       ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+		       ahc_index_busy_tcl(ahc,
+			    BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+				      ahc_inb(ahc, SAVED_LUN))),
+		       ahc_inb(ahc, SINDEX));
+		printk("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+		       "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+		       ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+		       ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+		       ahc_inb(ahc, SCB_CONTROL));
+		printk("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+		       ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+		printk("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
+		printk("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
+		ahc_dump_card_state(ahc);
+		ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
+		ahc->msgout_len = 1;
+		ahc->msgout_index = 0;
+		ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+		ahc_outb(ahc, MSG_OUT, HOST_MSG);
+		ahc_assert_atn(ahc);
+		break;
+	}
+	case SEND_REJECT: 
+	{
+		u_int rejbyte = ahc_inb(ahc, ACCUM);
+		printk("%s:%c:%d: Warning - unknown message received from "
+		       "target (0x%x).  Rejecting\n", 
+		       ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
+		break; 
+	}
+	case PROTO_VIOLATION:
+	{
+		ahc_handle_proto_violation(ahc);
+		break;
+	}
+	case IGN_WIDE_RES:
+		ahc_handle_ign_wide_residue(ahc, &devinfo);
+		break;
+	case PDATA_REINIT:
+		ahc_reinitialize_dataptrs(ahc);
+		break;
+	case BAD_PHASE:
+	{
+		u_int lastphase;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		printk("%s:%c:%d: unknown scsi bus phase %x, "
+		       "lastphase = 0x%x.  Attempting to continue\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       lastphase, ahc_inb(ahc, SCSISIGI));
+		break;
+	}
+	case MISSED_BUSFREE:
+	{
+		u_int lastphase;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		printk("%s:%c:%d: Missed busfree. "
+		       "Lastphase = 0x%x, Curphase = 0x%x\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       lastphase, ahc_inb(ahc, SCSISIGI));
+		ahc_restart(ahc);
+		return;
+	}
+	case HOST_MSG_LOOP:
+	{
+		/*
+		 * The sequencer has encountered a message phase
+		 * that requires host assistance for completion.
+		 * While handling the message phase(s), we will be
+		 * notified by the sequencer after each byte is
+		 * transferred so we can track bus phase changes.
+		 *
+		 * If this is the first time we've seen a HOST_MSG_LOOP
+		 * interrupt, initialize the state of the host message
+		 * loop.
+		 */
+		if (ahc->msg_type == MSG_TYPE_NONE) {
+			struct scb *scb;
+			u_int scb_index;
+			u_int bus_phase;
+
+			bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+			if (bus_phase != P_MESGIN
+			 && bus_phase != P_MESGOUT) {
+				printk("ahc_intr: HOST_MSG_LOOP bad "
+				       "phase 0x%x\n",
+				      bus_phase);
+				/*
+				 * Probably transitioned to bus free before
+				 * we got here.  Just punt the message.
+				 */
+				ahc_clear_intstat(ahc);
+				ahc_restart(ahc);
+				return;
+			}
+
+			scb_index = ahc_inb(ahc, SCB_TAG);
+			scb = ahc_lookup_scb(ahc, scb_index);
+			if (devinfo.role == ROLE_INITIATOR) {
+				if (bus_phase == P_MESGOUT) {
+					if (scb == NULL)
+						panic("HOST_MSG_LOOP with "
+						      "invalid SCB %x\n",
+						      scb_index);
+
+					ahc_setup_initiator_msgout(ahc,
+								   &devinfo,
+								   scb);
+				} else {
+					ahc->msg_type =
+					    MSG_TYPE_INITIATOR_MSGIN;
+					ahc->msgin_index = 0;
+				}
+			}
+#ifdef AHC_TARGET_MODE
+			else {
+				if (bus_phase == P_MESGOUT) {
+					ahc->msg_type =
+					    MSG_TYPE_TARGET_MSGOUT;
+					ahc->msgin_index = 0;
+				}
+				else 
+					ahc_setup_target_msgin(ahc,
+							       &devinfo,
+							       scb);
+			}
+#endif
+		}
+
+		ahc_handle_message_phase(ahc);
+		break;
+	}
+	case PERR_DETECTED:
+	{
+		/*
+		 * If we've cleared the parity error interrupt
+		 * but the sequencer still believes that SCSIPERR
+		 * is true, it must be that the parity error is
+		 * for the currently presented byte on the bus,
+		 * and we are not in a phase (data-in) where we will
+		 * eventually ack this byte.  Ack the byte and
+		 * throw it away in the hope that the target will
+		 * take us to message out to deliver the appropriate
+		 * error message.
+		 */
+		if ((intstat & SCSIINT) == 0
+		 && (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
+
+			if ((ahc->features & AHC_DT) == 0) {
+				u_int curphase;
+
+				/*
+				 * The hardware will only let you ack bytes
+				 * if the expected phase in SCSISIGO matches
+				 * the current phase.  Make sure this is
+				 * currently the case.
+				 */
+				curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+				ahc_outb(ahc, LASTPHASE, curphase);
+				ahc_outb(ahc, SCSISIGO, curphase);
+			}
+			if ((ahc_inb(ahc, SCSISIGI) & (CDI|MSGI)) == 0) {
+				int wait;
+
+				/*
+				 * In a data phase.  Faster to bitbucket
+				 * the data than to individually ack each
+				 * byte.  This is also the only strategy
+				 * that will work with AUTOACK enabled.
+				 */
+				ahc_outb(ahc, SXFRCTL1,
+					 ahc_inb(ahc, SXFRCTL1) | BITBUCKET);
+				wait = 5000;
+				while (--wait != 0) {
+					if ((ahc_inb(ahc, SCSISIGI)
+					  & (CDI|MSGI)) != 0)
+						break;
+					ahc_delay(100);
+				}
+				ahc_outb(ahc, SXFRCTL1,
+					 ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+				if (wait == 0) {
+					struct	scb *scb;
+					u_int	scb_index;
+
+					ahc_print_devinfo(ahc, &devinfo);
+					printk("Unable to clear parity error.  "
+					       "Resetting bus.\n");
+					scb_index = ahc_inb(ahc, SCB_TAG);
+					scb = ahc_lookup_scb(ahc, scb_index);
+					if (scb != NULL)
+						ahc_set_transaction_status(scb,
+						    CAM_UNCOR_PARITY);
+					ahc_reset_channel(ahc, devinfo.channel, 
+							  /*init reset*/TRUE);
+				}
+			} else {
+				ahc_inb(ahc, SCSIDATL);
+			}
+		}
+		break;
+	}
+	case DATA_OVERRUN:
+	{
+		/*
+		 * When the sequencer detects an overrun, it
+		 * places the controller in "BITBUCKET" mode
+		 * and allows the target to complete its transfer.
+		 * Unfortunately, none of the counters get updated
+		 * when the controller is in this mode, so we have
+		 * no way of knowing how large the overrun was.
+		 */
+		u_int scbindex = ahc_inb(ahc, SCB_TAG);
+		u_int lastphase = ahc_inb(ahc, LASTPHASE);
+		u_int i;
+
+		scb = ahc_lookup_scb(ahc, scbindex);
+		for (i = 0; i < num_phases; i++) {
+			if (lastphase == ahc_phase_table[i].phase)
+				break;
+		}
+		ahc_print_path(ahc, scb);
+		printk("data overrun detected %s."
+		       "  Tag == 0x%x.\n",
+		       ahc_phase_table[i].phasemsg,
+  		       scb->hscb->tag);
+		ahc_print_path(ahc, scb);
+		printk("%s seen Data Phase.  Length = %ld.  NumSGs = %d.\n",
+		       ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+		       ahc_get_transfer_length(scb), scb->sg_count);
+		if (scb->sg_count > 0) {
+			for (i = 0; i < scb->sg_count; i++) {
+
+				printk("sg[%d] - Addr 0x%x%x : Length %d\n",
+				       i,
+				       (ahc_le32toh(scb->sg_list[i].len) >> 24
+				        & SG_HIGH_ADDR_BITS),
+				       ahc_le32toh(scb->sg_list[i].addr),
+				       ahc_le32toh(scb->sg_list[i].len)
+				       & AHC_SG_LEN_MASK);
+			}
+		}
+		/*
+		 * Set this and it will take effect when the
+		 * target does a command complete.
+		 */
+		ahc_freeze_devq(ahc, scb);
+		if ((scb->flags & SCB_SENSE) == 0) {
+			ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+		} else {
+			scb->flags &= ~SCB_SENSE;
+			ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+		}
+		ahc_freeze_scb(scb);
+
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			/*
+			 * Clear the channel in case we return
+			 * to data phase later.
+			 */
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+		}
+		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+			u_int dscommand1;
+
+			/* Ensure HHADDR is 0 for future DMA operations. */
+			dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+			ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+			ahc_outb(ahc, HADDR, 0);
+			ahc_outb(ahc, DSCOMMAND1, dscommand1);
+		}
+		break;
+	}
+	case MKMSG_FAILED:
+	{
+		u_int scbindex;
+
+		printk("%s:%c:%d:%d: Attempt to issue message failed\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       devinfo.lun);
+		scbindex = ahc_inb(ahc, SCB_TAG);
+		scb = ahc_lookup_scb(ahc, scbindex);
+		if (scb != NULL
+		 && (scb->flags & SCB_RECOVERY_SCB) != 0)
+			/*
+			 * Ensure that we didn't put a second instance of this
+			 * SCB into the QINFIFO.
+			 */
+			ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+					   SCB_GET_CHANNEL(ahc, scb),
+					   SCB_GET_LUN(scb), scb->hscb->tag,
+					   ROLE_INITIATOR, /*status*/0,
+					   SEARCH_REMOVE);
+		break;
+	}
+	case NO_FREE_SCB:
+	{
+		printk("%s: No free or disconnected SCBs\n", ahc_name(ahc));
+		ahc_dump_card_state(ahc);
+		panic("for safety");
+		break;
+	}
+	case SCB_MISMATCH:
+	{
+		u_int scbptr;
+
+		scbptr = ahc_inb(ahc, SCBPTR);
+		printk("Bogus TAG after DMA.  SCBPTR %d, tag %d, our tag %d\n",
+		       scbptr, ahc_inb(ahc, ARG_1),
+		       ahc->scb_data->hscbs[scbptr].tag);
+		ahc_dump_card_state(ahc);
+		panic("for safety");
+		break;
+	}
+	case OUT_OF_RANGE:
+	{
+		printk("%s: BTT calculation out of range\n", ahc_name(ahc));
+		printk("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+		       "ARG_1 == 0x%x ACCUM = 0x%x\n",
+		       ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+		       ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+		printk("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+		       "SINDEX == 0x%x\n, A == 0x%x\n",
+		       ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+		       ahc_index_busy_tcl(ahc,
+			    BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+				      ahc_inb(ahc, SAVED_LUN))),
+		       ahc_inb(ahc, SINDEX),
+		       ahc_inb(ahc, ACCUM));
+		printk("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+		       "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+		       ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+		       ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+		       ahc_inb(ahc, SCB_CONTROL));
+		printk("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+		       ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+		ahc_dump_card_state(ahc);
+		panic("for safety");
+		break;
+	}
+	default:
+		printk("ahc_intr: seqint, "
+		       "intstat == 0x%x, scsisigi = 0x%x\n",
+		       intstat, ahc_inb(ahc, SCSISIGI));
+		break;
+	}
+unpause:
+	/*
+	 *  The sequencer is paused immediately on
+	 *  a SEQINT, so we should restart it when
+	 *  we're done.
+	 */
+	ahc_unpause(ahc);
+}
+
+static void
+ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
+{
+	u_int	scb_index;
+	u_int	status0;
+	u_int	status;
+	struct	scb *scb;
+	char	cur_channel;
+	char	intr_channel;
+
+	if ((ahc->features & AHC_TWIN) != 0
+	 && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
+		cur_channel = 'B';
+	else
+		cur_channel = 'A';
+	intr_channel = cur_channel;
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		status0 = ahc_inb(ahc, SSTAT0) & IOERR;
+	else
+		status0 = 0;
+	status = ahc_inb(ahc, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+	if (status == 0 && status0 == 0) {
+		if ((ahc->features & AHC_TWIN) != 0) {
+			/* Try the other channel */
+		 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+			status = ahc_inb(ahc, SSTAT1)
+			       & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+			intr_channel = (cur_channel == 'A') ? 'B' : 'A';
+		}
+		if (status == 0) {
+			printk("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
+			ahc_outb(ahc, CLRINT, CLRSCSIINT);
+			ahc_unpause(ahc);
+			return;
+		}
+	}
+
+	/* Make sure the sequencer is in a safe location. */
+	ahc_clear_critical_section(ahc);
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	if (scb != NULL
+	 && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+		scb = NULL;
+
+	if ((ahc->features & AHC_ULTRA2) != 0
+	 && (status0 & IOERR) != 0) {
+		int now_lvd;
+
+		now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
+		printk("%s: Transceiver State Has Changed to %s mode\n",
+		       ahc_name(ahc), now_lvd ? "LVD" : "SE");
+		ahc_outb(ahc, CLRSINT0, CLRIOERR);
+		/*
+		 * When transitioning to SE mode, the reset line
+		 * glitches, triggering an arbitration bug in some
+		 * Ultra2 controllers.  This bug is cleared when we
+		 * assert the reset line.  Since a reset glitch has
+		 * already occurred with this transition and a
+		 * transceiver state change is handled just like
+		 * a bus reset anyway, asserting the reset line
+		 * ourselves is safe.
+		 */
+		ahc_reset_channel(ahc, intr_channel,
+				 /*Initiate Reset*/now_lvd == 0);
+	} else if ((status & SCSIRSTI) != 0) {
+		printk("%s: Someone reset channel %c\n",
+			ahc_name(ahc), intr_channel);
+		if (intr_channel != cur_channel)
+		 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+		ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
+	} else if ((status & SCSIPERR) != 0) {
+		/*
+		 * Determine the bus phase and queue an appropriate message.
+		 * SCSIPERR is latched true as soon as a parity error
+		 * occurs.  If the sequencer acked the transfer that
+		 * caused the parity error and the currently presented
+		 * transfer on the bus has correct parity, SCSIPERR will
+		 * be cleared by CLRSCSIPERR.  Use this to determine if
+		 * we should look at the last phase the sequencer recorded,
+		 * or the current phase presented on the bus.
+		 */
+		struct	ahc_devinfo devinfo;
+		u_int	mesg_out;
+		u_int	curphase;
+		u_int	errorphase;
+		u_int	lastphase;
+		u_int	scsirate;
+		u_int	i;
+		u_int	sstat2;
+		int	silent;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+		sstat2 = ahc_inb(ahc, SSTAT2);
+		ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
+		/*
+		 * For all phases save DATA, the sequencer won't
+		 * automatically ack a byte that has a parity error
+		 * in it.  So the only way that the current phase
+		 * could be 'data-in' is if the parity error is for
+		 * an already acked byte in the data phase.  During
+		 * synchronous data-in transfers, we may actually
+		 * ack bytes before latching the current phase in
+		 * LASTPHASE, leading to the discrepancy between
+		 * curphase and lastphase.
+		 */
+		if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
+		 || curphase == P_DATAIN || curphase == P_DATAIN_DT)
+			errorphase = curphase;
+		else
+			errorphase = lastphase;
+
+		for (i = 0; i < num_phases; i++) {
+			if (errorphase == ahc_phase_table[i].phase)
+				break;
+		}
+		mesg_out = ahc_phase_table[i].mesg_out;
+		silent = FALSE;
+		if (scb != NULL) {
+			if (SCB_IS_SILENT(scb))
+				silent = TRUE;
+			else
+				ahc_print_path(ahc, scb);
+			scb->flags |= SCB_TRANSMISSION_ERROR;
+		} else
+			printk("%s:%c:%d: ", ahc_name(ahc), intr_channel,
+			       SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
+		scsirate = ahc_inb(ahc, SCSIRATE);
+		if (silent == FALSE) {
+			printk("parity error detected %s. "
+			       "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
+			       ahc_phase_table[i].phasemsg,
+			       ahc_inw(ahc, SEQADDR0),
+			       scsirate);
+			if ((ahc->features & AHC_DT) != 0) {
+				if ((sstat2 & CRCVALERR) != 0)
+					printk("\tCRC Value Mismatch\n");
+				if ((sstat2 & CRCENDERR) != 0)
+					printk("\tNo terminal CRC packet "
+					       "recevied\n");
+				if ((sstat2 & CRCREQERR) != 0)
+					printk("\tIllegal CRC packet "
+					       "request\n");
+				if ((sstat2 & DUAL_EDGE_ERR) != 0)
+					printk("\tUnexpected %sDT Data Phase\n",
+					       (scsirate & SINGLE_EDGE)
+					     ? "" : "non-");
+			}
+		}
+
+		if ((ahc->features & AHC_DT) != 0
+		 && (sstat2 & DUAL_EDGE_ERR) != 0) {
+			/*
+			 * This error applies regardless of
+			 * data direction, so ignore the value
+			 * in the phase table.
+			 */
+			mesg_out = MSG_INITIATOR_DET_ERR;
+		}
+
+		/*
+		 * We've set the hardware to assert ATN if we   
+		 * get a parity error on "in" phases, so all we  
+		 * need to do is stuff the message buffer with
+		 * the appropriate message.  "In" phases have set
+		 * mesg_out to something other than MSG_NOP.
+		 */
+		if (mesg_out != MSG_NOOP) {
+			if (ahc->msg_type != MSG_TYPE_NONE)
+				ahc->send_msg_perror = TRUE;
+			else
+				ahc_outb(ahc, MSG_OUT, mesg_out);
+		}
+		/*
+		 * Force a renegotiation with this target just in
+		 * case we are out of sync for some external reason
+		 * unknown (or unreported) by the target.
+		 */
+		ahc_fetch_devinfo(ahc, &devinfo);
+		ahc_force_renegotiation(ahc, &devinfo);
+
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		ahc_unpause(ahc);
+	} else if ((status & SELTO) != 0) {
+		u_int	scbptr;
+
+		/* Stop the selection */
+		ahc_outb(ahc, SCSISEQ, 0);
+
+		/* No more pending messages */
+		ahc_clear_msg_state(ahc);
+
+		/* Clear interrupt state */
+		ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+		ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+		/*
+		 * Although the driver does not care about the
+		 * 'Selection in Progress' status bit, the busy
+		 * LED does.  SELINGO is only cleared by a successful
+		 * selection, so we must manually clear it to insure
+		 * the LED turns off just incase no future successful
+		 * selections occur (e.g. no devices on the bus).
+		 */
+		ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+
+		scbptr = ahc_inb(ahc, WAITING_SCBH);
+		ahc_outb(ahc, SCBPTR, scbptr);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printk("%s: ahc_intr - referenced scb not "
+			       "valid during SELTO scb(%d, %d)\n",
+			       ahc_name(ahc), scbptr, scb_index);
+			ahc_dump_card_state(ahc);
+		} else {
+			struct ahc_devinfo devinfo;
+#ifdef AHC_DEBUG
+			if ((ahc_debug & AHC_SHOW_SELTO) != 0) {
+				ahc_print_path(ahc, scb);
+				printk("Saw Selection Timeout for SCB 0x%x\n",
+				       scb_index);
+			}
+#endif
+			ahc_scb_devinfo(ahc, &devinfo, scb);
+			ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+			ahc_freeze_devq(ahc, scb);
+
+			/*
+			 * Cancel any pending transactions on the device
+			 * now that it seems to be missing.  This will
+			 * also revert us to async/narrow transfers until
+			 * we can renegotiate with the device.
+			 */
+			ahc_handle_devreset(ahc, &devinfo,
+					    CAM_SEL_TIMEOUT,
+					    "Selection Timeout",
+					    /*verbose_level*/1);
+		}
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		ahc_restart(ahc);
+	} else if ((status & BUSFREE) != 0
+		&& (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
+		struct	ahc_devinfo devinfo;
+		u_int	lastphase;
+		u_int	saved_scsiid;
+		u_int	saved_lun;
+		u_int	target;
+		u_int	initiator_role_id;
+		char	channel;
+		int	printerror;
+
+		/*
+		 * Clear our selection hardware as soon as possible.
+		 * We may have an entry in the waiting Q for this target,
+		 * that is affected by this busfree and we don't want to
+		 * go about selecting the target while we handle the event.
+		 */
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+		/*
+		 * Disable busfree interrupts and clear the busfree
+		 * interrupt status.  We do this here so that several
+		 * bus transactions occur prior to clearing the SCSIINT
+		 * latch.  It can take a bit for the clearing to take effect.
+		 */
+		ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+		ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
+		/*
+		 * Look at what phase we were last in.
+		 * If its message out, chances are pretty good
+		 * that the busfree was in response to one of
+		 * our abort requests.
+		 */
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+		saved_lun = ahc_inb(ahc, SAVED_LUN);
+		target = SCSIID_TARGET(ahc, saved_scsiid);
+		initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+		channel = SCSIID_CHANNEL(ahc, saved_scsiid);
+		ahc_compile_devinfo(&devinfo, initiator_role_id,
+				    target, saved_lun, channel, ROLE_INITIATOR);
+		printerror = 1;
+
+		if (lastphase == P_MESGOUT) {
+			u_int tag;
+
+			tag = SCB_LIST_NULL;
+			if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
+			 || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
+				if (ahc->msgout_buf[ahc->msgout_index - 1]
+				 == MSG_ABORT_TAG)
+					tag = scb->hscb->tag;
+				ahc_print_path(ahc, scb);
+				printk("SCB %d - Abort%s Completed.\n",
+				       scb->hscb->tag, tag == SCB_LIST_NULL ?
+				       "" : " Tag");
+				ahc_abort_scbs(ahc, target, channel,
+					       saved_lun, tag,
+					       ROLE_INITIATOR,
+					       CAM_REQ_ABORTED);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_1B,
+						MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+				/*
+				 * Don't mark the user's request for this BDR
+				 * as completing with CAM_BDR_SENT.  CAM3
+				 * specifies CAM_REQ_CMP.
+				 */
+				if (scb != NULL
+				 && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+				 && ahc_match_scb(ahc, scb, target, channel,
+						  CAM_LUN_WILDCARD,
+						  SCB_LIST_NULL,
+						  ROLE_INITIATOR)) {
+					ahc_set_transaction_status(scb, CAM_REQ_CMP);
+				}
+#endif
+				ahc_compile_devinfo(&devinfo,
+						    initiator_role_id,
+						    target,
+						    CAM_LUN_WILDCARD,
+						    channel,
+						    ROLE_INITIATOR);
+				ahc_handle_devreset(ahc, &devinfo,
+						    CAM_BDR_SENT,
+						    "Bus Device Reset",
+						    /*verbose_level*/0);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_PPR, FALSE)) {
+				struct ahc_initiator_tinfo *tinfo;
+				struct ahc_tmode_tstate *tstate;
+
+				/*
+				 * PPR Rejected.  Try non-ppr negotiation
+				 * and retry command.
+				 */
+				tinfo = ahc_fetch_transinfo(ahc,
+							    devinfo.channel,
+							    devinfo.our_scsiid,
+							    devinfo.target,
+							    &tstate);
+				tinfo->curr.transport_version = 2;
+				tinfo->goal.transport_version = 2;
+				tinfo->goal.ppr_options = 0;
+				ahc_qinfifo_requeue_tail(ahc, scb);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_WDTR, FALSE)) {
+				/*
+				 * Negotiation Rejected.  Go-narrow and
+				 * retry command.
+				 */
+				ahc_set_width(ahc, &devinfo,
+					      MSG_EXT_WDTR_BUS_8_BIT,
+					      AHC_TRANS_CUR|AHC_TRANS_GOAL,
+					      /*paused*/TRUE);
+				ahc_qinfifo_requeue_tail(ahc, scb);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_SDTR, FALSE)) {
+				/*
+				 * Negotiation Rejected.  Go-async and
+				 * retry command.
+				 */
+				ahc_set_syncrate(ahc, &devinfo,
+						/*syncrate*/NULL,
+						/*period*/0, /*offset*/0,
+						/*ppr_options*/0,
+						AHC_TRANS_CUR|AHC_TRANS_GOAL,
+						/*paused*/TRUE);
+				ahc_qinfifo_requeue_tail(ahc, scb);
+				printerror = 0;
+			}
+		}
+		if (printerror != 0) {
+			u_int i;
+
+			if (scb != NULL) {
+				u_int tag;
+
+				if ((scb->hscb->control & TAG_ENB) != 0)
+					tag = scb->hscb->tag;
+				else
+					tag = SCB_LIST_NULL;
+				ahc_print_path(ahc, scb);
+				ahc_abort_scbs(ahc, target, channel,
+					       SCB_GET_LUN(scb), tag,
+					       ROLE_INITIATOR,
+					       CAM_UNEXP_BUSFREE);
+			} else {
+				/*
+				 * We had not fully identified this connection,
+				 * so we cannot abort anything.
+				 */
+				printk("%s: ", ahc_name(ahc));
+			}
+			for (i = 0; i < num_phases; i++) {
+				if (lastphase == ahc_phase_table[i].phase)
+					break;
+			}
+			if (lastphase != P_BUSFREE) {
+				/*
+				 * Renegotiate with this device at the
+				 * next opportunity just in case this busfree
+				 * is due to a negotiation mismatch with the
+				 * device.
+				 */
+				ahc_force_renegotiation(ahc, &devinfo);
+			}
+			printk("Unexpected busfree %s\n"
+			       "SEQADDR == 0x%x\n",
+			       ahc_phase_table[i].phasemsg,
+			       ahc_inb(ahc, SEQADDR0)
+				| (ahc_inb(ahc, SEQADDR1) << 8));
+		}
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		ahc_restart(ahc);
+	} else {
+		printk("%s: Missing case in ahc_handle_scsiint. status = %x\n",
+		       ahc_name(ahc), status);
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+	}
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahc_force_renegotiation(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	struct	ahc_initiator_tinfo *targ_info;
+	struct	ahc_tmode_tstate *tstate;
+
+	targ_info = ahc_fetch_transinfo(ahc,
+					devinfo->channel,
+					devinfo->our_scsiid,
+					devinfo->target,
+					&tstate);
+	ahc_update_neg_request(ahc, devinfo, tstate,
+			       targ_info, AHC_NEG_IF_NON_ASYNC);
+}
+
+#define AHC_MAX_STEPS 2000
+static void
+ahc_clear_critical_section(struct ahc_softc *ahc)
+{
+	int	stepping;
+	int	steps;
+	u_int	simode0;
+	u_int	simode1;
+
+	if (ahc->num_critical_sections == 0)
+		return;
+
+	stepping = FALSE;
+	steps = 0;
+	simode0 = 0;
+	simode1 = 0;
+	for (;;) {
+		struct	cs *cs;
+		u_int	seqaddr;
+		u_int	i;
+
+		seqaddr = ahc_inb(ahc, SEQADDR0)
+			| (ahc_inb(ahc, SEQADDR1) << 8);
+
+		/*
+		 * Seqaddr represents the next instruction to execute, 
+		 * so we are really executing the instruction just
+		 * before it.
+		 */
+		if (seqaddr != 0)
+			seqaddr -= 1;
+		cs = ahc->critical_sections;
+		for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
+			
+			if (cs->begin < seqaddr && cs->end >= seqaddr)
+				break;
+		}
+
+		if (i == ahc->num_critical_sections)
+			break;
+
+		if (steps > AHC_MAX_STEPS) {
+			printk("%s: Infinite loop in critical section\n",
+			       ahc_name(ahc));
+			ahc_dump_card_state(ahc);
+			panic("critical section loop");
+		}
+
+		steps++;
+		if (stepping == FALSE) {
+
+			/*
+			 * Disable all interrupt sources so that the
+			 * sequencer will not be stuck by a pausing
+			 * interrupt condition while we attempt to
+			 * leave a critical section.
+			 */
+			simode0 = ahc_inb(ahc, SIMODE0);
+			ahc_outb(ahc, SIMODE0, 0);
+			simode1 = ahc_inb(ahc, SIMODE1);
+			if ((ahc->features & AHC_DT) != 0)
+				/*
+				 * On DT class controllers, we
+				 * use the enhanced busfree logic.
+				 * Unfortunately we cannot re-enable
+				 * busfree detection within the
+				 * current connection, so we must
+				 * leave it on while single stepping.
+				 */
+				ahc_outb(ahc, SIMODE1, simode1 & ENBUSFREE);
+			else
+				ahc_outb(ahc, SIMODE1, 0);
+			ahc_outb(ahc, CLRINT, CLRSCSIINT);
+			ahc_outb(ahc, SEQCTL, ahc->seqctl | STEP);
+			stepping = TRUE;
+		}
+		if ((ahc->features & AHC_DT) != 0) {
+			ahc_outb(ahc, CLRSINT1, CLRBUSFREE);
+			ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		}
+		ahc_outb(ahc, HCNTRL, ahc->unpause);
+		while (!ahc_is_paused(ahc))
+			ahc_delay(200);
+	}
+	if (stepping) {
+		ahc_outb(ahc, SIMODE0, simode0);
+		ahc_outb(ahc, SIMODE1, simode1);
+		ahc_outb(ahc, SEQCTL, ahc->seqctl);
+	}
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+static void
+ahc_clear_intstat(struct ahc_softc *ahc)
+{
+	/* Clear any interrupt conditions this may have caused */
+	ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+				|CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
+				CLRREQINIT);
+	ahc_flush_device_writes(ahc);
+	ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
+ 	ahc_flush_device_writes(ahc);
+	ahc_outb(ahc, CLRINT, CLRSCSIINT);
+	ahc_flush_device_writes(ahc);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHC_DEBUG
+uint32_t ahc_debug = AHC_DEBUG_OPTS;
+#endif
+
+#if 0 /* unused */
+static void
+ahc_print_scb(struct scb *scb)
+{
+	int i;
+
+	struct hardware_scb *hscb = scb->hscb;
+
+	printk("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+	       (void *)scb,
+	       hscb->control,
+	       hscb->scsiid,
+	       hscb->lun,
+	       hscb->cdb_len);
+	printk("Shared Data: ");
+	for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
+		printk("%#02x", hscb->shared_data.cdb[i]);
+	printk("        dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
+		ahc_le32toh(hscb->dataptr),
+		ahc_le32toh(hscb->datacnt),
+		ahc_le32toh(hscb->sgptr),
+		hscb->tag);
+	if (scb->sg_count > 0) {
+		for (i = 0; i < scb->sg_count; i++) {
+			printk("sg[%d] - Addr 0x%x%x : Length %d\n",
+			       i,
+			       (ahc_le32toh(scb->sg_list[i].len) >> 24
+			        & SG_HIGH_ADDR_BITS),
+			       ahc_le32toh(scb->sg_list[i].addr),
+			       ahc_le32toh(scb->sg_list[i].len));
+		}
+	}
+}
+#endif
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahc_tmode_tstate *
+ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
+{
+	struct ahc_tmode_tstate *master_tstate;
+	struct ahc_tmode_tstate *tstate;
+	int i;
+
+	master_tstate = ahc->enabled_targets[ahc->our_id];
+	if (channel == 'B') {
+		scsi_id += 8;
+		master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
+	}
+	if (ahc->enabled_targets[scsi_id] != NULL
+	 && ahc->enabled_targets[scsi_id] != master_tstate)
+		panic("%s: ahc_alloc_tstate - Target already allocated",
+		      ahc_name(ahc));
+	tstate = kmalloc(sizeof(*tstate), GFP_ATOMIC);
+	if (tstate == NULL)
+		return (NULL);
+
+	/*
+	 * If we have allocated a master tstate, copy user settings from
+	 * the master tstate (taken from SRAM or the EEPROM) for this
+	 * channel, but reset our current and goal settings to async/narrow
+	 * until an initiator talks to us.
+	 */
+	if (master_tstate != NULL) {
+		memcpy(tstate, master_tstate, sizeof(*tstate));
+		memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+		tstate->ultraenb = 0;
+		for (i = 0; i < AHC_NUM_TARGETS; i++) {
+			memset(&tstate->transinfo[i].curr, 0,
+			      sizeof(tstate->transinfo[i].curr));
+			memset(&tstate->transinfo[i].goal, 0,
+			      sizeof(tstate->transinfo[i].goal));
+		}
+	} else
+		memset(tstate, 0, sizeof(*tstate));
+	ahc->enabled_targets[scsi_id] = tstate;
+	return (tstate);
+}
+
+#ifdef AHC_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
+{
+	struct ahc_tmode_tstate *tstate;
+
+	/*
+	 * Don't clean up our "master" tstate.
+	 * It has our default user settings.
+	 */
+	if (((channel == 'B' && scsi_id == ahc->our_id_b)
+	  || (channel == 'A' && scsi_id == ahc->our_id))
+	 && force == FALSE)
+		return;
+
+	if (channel == 'B')
+		scsi_id += 8;
+	tstate = ahc->enabled_targets[scsi_id];
+	if (tstate != NULL)
+		kfree(tstate);
+	ahc->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest syncrate to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+const struct ahc_syncrate *
+ahc_devlimited_syncrate(struct ahc_softc *ahc,
+			struct ahc_initiator_tinfo *tinfo,
+			u_int *period, u_int *ppr_options, role_t role)
+{
+	struct	ahc_transinfo *transinfo;
+	u_int	maxsync;
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+		 && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+			maxsync = AHC_SYNCRATE_DT;
+		} else {
+			maxsync = AHC_SYNCRATE_ULTRA;
+			/* Can't do DT on an SE bus */
+			*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+		}
+	} else if ((ahc->features & AHC_ULTRA) != 0) {
+		maxsync = AHC_SYNCRATE_ULTRA;
+	} else {
+		maxsync = AHC_SYNCRATE_FAST;
+	}
+	/*
+	 * Never allow a value higher than our current goal
+	 * period otherwise we may allow a target initiated
+	 * negotiation to go above the limit as set by the
+	 * user.  In the case of an initiator initiated
+	 * sync negotiation, we limit based on the user
+	 * setting.  This allows the system to still accept
+	 * incoming negotiations even if target initiated
+	 * negotiation is not performed.
+	 */
+	if (role == ROLE_TARGET)
+		transinfo = &tinfo->user;
+	else 
+		transinfo = &tinfo->goal;
+	*ppr_options &= transinfo->ppr_options;
+	if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
+		maxsync = max(maxsync, (u_int)AHC_SYNCRATE_ULTRA2);
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+	}
+	if (transinfo->period == 0) {
+		*period = 0;
+		*ppr_options = 0;
+		return (NULL);
+	}
+	*period = max(*period, (u_int)transinfo->period);
+	return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+const struct ahc_syncrate *
+ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+		  u_int *ppr_options, u_int maxsync)
+{
+	const struct ahc_syncrate *syncrate;
+
+	if ((ahc->features & AHC_DT) == 0)
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+	/* Skip all DT only entries if DT is not available */
+	if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+	 && maxsync < AHC_SYNCRATE_ULTRA2)
+		maxsync = AHC_SYNCRATE_ULTRA2;
+
+	/* Now set the maxsync based on the card capabilities
+	 * DT is already done above */
+	if ((ahc->features & (AHC_DT | AHC_ULTRA2)) == 0
+	    && maxsync < AHC_SYNCRATE_ULTRA)
+		maxsync = AHC_SYNCRATE_ULTRA;
+	if ((ahc->features & (AHC_DT | AHC_ULTRA2 | AHC_ULTRA)) == 0
+	    && maxsync < AHC_SYNCRATE_FAST)
+		maxsync = AHC_SYNCRATE_FAST;
+
+	for (syncrate = &ahc_syncrates[maxsync];
+	     syncrate->rate != NULL;
+	     syncrate++) {
+
+		/*
+		 * The Ultra2 table doesn't go as low
+		 * as for the Fast/Ultra cards.
+		 */
+		if ((ahc->features & AHC_ULTRA2) != 0
+		 && (syncrate->sxfr_u2 == 0))
+			break;
+
+		if (*period <= syncrate->period) {
+			/*
+			 * When responding to a target that requests
+			 * sync, the requested rate may fall between
+			 * two rates that we can output, but still be
+			 * a rate that we can receive.  Because of this,
+			 * we want to respond to the target with
+			 * the same rate that it sent to us even
+			 * if the period we use to send data to it
+			 * is lower.  Only lower the response period
+			 * if we must.
+			 */
+			if (syncrate == &ahc_syncrates[maxsync])
+				*period = syncrate->period;
+
+			/*
+			 * At some speeds, we only support
+			 * ST transfers.
+			 */
+		 	if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
+				*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+			break;
+		}
+	}
+
+	if ((*period == 0)
+	 || (syncrate->rate == NULL)
+	 || ((ahc->features & AHC_ULTRA2) != 0
+	  && (syncrate->sxfr_u2 == 0))) {
+		/* Use asynchronous transfers. */
+		*period = 0;
+		syncrate = NULL;
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+	}
+	return (syncrate);
+}
+
+/*
+ * Convert from an entry in our syncrate table to the SCSI equivalent
+ * sync "period" factor.
+ */
+u_int
+ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+{
+	const struct ahc_syncrate *syncrate;
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		scsirate &= SXFR_ULTRA2;
+	else
+		scsirate &= SXFR;
+
+	/* now set maxsync based on card capabilities */
+	if ((ahc->features & AHC_DT) == 0 && maxsync < AHC_SYNCRATE_ULTRA2)
+		maxsync = AHC_SYNCRATE_ULTRA2;
+	if ((ahc->features & (AHC_DT | AHC_ULTRA2)) == 0
+	    && maxsync < AHC_SYNCRATE_ULTRA)
+		maxsync = AHC_SYNCRATE_ULTRA;
+	if ((ahc->features & (AHC_DT | AHC_ULTRA2 | AHC_ULTRA)) == 0
+	    && maxsync < AHC_SYNCRATE_FAST)
+		maxsync = AHC_SYNCRATE_FAST;
+
+
+	syncrate = &ahc_syncrates[maxsync];
+	while (syncrate->rate != NULL) {
+
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			if (syncrate->sxfr_u2 == 0)
+				break;
+			else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
+				return (syncrate->period);
+		} else if (scsirate == (syncrate->sxfr & SXFR)) {
+				return (syncrate->period);
+		}
+		syncrate++;
+	}
+	return (0); /* async */
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+static void
+ahc_validate_offset(struct ahc_softc *ahc,
+		    struct ahc_initiator_tinfo *tinfo,
+		    const struct ahc_syncrate *syncrate,
+		    u_int *offset, int wide, role_t role)
+{
+	u_int maxoffset;
+
+	/* Limit offset to what we can do */
+	if (syncrate == NULL) {
+		maxoffset = 0;
+	} else if ((ahc->features & AHC_ULTRA2) != 0) {
+		maxoffset = MAX_OFFSET_ULTRA2;
+	} else {
+		if (wide)
+			maxoffset = MAX_OFFSET_16BIT;
+		else
+			maxoffset = MAX_OFFSET_8BIT;
+	}
+	*offset = min(*offset, maxoffset);
+	if (tinfo != NULL) {
+		if (role == ROLE_TARGET)
+			*offset = min(*offset, (u_int)tinfo->user.offset);
+		else
+			*offset = min(*offset, (u_int)tinfo->goal.offset);
+	}
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+static void
+ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
+		   u_int *bus_width, role_t role)
+{
+	switch (*bus_width) {
+	default:
+		if (ahc->features & AHC_WIDE) {
+			/* Respond Wide */
+			*bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+			break;
+		}
+		/* FALLTHROUGH */
+	case MSG_EXT_WDTR_BUS_8_BIT:
+		*bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+		break;
+	}
+	if (tinfo != NULL) {
+		if (role == ROLE_TARGET)
+			*bus_width = min((u_int)tinfo->user.width, *bus_width);
+		else
+			*bus_width = min((u_int)tinfo->goal.width, *bus_width);
+	}
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient opportunity.  This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		       struct ahc_tmode_tstate *tstate,
+		       struct ahc_initiator_tinfo *tinfo, ahc_neg_type neg_type)
+{
+	u_int auto_negotiate_orig;
+
+	auto_negotiate_orig = tstate->auto_negotiate;
+	if (neg_type == AHC_NEG_ALWAYS) {
+		/*
+		 * Force our "current" settings to be
+		 * unknown so that unless a bus reset
+		 * occurs the need to renegotiate is
+		 * recorded persistently.
+		 */
+		if ((ahc->features & AHC_WIDE) != 0)
+			tinfo->curr.width = AHC_WIDTH_UNKNOWN;
+		tinfo->curr.period = AHC_PERIOD_UNKNOWN;
+		tinfo->curr.offset = AHC_OFFSET_UNKNOWN;
+	}
+	if (tinfo->curr.period != tinfo->goal.period
+	 || tinfo->curr.width != tinfo->goal.width
+	 || tinfo->curr.offset != tinfo->goal.offset
+	 || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+	 || (neg_type == AHC_NEG_IF_NON_ASYNC
+	  && (tinfo->goal.offset != 0
+	   || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+	   || tinfo->goal.ppr_options != 0)))
+		tstate->auto_negotiate |= devinfo->target_mask;
+	else
+		tstate->auto_negotiate &= ~devinfo->target_mask;
+
+	return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		 const struct ahc_syncrate *syncrate, u_int period,
+		 u_int offset, u_int ppr_options, u_int type, int paused)
+{
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	u_int	old_period;
+	u_int	old_offset;
+	u_int	old_ppr;
+	int	active;
+	int	update_needed;
+
+	active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+	update_needed = 0;
+
+	if (syncrate == NULL) {
+		period = 0;
+		offset = 0;
+	}
+
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	if ((type & AHC_TRANS_USER) != 0) {
+		tinfo->user.period = period;
+		tinfo->user.offset = offset;
+		tinfo->user.ppr_options = ppr_options;
+	}
+
+	if ((type & AHC_TRANS_GOAL) != 0) {
+		tinfo->goal.period = period;
+		tinfo->goal.offset = offset;
+		tinfo->goal.ppr_options = ppr_options;
+	}
+
+	old_period = tinfo->curr.period;
+	old_offset = tinfo->curr.offset;
+	old_ppr	   = tinfo->curr.ppr_options;
+
+	if ((type & AHC_TRANS_CUR) != 0
+	 && (old_period != period
+	  || old_offset != offset
+	  || old_ppr != ppr_options)) {
+		u_int	scsirate;
+
+		update_needed++;
+		scsirate = tinfo->scsirate;
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+
+			scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
+			if (syncrate != NULL) {
+				scsirate |= syncrate->sxfr_u2;
+				if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0)
+					scsirate |= ENABLE_CRC;
+				else
+					scsirate |= SINGLE_EDGE;
+			}
+		} else {
+
+			scsirate &= ~(SXFR|SOFS);
+			/*
+			 * Ensure Ultra mode is set properly for
+			 * this target.
+			 */
+			tstate->ultraenb &= ~devinfo->target_mask;
+			if (syncrate != NULL) {
+				if (syncrate->sxfr & ULTRA_SXFR) {
+					tstate->ultraenb |=
+						devinfo->target_mask;
+				}
+				scsirate |= syncrate->sxfr & SXFR;
+				scsirate |= offset & SOFS;
+			}
+			if (active) {
+				u_int sxfrctl0;
+
+				sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+				sxfrctl0 &= ~FAST20;
+				if (tstate->ultraenb & devinfo->target_mask)
+					sxfrctl0 |= FAST20;
+				ahc_outb(ahc, SXFRCTL0, sxfrctl0);
+			}
+		}
+		if (active) {
+			ahc_outb(ahc, SCSIRATE, scsirate);
+			if ((ahc->features & AHC_ULTRA2) != 0)
+				ahc_outb(ahc, SCSIOFFSET, offset);
+		}
+
+		tinfo->scsirate = scsirate;
+		tinfo->curr.period = period;
+		tinfo->curr.offset = offset;
+		tinfo->curr.ppr_options = ppr_options;
+
+		ahc_send_async(ahc, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG);
+		if (bootverbose) {
+			if (offset != 0) {
+				printk("%s: target %d synchronous at %sMHz%s, "
+				       "offset = 0x%x\n", ahc_name(ahc),
+				       devinfo->target, syncrate->rate,
+				       (ppr_options & MSG_EXT_PPR_DT_REQ)
+				       ? " DT" : "", offset);
+			} else {
+				printk("%s: target %d using "
+				       "asynchronous transfers\n",
+				       ahc_name(ahc), devinfo->target);
+			}
+		}
+	}
+
+	update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+						tinfo, AHC_NEG_TO_GOAL);
+
+	if (update_needed)
+		ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+	      u_int width, u_int type, int paused)
+{
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	u_int	oldwidth;
+	int	active;
+	int	update_needed;
+
+	active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+	update_needed = 0;
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	if ((type & AHC_TRANS_USER) != 0)
+		tinfo->user.width = width;
+
+	if ((type & AHC_TRANS_GOAL) != 0)
+		tinfo->goal.width = width;
+
+	oldwidth = tinfo->curr.width;
+	if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
+		u_int	scsirate;
+
+		update_needed++;
+		scsirate =  tinfo->scsirate;
+		scsirate &= ~WIDEXFER;
+		if (width == MSG_EXT_WDTR_BUS_16_BIT)
+			scsirate |= WIDEXFER;
+
+		tinfo->scsirate = scsirate;
+
+		if (active)
+			ahc_outb(ahc, SCSIRATE, scsirate);
+
+		tinfo->curr.width = width;
+
+		ahc_send_async(ahc, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG);
+		if (bootverbose) {
+			printk("%s: target %d using %dbit transfers\n",
+			       ahc_name(ahc), devinfo->target,
+			       8 * (0x01 << width));
+		}
+	}
+
+	update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+						tinfo, AHC_NEG_TO_GOAL);
+	if (update_needed)
+		ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+static void
+ahc_set_tags(struct ahc_softc *ahc, struct scsi_cmnd *cmd,
+	     struct ahc_devinfo *devinfo, ahc_queue_alg alg)
+{
+	struct scsi_device *sdev = cmd->device;
+
+ 	ahc_platform_set_tags(ahc, sdev, devinfo, alg);
+ 	ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ 		       devinfo->lun, AC_TRANSFER_NEG);
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahc_update_pending_scbs(struct ahc_softc *ahc)
+{
+	struct	scb *pending_scb;
+	int	pending_scb_count;
+	int	i;
+	int	paused;
+	u_int	saved_scbptr;
+
+	/*
+	 * Traverse the pending SCB list and ensure that all of the
+	 * SCBs there have the proper settings.
+	 */
+	pending_scb_count = 0;
+	LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+		struct ahc_devinfo devinfo;
+		struct hardware_scb *pending_hscb;
+		struct ahc_initiator_tinfo *tinfo;
+		struct ahc_tmode_tstate *tstate;
+
+		ahc_scb_devinfo(ahc, &devinfo, pending_scb);
+		tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+					    devinfo.our_scsiid,
+					    devinfo.target, &tstate);
+		pending_hscb = pending_scb->hscb;
+		pending_hscb->control &= ~ULTRAENB;
+		if ((tstate->ultraenb & devinfo.target_mask) != 0)
+			pending_hscb->control |= ULTRAENB;
+		pending_hscb->scsirate = tinfo->scsirate;
+		pending_hscb->scsioffset = tinfo->curr.offset;
+		if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+		 && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+			pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+			pending_hscb->control &= ~MK_MESSAGE;
+		}
+		ahc_sync_scb(ahc, pending_scb,
+			     BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+		pending_scb_count++;
+	}
+
+	if (pending_scb_count == 0)
+		return;
+
+	if (ahc_is_paused(ahc)) {
+		paused = 1;
+	} else {
+		paused = 0;
+		ahc_pause(ahc);
+	}
+
+	saved_scbptr = ahc_inb(ahc, SCBPTR);
+	/* Ensure that the hscbs down on the card match the new information */
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		struct	hardware_scb *pending_hscb;
+		u_int	control;
+		u_int	scb_tag;
+
+		ahc_outb(ahc, SCBPTR, i);
+		scb_tag = ahc_inb(ahc, SCB_TAG);
+		pending_scb = ahc_lookup_scb(ahc, scb_tag);
+		if (pending_scb == NULL)
+			continue;
+
+		pending_hscb = pending_scb->hscb;
+		control = ahc_inb(ahc, SCB_CONTROL);
+		control &= ~(ULTRAENB|MK_MESSAGE);
+		control |= pending_hscb->control & (ULTRAENB|MK_MESSAGE);
+		ahc_outb(ahc, SCB_CONTROL, control);
+		ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
+		ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
+	}
+	ahc_outb(ahc, SCBPTR, saved_scbptr);
+
+	if (paused == 0)
+		ahc_unpause(ahc);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	u_int	saved_scsiid;
+	role_t	role;
+	int	our_id;
+
+	if (ahc_inb(ahc, SSTAT0) & TARGET)
+		role = ROLE_TARGET;
+	else
+		role = ROLE_INITIATOR;
+
+	if (role == ROLE_TARGET
+	 && (ahc->features & AHC_MULTI_TID) != 0
+	 && (ahc_inb(ahc, SEQ_FLAGS)
+ 	   & (CMDPHASE_PENDING|TARG_CMD_PENDING|NO_DISCONNECT)) != 0) {
+		/* We were selected, so pull our id from TARGIDIN */
+		our_id = ahc_inb(ahc, TARGIDIN) & OID;
+	} else if ((ahc->features & AHC_ULTRA2) != 0)
+		our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+	else
+		our_id = ahc_inb(ahc, SCSIID) & OID;
+
+	saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+	ahc_compile_devinfo(devinfo,
+			    our_id,
+			    SCSIID_TARGET(ahc, saved_scsiid),
+			    ahc_inb(ahc, SAVED_LUN),
+			    SCSIID_CHANNEL(ahc, saved_scsiid),
+			    role);
+}
+
+static const struct ahc_phase_table_entry*
+ahc_lookup_phase_entry(int phase)
+{
+	const struct ahc_phase_table_entry *entry;
+	const struct ahc_phase_table_entry *last_entry;
+
+	/*
+	 * num_phases doesn't include the default entry which
+	 * will be returned if the phase doesn't match.
+	 */
+	last_entry = &ahc_phase_table[num_phases];
+	for (entry = ahc_phase_table; entry < last_entry; entry++) {
+		if (phase == entry->phase)
+			break;
+	}
+	return (entry);
+}
+
+void
+ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
+		    u_int lun, char channel, role_t role)
+{
+	devinfo->our_scsiid = our_id;
+	devinfo->target = target;
+	devinfo->lun = lun;
+	devinfo->target_offset = target;
+	devinfo->channel = channel;
+	devinfo->role = role;
+	if (channel == 'B')
+		devinfo->target_offset += 8;
+	devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+void
+ahc_print_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	printk("%s:%c:%d:%d: ", ahc_name(ahc), devinfo->channel,
+	       devinfo->target, devinfo->lun);
+}
+
+static void
+ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		struct scb *scb)
+{
+	role_t	role;
+	int	our_id;
+
+	our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+	role = ROLE_INITIATOR;
+	if ((scb->flags & SCB_TARGET_SCB) != 0)
+		role = ROLE_TARGET;
+	ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
+			    SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+static void
+ahc_assert_atn(struct ahc_softc *ahc)
+{
+	u_int scsisigo;
+
+	scsisigo = ATNO;
+	if ((ahc->features & AHC_DT) == 0)
+		scsisigo |= ahc_inb(ahc, SCSISIGI);
+	ahc_outb(ahc, SCSISIGO, scsisigo);
+}
+
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted.  Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+			   struct scb *scb)
+{
+	/*
+	 * To facilitate adding multiple messages together,
+	 * each routine should increment the index and len
+	 * variables instead of setting them explicitly.
+	 */
+	ahc->msgout_index = 0;
+	ahc->msgout_len = 0;
+
+	if ((scb->flags & SCB_DEVICE_RESET) == 0
+	 && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
+		u_int identify_msg;
+
+		identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+		if ((scb->hscb->control & DISCENB) != 0)
+			identify_msg |= MSG_IDENTIFY_DISCFLAG;
+		ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
+		ahc->msgout_len++;
+
+		if ((scb->hscb->control & TAG_ENB) != 0) {
+			ahc->msgout_buf[ahc->msgout_index++] =
+			    scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+			ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
+			ahc->msgout_len += 2;
+		}
+	}
+
+	if (scb->flags & SCB_DEVICE_RESET) {
+		ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
+		ahc->msgout_len++;
+		ahc_print_path(ahc, scb);
+		printk("Bus Device Reset Message Sent\n");
+		/*
+		 * Clear our selection hardware in advance of
+		 * the busfree.  We may have an entry in the waiting
+		 * Q for this target, and we don't want to go about
+		 * selecting while we handle the busfree and blow it
+		 * away.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else if ((scb->flags & SCB_ABORT) != 0) {
+		if ((scb->hscb->control & TAG_ENB) != 0)
+			ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
+		else
+			ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
+		ahc->msgout_len++;
+		ahc_print_path(ahc, scb);
+		printk("Abort%s Message Sent\n",
+		       (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+		/*
+		 * Clear our selection hardware in advance of
+		 * the busfree.  We may have an entry in the waiting
+		 * Q for this target, and we don't want to go about
+		 * selecting while we handle the busfree and blow it
+		 * away.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+		ahc_build_transfer_msg(ahc, devinfo);
+	} else {
+		printk("ahc_intr: AWAITING_MSG for an SCB that "
+		       "does not have a waiting message\n");
+		printk("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+		       devinfo->target_mask);
+		panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+		      "SCB flags = %x", scb->hscb->tag, scb->hscb->control,
+		      ahc_inb(ahc, MSG_OUT), scb->flags);
+	}
+
+	/*
+	 * Clear the MK_MESSAGE flag from the SCB so we aren't
+	 * asked to send this message again.
+	 */
+	ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
+	scb->hscb->control &= ~MK_MESSAGE;
+	ahc->msgout_index = 0;
+	ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	/*
+	 * We need to initiate transfer negotiations.
+	 * If our current and goal settings are identical,
+	 * we want to renegotiate due to a check condition.
+	 */
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	const struct ahc_syncrate *rate;
+	int	dowide;
+	int	dosync;
+	int	doppr;
+	u_int	period;
+	u_int	ppr_options;
+	u_int	offset;
+
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	/*
+	 * Filter our period based on the current connection.
+	 * If we can't perform DT transfers on this segment (not in LVD
+	 * mode for instance), then our decision to issue a PPR message
+	 * may change.
+	 */
+	period = tinfo->goal.period;
+	offset = tinfo->goal.offset;
+	ppr_options = tinfo->goal.ppr_options;
+	/* Target initiated PPR is not allowed in the SCSI spec */
+	if (devinfo->role == ROLE_TARGET)
+		ppr_options = 0;
+	rate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+				       &ppr_options, devinfo->role);
+	dowide = tinfo->curr.width != tinfo->goal.width;
+	dosync = tinfo->curr.offset != offset || tinfo->curr.period != period;
+	/*
+	 * Only use PPR if we have options that need it, even if the device
+	 * claims to support it.  There might be an expander in the way
+	 * that doesn't.
+	 */
+	doppr = ppr_options != 0;
+
+	if (!dowide && !dosync && !doppr) {
+		dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+		dosync = tinfo->goal.offset != 0;
+	}
+
+	if (!dowide && !dosync && !doppr) {
+		/*
+		 * Force async with a WDTR message if we have a wide bus,
+		 * or just issue an SDTR with a 0 offset.
+		 */
+		if ((ahc->features & AHC_WIDE) != 0)
+			dowide = 1;
+		else
+			dosync = 1;
+
+		if (bootverbose) {
+			ahc_print_devinfo(ahc, devinfo);
+			printk("Ensuring async\n");
+		}
+	}
+
+	/* Target initiated PPR is not allowed in the SCSI spec */
+	if (devinfo->role == ROLE_TARGET)
+		doppr = 0;
+
+	/*
+	 * Both the PPR message and SDTR message require the
+	 * goal syncrate to be limited to what the target device
+	 * is capable of handling (based on whether an LVD->SE
+	 * expander is on the bus), so combine these two cases.
+	 * Regardless, guarantee that if we are using WDTR and SDTR
+	 * messages that WDTR comes first.
+	 */
+	if (doppr || (dosync && !dowide)) {
+
+		offset = tinfo->goal.offset;
+		ahc_validate_offset(ahc, tinfo, rate, &offset,
+				    doppr ? tinfo->goal.width
+					  : tinfo->curr.width,
+				    devinfo->role);
+		if (doppr) {
+			ahc_construct_ppr(ahc, devinfo, period, offset,
+					  tinfo->goal.width, ppr_options);
+		} else {
+			ahc_construct_sdtr(ahc, devinfo, period, offset);
+		}
+	} else {
+		ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width);
+	}
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		   u_int period, u_int offset)
+{
+	if (offset == 0)
+		period = AHC_ASYNC_XFER_PERIOD;
+	ahc->msgout_index += spi_populate_sync_msg(
+			ahc->msgout_buf + ahc->msgout_index, period, offset);
+	ahc->msgout_len += 5;
+	if (bootverbose) {
+		printk("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       devinfo->lun, period, offset);
+	}
+}
+
+/*
+ * Build a wide negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		   u_int bus_width)
+{
+	ahc->msgout_index += spi_populate_width_msg(
+			ahc->msgout_buf + ahc->msgout_index, bus_width);
+	ahc->msgout_len += 4;
+	if (bootverbose) {
+		printk("(%s:%c:%d:%d): Sending WDTR %x\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       devinfo->lun, bus_width);
+	}
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		  u_int period, u_int offset, u_int bus_width,
+		  u_int ppr_options)
+{
+	if (offset == 0)
+		period = AHC_ASYNC_XFER_PERIOD;
+	ahc->msgout_index += spi_populate_ppr_msg(
+			ahc->msgout_buf + ahc->msgout_index, period, offset,
+			bus_width, ppr_options);
+	ahc->msgout_len += 8;
+	if (bootverbose) {
+		printk("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+		       "offset %x, ppr_options %x\n", ahc_name(ahc),
+		       devinfo->channel, devinfo->target, devinfo->lun,
+		       bus_width, period, offset, ppr_options);
+	}
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahc_clear_msg_state(struct ahc_softc *ahc)
+{
+	ahc->msgout_len = 0;
+	ahc->msgin_index = 0;
+	ahc->msg_type = MSG_TYPE_NONE;
+	if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0) {
+		/*
+		 * The target didn't care to respond to our
+		 * message request, so clear ATN.
+		 */
+		ahc_outb(ahc, CLRSINT1, CLRATNO);
+	}
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+	ahc_outb(ahc, SEQ_FLAGS2,
+		 ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+}
+
+static void
+ahc_handle_proto_violation(struct ahc_softc *ahc)
+{
+	struct	ahc_devinfo devinfo;
+	struct	scb *scb;
+	u_int	scbid;
+	u_int	seq_flags;
+	u_int	curphase;
+	u_int	lastphase;
+	int	found;
+
+	ahc_fetch_devinfo(ahc, &devinfo);
+	scbid = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scbid);
+	seq_flags = ahc_inb(ahc, SEQ_FLAGS);
+	curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+	lastphase = ahc_inb(ahc, LASTPHASE);
+	if ((seq_flags & NOT_IDENTIFIED) != 0) {
+
+		/*
+		 * The reconnecting target either did not send an
+		 * identify message, or did, but we didn't find an SCB
+		 * to match.
+		 */
+		ahc_print_devinfo(ahc, &devinfo);
+		printk("Target did not send an IDENTIFY message. "
+		       "LASTPHASE = 0x%x.\n", lastphase);
+		scb = NULL;
+	} else if (scb == NULL) {
+		/*
+		 * We don't seem to have an SCB active for this
+		 * transaction.  Print an error and reset the bus.
+		 */
+		ahc_print_devinfo(ahc, &devinfo);
+		printk("No SCB found during protocol violation\n");
+		goto proto_violation_reset;
+	} else {
+		ahc_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
+		if ((seq_flags & NO_CDB_SENT) != 0) {
+			ahc_print_path(ahc, scb);
+			printk("No or incomplete CDB sent to device.\n");
+		} else if ((ahc_inb(ahc, SCB_CONTROL) & STATUS_RCVD) == 0) {
+			/*
+			 * The target never bothered to provide status to
+			 * us prior to completing the command.  Since we don't
+			 * know the disposition of this command, we must attempt
+			 * to abort it.  Assert ATN and prepare to send an abort
+			 * message.
+			 */
+			ahc_print_path(ahc, scb);
+			printk("Completed command without status.\n");
+		} else {
+			ahc_print_path(ahc, scb);
+			printk("Unknown protocol violation.\n");
+			ahc_dump_card_state(ahc);
+		}
+	}
+	if ((lastphase & ~P_DATAIN_DT) == 0
+	 || lastphase == P_COMMAND) {
+proto_violation_reset:
+		/*
+		 * Target either went directly to data/command
+		 * phase or didn't respond to our ATN.
+		 * The only safe thing to do is to blow
+		 * it away with a bus reset.
+		 */
+		found = ahc_reset_channel(ahc, 'A', TRUE);
+		printk("%s: Issued Channel %c Bus Reset. "
+		       "%d SCBs aborted\n", ahc_name(ahc), 'A', found);
+	} else {
+		/*
+		 * Leave the selection hardware off in case
+		 * this abort attempt will affect yet to
+		 * be sent commands.
+		 */
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+		ahc_assert_atn(ahc);
+		ahc_outb(ahc, MSG_OUT, HOST_MSG);
+		if (scb == NULL) {
+			ahc_print_devinfo(ahc, &devinfo);
+			ahc->msgout_buf[0] = MSG_ABORT_TASK;
+			ahc->msgout_len = 1;
+			ahc->msgout_index = 0;
+			ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+		} else {
+			ahc_print_path(ahc, scb);
+			scb->flags |= SCB_ABORT;
+		}
+		printk("Protocol violation %s.  Attempting to abort.\n",
+		       ahc_lookup_phase_entry(curphase)->phasemsg);
+	}
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahc_handle_message_phase(struct ahc_softc *ahc)
+{
+	struct	ahc_devinfo devinfo;
+	u_int	bus_phase;
+	int	end_session;
+
+	ahc_fetch_devinfo(ahc, &devinfo);
+	end_session = FALSE;
+	bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+
+reswitch:
+	switch (ahc->msg_type) {
+	case MSG_TYPE_INITIATOR_MSGOUT:
+	{
+		int lastbyte;
+		int phasemis;
+		int msgdone;
+
+		if (ahc->msgout_len == 0)
+			panic("HOST_MSG_LOOP interrupt with no active message");
+
+#ifdef AHC_DEBUG
+		if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+			ahc_print_devinfo(ahc, &devinfo);
+			printk("INITIATOR_MSG_OUT");
+		}
+#endif
+		phasemis = bus_phase != P_MESGOUT;
+		if (phasemis) {
+#ifdef AHC_DEBUG
+			if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+				printk(" PHASEMIS %s\n",
+				       ahc_lookup_phase_entry(bus_phase)
+							     ->phasemsg);
+			}
+#endif
+			if (bus_phase == P_MESGIN) {
+				/*
+				 * Change gears and see if
+				 * this messages is of interest to
+				 * us or should be passed back to
+				 * the sequencer.
+				 */
+				ahc_outb(ahc, CLRSINT1, CLRATNO);
+				ahc->send_msg_perror = FALSE;
+				ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+				ahc->msgin_index = 0;
+				goto reswitch;
+			}
+			end_session = TRUE;
+			break;
+		}
+
+		if (ahc->send_msg_perror) {
+			ahc_outb(ahc, CLRSINT1, CLRATNO);
+			ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+#ifdef AHC_DEBUG
+			if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+				printk(" byte 0x%x\n", ahc->send_msg_perror);
+#endif
+			ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
+			break;
+		}
+
+		msgdone	= ahc->msgout_index == ahc->msgout_len;
+		if (msgdone) {
+			/*
+			 * The target has requested a retry.
+			 * Re-assert ATN, reset our message index to
+			 * 0, and try again.
+			 */
+			ahc->msgout_index = 0;
+			ahc_assert_atn(ahc);
+		}
+
+		lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
+		if (lastbyte) {
+			/* Last byte is signified by dropping ATN */
+			ahc_outb(ahc, CLRSINT1, CLRATNO);
+		}
+
+		/*
+		 * Clear our interrupt status and present
+		 * the next byte on the bus.
+		 */
+		ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+#ifdef AHC_DEBUG
+		if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+			printk(" byte 0x%x\n",
+			       ahc->msgout_buf[ahc->msgout_index]);
+#endif
+		ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+		break;
+	}
+	case MSG_TYPE_INITIATOR_MSGIN:
+	{
+		int phasemis;
+		int message_done;
+
+#ifdef AHC_DEBUG
+		if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+			ahc_print_devinfo(ahc, &devinfo);
+			printk("INITIATOR_MSG_IN");
+		}
+#endif
+		phasemis = bus_phase != P_MESGIN;
+		if (phasemis) {
+#ifdef AHC_DEBUG
+			if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+				printk(" PHASEMIS %s\n",
+				       ahc_lookup_phase_entry(bus_phase)
+							     ->phasemsg);
+			}
+#endif
+			ahc->msgin_index = 0;
+			if (bus_phase == P_MESGOUT
+			 && (ahc->send_msg_perror == TRUE
+			  || (ahc->msgout_len != 0
+			   && ahc->msgout_index == 0))) {
+				ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+				goto reswitch;
+			}
+			end_session = TRUE;
+			break;
+		}
+
+		/* Pull the byte in without acking it */
+		ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+#ifdef AHC_DEBUG
+		if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+			printk(" byte 0x%x\n",
+			       ahc->msgin_buf[ahc->msgin_index]);
+#endif
+
+		message_done = ahc_parse_msg(ahc, &devinfo);
+
+		if (message_done) {
+			/*
+			 * Clear our incoming message buffer in case there
+			 * is another message following this one.
+			 */
+			ahc->msgin_index = 0;
+
+			/*
+			 * If this message illicited a response,
+			 * assert ATN so the target takes us to the
+			 * message out phase.
+			 */
+			if (ahc->msgout_len != 0) {
+#ifdef AHC_DEBUG
+				if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+					ahc_print_devinfo(ahc, &devinfo);
+					printk("Asserting ATN for response\n");
+				}
+#endif
+				ahc_assert_atn(ahc);
+			}
+		} else 
+			ahc->msgin_index++;
+
+		if (message_done == MSGLOOP_TERMINATED) {
+			end_session = TRUE;
+		} else {
+			/* Ack the byte */
+			ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+			ahc_inb(ahc, SCSIDATL);
+		}
+		break;
+	}
+	case MSG_TYPE_TARGET_MSGIN:
+	{
+		int msgdone;
+		int msgout_request;
+
+		if (ahc->msgout_len == 0)
+			panic("Target MSGIN with no active message");
+
+		/*
+		 * If we interrupted a mesgout session, the initiator
+		 * will not know this until our first REQ.  So, we
+		 * only honor mesgout requests after we've sent our
+		 * first byte.
+		 */
+		if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0
+		 && ahc->msgout_index > 0)
+			msgout_request = TRUE;
+		else
+			msgout_request = FALSE;
+
+		if (msgout_request) {
+
+			/*
+			 * Change gears and see if
+			 * this messages is of interest to
+			 * us or should be passed back to
+			 * the sequencer.
+			 */
+			ahc->msg_type = MSG_TYPE_TARGET_MSGOUT;
+			ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO);
+			ahc->msgin_index = 0;
+			/* Dummy read to REQ for first byte */
+			ahc_inb(ahc, SCSIDATL);
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+			break;
+		}
+
+		msgdone = ahc->msgout_index == ahc->msgout_len;
+		if (msgdone) {
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+			end_session = TRUE;
+			break;
+		}
+
+		/*
+		 * Present the next byte on the bus.
+		 */
+		ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+		ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+		break;
+	}
+	case MSG_TYPE_TARGET_MSGOUT:
+	{
+		int lastbyte;
+		int msgdone;
+
+		/*
+		 * The initiator signals that this is
+		 * the last byte by dropping ATN.
+		 */
+		lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0;
+
+		/*
+		 * Read the latched byte, but turn off SPIOEN first
+		 * so that we don't inadvertently cause a REQ for the
+		 * next byte.
+		 */
+		ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+		ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL);
+		msgdone = ahc_parse_msg(ahc, &devinfo);
+		if (msgdone == MSGLOOP_TERMINATED) {
+			/*
+			 * The message is *really* done in that it caused
+			 * us to go to bus free.  The sequencer has already
+			 * been reset at this point, so pull the ejection
+			 * handle.
+			 */
+			return;
+		}
+		
+		ahc->msgin_index++;
+
+		/*
+		 * XXX Read spec about initiator dropping ATN too soon
+		 *     and use msgdone to detect it.
+		 */
+		if (msgdone == MSGLOOP_MSGCOMPLETE) {
+			ahc->msgin_index = 0;
+
+			/*
+			 * If this message illicited a response, transition
+			 * to the Message in phase and send it.
+			 */
+			if (ahc->msgout_len != 0) {
+				ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO);
+				ahc_outb(ahc, SXFRCTL0,
+					 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+				ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+				ahc->msgin_index = 0;
+				break;
+			}
+		}
+
+		if (lastbyte)
+			end_session = TRUE;
+		else {
+			/* Ask for the next byte. */
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+		}
+
+		break;
+	}
+	default:
+		panic("Unknown REQINIT message type");
+	}
+
+	if (end_session) {
+		ahc_clear_msg_state(ahc);
+		ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP);
+	} else
+		ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message.  If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
+{
+	int found;
+	u_int index;
+
+	found = FALSE;
+	index = 0;
+
+	while (index < ahc->msgout_len) {
+		if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+			u_int end_index;
+
+			end_index = index + 1 + ahc->msgout_buf[index + 1];
+			if (ahc->msgout_buf[index+2] == msgval
+			 && type == AHCMSG_EXT) {
+
+				if (full) {
+					if (ahc->msgout_index > end_index)
+						found = TRUE;
+				} else if (ahc->msgout_index > index)
+					found = TRUE;
+			}
+			index = end_index;
+		} else if (ahc->msgout_buf[index] >= MSG_SIMPLE_TASK
+			&& ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+			/* Skip tag type and tag id or residue param*/
+			index += 2;
+		} else {
+			/* Single byte message */
+			if (type == AHCMSG_1B
+			 && ahc->msgout_buf[index] == msgval
+			 && ahc->msgout_index > index)
+				found = TRUE;
+			index++;
+		}
+
+		if (found)
+			break;
+	}
+	return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	int	reject;
+	int	done;
+	int	response;
+	u_int	targ_scsirate;
+
+	done = MSGLOOP_IN_PROG;
+	response = FALSE;
+	reject = FALSE;
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	targ_scsirate = tinfo->scsirate;
+
+	/*
+	 * Parse as much of the message as is available,
+	 * rejecting it if we don't support it.  When
+	 * the entire message is available and has been
+	 * handled, return MSGLOOP_MSGCOMPLETE, indicating
+	 * that we have parsed an entire message.
+	 *
+	 * In the case of extended messages, we accept the length
+	 * byte outright and perform more checking once we know the
+	 * extended message type.
+	 */
+	switch (ahc->msgin_buf[0]) {
+	case MSG_DISCONNECT:
+	case MSG_SAVEDATAPOINTER:
+	case MSG_CMDCOMPLETE:
+	case MSG_RESTOREPOINTERS:
+	case MSG_IGN_WIDE_RESIDUE:
+		/*
+		 * End our message loop as these are messages
+		 * the sequencer handles on its own.
+		 */
+		done = MSGLOOP_TERMINATED;
+		break;
+	case MSG_MESSAGE_REJECT:
+		response = ahc_handle_msg_reject(ahc, devinfo);
+		/* FALLTHROUGH */
+	case MSG_NOOP:
+		done = MSGLOOP_MSGCOMPLETE;
+		break;
+	case MSG_EXTENDED:
+	{
+		/* Wait for enough of the message to begin validation */
+		if (ahc->msgin_index < 2)
+			break;
+		switch (ahc->msgin_buf[2]) {
+		case MSG_EXT_SDTR:
+		{
+			const struct ahc_syncrate *syncrate;
+			u_int	 period;
+			u_int	 ppr_options;
+			u_int	 offset;
+			u_int	 saved_offset;
+			
+			if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have both args before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_SDTR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+				break;
+
+			period = ahc->msgin_buf[3];
+			ppr_options = 0;
+			saved_offset = offset = ahc->msgin_buf[4];
+			syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+							   &ppr_options,
+							   devinfo->role);
+			ahc_validate_offset(ahc, tinfo, syncrate, &offset,
+					    targ_scsirate & WIDEXFER,
+					    devinfo->role);
+			if (bootverbose) {
+				printk("(%s:%c:%d:%d): Received "
+				       "SDTR period %x, offset %x\n\t"
+				       "Filtered to period %x, offset %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       ahc->msgin_buf[3], saved_offset,
+				       period, offset);
+			}
+			ahc_set_syncrate(ahc, devinfo, 
+					 syncrate, period,
+					 offset, ppr_options,
+					 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+					 /*paused*/TRUE);
+
+			/*
+			 * See if we initiated Sync Negotiation
+			 * and didn't have to fall down to async
+			 * transfers.
+			 */
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+				/* We started it */
+				if (saved_offset != offset) {
+					/* Went too low - force async */
+					reject = TRUE;
+				}
+			} else {
+				/*
+				 * Send our own SDTR in reply
+				 */
+				if (bootverbose
+				 && devinfo->role == ROLE_INITIATOR) {
+					printk("(%s:%c:%d:%d): Target "
+					       "Initiated SDTR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				}
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_sdtr(ahc, devinfo,
+						   period, offset);
+				ahc->msgout_index = 0;
+				response = TRUE;
+			}
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		case MSG_EXT_WDTR:
+		{
+			u_int bus_width;
+			u_int saved_width;
+			u_int sending_reply;
+
+			sending_reply = FALSE;
+			if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have our arg before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_WDTR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+				break;
+
+			bus_width = ahc->msgin_buf[3];
+			saved_width = bus_width;
+			ahc_validate_width(ahc, tinfo, &bus_width,
+					   devinfo->role);
+			if (bootverbose) {
+				printk("(%s:%c:%d:%d): Received WDTR "
+				       "%x filtered to %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       saved_width, bus_width);
+			}
+
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+				/*
+				 * Don't send a WDTR back to the
+				 * target, since we asked first.
+				 * If the width went higher than our
+				 * request, reject it.
+				 */
+				if (saved_width > bus_width) {
+					reject = TRUE;
+					printk("(%s:%c:%d:%d): requested %dBit "
+					       "transfers.  Rejecting...\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun,
+					       8 * (0x01 << bus_width));
+					bus_width = 0;
+				}
+			} else {
+				/*
+				 * Send our own WDTR in reply
+				 */
+				if (bootverbose
+				 && devinfo->role == ROLE_INITIATOR) {
+					printk("(%s:%c:%d:%d): Target "
+					       "Initiated WDTR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				}
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_wdtr(ahc, devinfo, bus_width);
+				ahc->msgout_index = 0;
+				response = TRUE;
+				sending_reply = TRUE;
+			}
+			/*
+			 * After a wide message, we are async, but
+			 * some devices don't seem to honor this portion
+			 * of the spec.  Force a renegotiation of the
+			 * sync component of our transfer agreement even
+			 * if our goal is async.  By updating our width
+			 * after forcing the negotiation, we avoid
+			 * renegotiating for width.
+			 */
+			ahc_update_neg_request(ahc, devinfo, tstate,
+					       tinfo, AHC_NEG_ALWAYS);
+			ahc_set_width(ahc, devinfo, bus_width,
+				      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				      /*paused*/TRUE);
+			if (sending_reply == FALSE && reject == FALSE) {
+
+				/*
+				 * We will always have an SDTR to send.
+				 */
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_build_transfer_msg(ahc, devinfo);
+				ahc->msgout_index = 0;
+				response = TRUE;
+			}
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		case MSG_EXT_PPR:
+		{
+			const struct ahc_syncrate *syncrate;
+			u_int	period;
+			u_int	offset;
+			u_int	bus_width;
+			u_int	ppr_options;
+			u_int	saved_width;
+			u_int	saved_offset;
+			u_int	saved_ppr_options;
+
+			if (ahc->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have all args before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_PPR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_PPR_LEN + 1))
+				break;
+
+			period = ahc->msgin_buf[3];
+			offset = ahc->msgin_buf[5];
+			bus_width = ahc->msgin_buf[6];
+			saved_width = bus_width;
+			ppr_options = ahc->msgin_buf[7];
+			/*
+			 * According to the spec, a DT only
+			 * period factor with no DT option
+			 * set implies async.
+			 */
+			if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+			 && period == 9)
+				offset = 0;
+			saved_ppr_options = ppr_options;
+			saved_offset = offset;
+
+			/*
+			 * Mask out any options we don't support
+			 * on any controller.  Transfer options are
+			 * only available if we are negotiating wide.
+			 */
+			ppr_options &= MSG_EXT_PPR_DT_REQ;
+			if (bus_width == 0)
+				ppr_options = 0;
+
+			ahc_validate_width(ahc, tinfo, &bus_width,
+					   devinfo->role);
+			syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+							   &ppr_options,
+							   devinfo->role);
+			ahc_validate_offset(ahc, tinfo, syncrate,
+					    &offset, bus_width,
+					    devinfo->role);
+
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
+				/*
+				 * If we are unable to do any of the
+				 * requested options (we went too low),
+				 * then we'll have to reject the message.
+				 */
+				if (saved_width > bus_width
+				 || saved_offset != offset
+				 || saved_ppr_options != ppr_options) {
+					reject = TRUE;
+					period = 0;
+					offset = 0;
+					bus_width = 0;
+					ppr_options = 0;
+					syncrate = NULL;
+				}
+			} else {
+				if (devinfo->role != ROLE_TARGET)
+					printk("(%s:%c:%d:%d): Target "
+					       "Initiated PPR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				else
+					printk("(%s:%c:%d:%d): Initiator "
+					       "Initiated PPR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_ppr(ahc, devinfo, period, offset,
+						  bus_width, ppr_options);
+				ahc->msgout_index = 0;
+				response = TRUE;
+			}
+			if (bootverbose) {
+				printk("(%s:%c:%d:%d): Received PPR width %x, "
+				       "period %x, offset %x,options %x\n"
+				       "\tFiltered to width %x, period %x, "
+				       "offset %x, options %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       saved_width, ahc->msgin_buf[3],
+				       saved_offset, saved_ppr_options,
+				       bus_width, period, offset, ppr_options);
+			}
+			ahc_set_width(ahc, devinfo, bus_width,
+				      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				      /*paused*/TRUE);
+			ahc_set_syncrate(ahc, devinfo,
+					 syncrate, period,
+					 offset, ppr_options,
+					 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+					 /*paused*/TRUE);
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		default:
+			/* Unknown extended message.  Reject it. */
+			reject = TRUE;
+			break;
+		}
+		break;
+	}
+#ifdef AHC_TARGET_MODE
+	case MSG_BUS_DEV_RESET:
+		ahc_handle_devreset(ahc, devinfo,
+				    CAM_BDR_SENT,
+				    "Bus Device Reset Received",
+				    /*verbose_level*/0);
+		ahc_restart(ahc);
+		done = MSGLOOP_TERMINATED;
+		break;
+	case MSG_ABORT_TAG:
+	case MSG_ABORT:
+	case MSG_CLEAR_QUEUE:
+	{
+		int tag;
+
+		/* Target mode messages */
+		if (devinfo->role != ROLE_TARGET) {
+			reject = TRUE;
+			break;
+		}
+		tag = SCB_LIST_NULL;
+		if (ahc->msgin_buf[0] == MSG_ABORT_TAG)
+			tag = ahc_inb(ahc, INITIATOR_TAG);
+		ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+			       devinfo->lun, tag, ROLE_TARGET,
+			       CAM_REQ_ABORTED);
+
+		tstate = ahc->enabled_targets[devinfo->our_scsiid];
+		if (tstate != NULL) {
+			struct ahc_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[devinfo->lun];
+			if (lstate != NULL) {
+				ahc_queue_lstate_event(ahc, lstate,
+						       devinfo->our_scsiid,
+						       ahc->msgin_buf[0],
+						       /*arg*/tag);
+				ahc_send_lstate_events(ahc, lstate);
+			}
+		}
+		ahc_restart(ahc);
+		done = MSGLOOP_TERMINATED;
+		break;
+	}
+#endif
+	case MSG_TERM_IO_PROC:
+	default:
+		reject = TRUE;
+		break;
+	}
+
+	if (reject) {
+		/*
+		 * Setup to reject the message.
+		 */
+		ahc->msgout_index = 0;
+		ahc->msgout_len = 1;
+		ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
+		done = MSGLOOP_MSGCOMPLETE;
+		response = TRUE;
+	}
+
+	if (done != MSGLOOP_IN_PROG && !response)
+		/* Clear the outgoing message buffer */
+		ahc->msgout_len = 0;
+
+	return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	/*
+	 * 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.
+	 */
+	struct scb *scb;
+	struct ahc_initiator_tinfo *tinfo;
+	struct ahc_tmode_tstate *tstate;
+	u_int scb_index;
+	u_int last_msg;
+	int   response = 0;
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+				    devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	/* Might be necessary */
+	last_msg = ahc_inb(ahc, LAST_MSG);
+
+	if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+		/*
+		 * Target does not support the PPR message.
+		 * Attempt to negotiate SPI-2 style.
+		 */
+		if (bootverbose) {
+			printk("(%s:%c:%d:%d): PPR Rejected. "
+			       "Trying WDTR/SDTR\n",
+			       ahc_name(ahc), devinfo->channel,
+			       devinfo->target, devinfo->lun);
+		}
+		tinfo->goal.ppr_options = 0;
+		tinfo->curr.transport_version = 2;
+		tinfo->goal.transport_version = 2;
+		ahc->msgout_index = 0;
+		ahc->msgout_len = 0;
+		ahc_build_transfer_msg(ahc, devinfo);
+		ahc->msgout_index = 0;
+		response = 1;
+	} else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+		/* note 8bit xfers */
+		printk("(%s:%c:%d:%d): refuses WIDE negotiation.  Using "
+		       "8bit transfers\n", ahc_name(ahc),
+		       devinfo->channel, devinfo->target, devinfo->lun);
+		ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+			      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+			      /*paused*/TRUE);
+		/*
+		 * No need to clear the sync rate.  If the target
+		 * did not accept the command, our syncrate is
+		 * unaffected.  If the target started the negotiation,
+		 * but rejected our response, we already cleared the
+		 * sync rate before sending our WDTR.
+		 */
+		if (tinfo->goal.offset != tinfo->curr.offset) {
+
+			/* Start the sync negotiation */
+			ahc->msgout_index = 0;
+			ahc->msgout_len = 0;
+			ahc_build_transfer_msg(ahc, devinfo);
+			ahc->msgout_index = 0;
+			response = 1;
+		}
+	} else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+		/* note asynch xfers and clear flag */
+		ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
+				 /*offset*/0, /*ppr_options*/0,
+				 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				 /*paused*/TRUE);
+		printk("(%s:%c:%d:%d): refuses synchronous negotiation. "
+		       "Using asynchronous transfers\n",
+		       ahc_name(ahc), devinfo->channel,
+		       devinfo->target, devinfo->lun);
+	} else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+		int tag_type;
+		int mask;
+
+		tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+		if (tag_type == MSG_SIMPLE_TASK) {
+			printk("(%s:%c:%d:%d): refuses tagged commands.  "
+			       "Performing non-tagged I/O\n", ahc_name(ahc),
+			       devinfo->channel, devinfo->target, devinfo->lun);
+			ahc_set_tags(ahc, scb->io_ctx, devinfo, AHC_QUEUE_NONE);
+			mask = ~0x23;
+		} else {
+			printk("(%s:%c:%d:%d): refuses %s tagged commands.  "
+			       "Performing simple queue tagged I/O only\n",
+			       ahc_name(ahc), devinfo->channel, devinfo->target,
+			       devinfo->lun, tag_type == MSG_ORDERED_TASK
+			       ? "ordered" : "head of queue");
+			ahc_set_tags(ahc, scb->io_ctx, devinfo, AHC_QUEUE_BASIC);
+			mask = ~0x03;
+		}
+
+		/*
+		 * Resend the identify for this CCB as the target
+		 * may believe that the selection is invalid otherwise.
+		 */
+		ahc_outb(ahc, SCB_CONTROL,
+			 ahc_inb(ahc, SCB_CONTROL) & mask);
+	 	scb->hscb->control &= mask;
+		ahc_set_transaction_tag(scb, /*enabled*/FALSE,
+					/*type*/MSG_SIMPLE_TASK);
+		ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
+		ahc_assert_atn(ahc);
+
+		/*
+		 * This transaction is now at the head of
+		 * the untagged queue for this target.
+		 */
+		if ((ahc->flags & AHC_SCB_BTT) == 0) {
+			struct scb_tailq *untagged_q;
+
+			untagged_q =
+			    &(ahc->untagged_queues[devinfo->target_offset]);
+			TAILQ_INSERT_HEAD(untagged_q, scb, links.tqe);
+			scb->flags |= SCB_UNTAGGEDQ;
+		}
+		ahc_busy_tcl(ahc, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+			     scb->hscb->tag);
+
+		/*
+		 * Requeue all tagged commands for this target
+		 * currently in our possession so they can be
+		 * converted to untagged commands.
+		 */
+		ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+				   SCB_GET_CHANNEL(ahc, scb),
+				   SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+				   ROLE_INITIATOR, CAM_REQUEUE_REQ,
+				   SEARCH_COMPLETE);
+	} else {
+		/*
+		 * Otherwise, we ignore it.
+		 */
+		printk("%s:%c:%d: Message reject for %x -- ignored\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       last_msg);
+	}
+	return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	u_int scb_index;
+	struct scb *scb;
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	/*
+	 * XXX Actually check data direction in the sequencer?
+	 * Perhaps add datadir to some spare bits in the hscb?
+	 */
+	if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
+	 || ahc_get_transfer_dir(scb) != CAM_DIR_IN) {
+		/*
+		 * Ignore the message if we haven't
+		 * seen an appropriate data phase yet.
+		 */
+	} else {
+		/*
+		 * If the residual occurred on the last
+		 * transfer and the transfer request was
+		 * expected to end on an odd count, do
+		 * nothing.  Otherwise, subtract a byte
+		 * and update the residual count accordingly.
+		 */
+		uint32_t sgptr;
+
+		sgptr = ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+		if ((sgptr & SG_LIST_NULL) != 0
+		 && (ahc_inb(ahc, SCB_LUN) & SCB_XFERLEN_ODD) != 0) {
+			/*
+			 * If the residual occurred on the last
+			 * transfer and the transfer request was
+			 * expected to end on an odd count, do
+			 * nothing.
+			 */
+		} else {
+			struct ahc_dma_seg *sg;
+			uint32_t data_cnt;
+			uint32_t data_addr;
+			uint32_t sglen;
+
+			/* Pull in all of the sgptr */
+			sgptr = ahc_inl(ahc, SCB_RESIDUAL_SGPTR);
+			data_cnt = ahc_inl(ahc, SCB_RESIDUAL_DATACNT);
+
+			if ((sgptr & SG_LIST_NULL) != 0) {
+				/*
+				 * The residual data count is not updated
+				 * for the command run to completion case.
+				 * Explicitly zero the count.
+				 */
+				data_cnt &= ~AHC_SG_LEN_MASK;
+			}
+
+			data_addr = ahc_inl(ahc, SHADDR);
+
+			data_cnt += 1;
+			data_addr -= 1;
+			sgptr &= SG_PTR_MASK;
+
+			sg = ahc_sg_bus_to_virt(scb, sgptr);
+
+			/*
+			 * The residual sg ptr points to the next S/G
+			 * to load so we must go back one.
+			 */
+			sg--;
+			sglen = ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+			if (sg != scb->sg_list
+			 && sglen < (data_cnt & AHC_SG_LEN_MASK)) {
+
+				sg--;
+				sglen = ahc_le32toh(sg->len);
+				/*
+				 * Preserve High Address and SG_LIST bits
+				 * while setting the count to 1.
+				 */
+				data_cnt = 1 | (sglen & (~AHC_SG_LEN_MASK));
+				data_addr = ahc_le32toh(sg->addr)
+					  + (sglen & AHC_SG_LEN_MASK) - 1;
+
+				/*
+				 * Increment sg so it points to the
+				 * "next" sg.
+				 */
+				sg++;
+				sgptr = ahc_sg_virt_to_bus(scb, sg);
+			}
+			ahc_outl(ahc, SCB_RESIDUAL_SGPTR, sgptr);
+			ahc_outl(ahc, SCB_RESIDUAL_DATACNT, data_cnt);
+			/*
+			 * Toggle the "oddness" of the transfer length
+			 * to handle this mid-transfer ignore wide
+			 * residue.  This ensures that the oddness is
+			 * correct for subsequent data transfers.
+			 */
+			ahc_outb(ahc, SCB_LUN,
+				 ahc_inb(ahc, SCB_LUN) ^ SCB_XFERLEN_ODD);
+		}
+	}
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahc_reinitialize_dataptrs(struct ahc_softc *ahc)
+{
+	struct	 scb *scb;
+	struct	 ahc_dma_seg *sg;
+	u_int	 scb_index;
+	uint32_t sgptr;
+	uint32_t resid;
+	uint32_t dataptr;
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	sgptr = (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+	      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+	      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8)
+	      |	ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+
+	sgptr &= SG_PTR_MASK;
+	sg = ahc_sg_bus_to_virt(scb, sgptr);
+
+	/* The residual sg_ptr always points to the next sg */
+	sg--;
+
+	resid = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 2) << 16)
+	      | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 1) << 8)
+	      | ahc_inb(ahc, SCB_RESIDUAL_DATACNT);
+
+	dataptr = ahc_le32toh(sg->addr)
+		+ (ahc_le32toh(sg->len) & AHC_SG_LEN_MASK)
+		- resid;
+	if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+		u_int dscommand1;
+
+		dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+		ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+		ahc_outb(ahc, HADDR,
+			 (ahc_le32toh(sg->len) >> 24) & SG_HIGH_ADDR_BITS);
+		ahc_outb(ahc, DSCOMMAND1, dscommand1);
+	}
+	ahc_outb(ahc, HADDR + 3, dataptr >> 24);
+	ahc_outb(ahc, HADDR + 2, dataptr >> 16);
+	ahc_outb(ahc, HADDR + 1, dataptr >> 8);
+	ahc_outb(ahc, HADDR, dataptr);
+	ahc_outb(ahc, HCNT + 2, resid >> 16);
+	ahc_outb(ahc, HCNT + 1, resid >> 8);
+	ahc_outb(ahc, HCNT, resid);
+	if ((ahc->features & AHC_ULTRA2) == 0) {
+		ahc_outb(ahc, STCNT + 2, resid >> 16);
+		ahc_outb(ahc, STCNT + 1, resid >> 8);
+		ahc_outb(ahc, STCNT, resid);
+	}
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		    cam_status status, char *message, int verbose_level)
+{
+#ifdef AHC_TARGET_MODE
+	struct ahc_tmode_tstate* tstate;
+	u_int lun;
+#endif
+	int found;
+
+	found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+			       CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+			       status);
+
+#ifdef AHC_TARGET_MODE
+	/*
+	 * Send an immediate notify ccb to all target mord peripheral
+	 * drivers affected by this action.
+	 */
+	tstate = ahc->enabled_targets[devinfo->our_scsiid];
+	if (tstate != NULL) {
+		for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+			struct ahc_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[lun];
+			if (lstate == NULL)
+				continue;
+
+			ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
+					       MSG_BUS_DEV_RESET, /*arg*/0);
+			ahc_send_lstate_events(ahc, lstate);
+		}
+	}
+#endif
+
+	/*
+	 * Go back to async/narrow transfers and renegotiate.
+	 */
+	ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+		      AHC_TRANS_CUR, /*paused*/TRUE);
+	ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
+			 /*period*/0, /*offset*/0, /*ppr_options*/0,
+			 AHC_TRANS_CUR, /*paused*/TRUE);
+	
+	if (status != CAM_SEL_TIMEOUT)
+		ahc_send_async(ahc, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_SENT_BDR);
+
+	if (message != NULL
+	 && (verbose_level <= bootverbose))
+		printk("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
+		       message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHC_TARGET_MODE
+static void
+ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		       struct scb *scb)
+{
+
+	/*              
+	 * To facilitate adding multiple messages together,
+	 * each routine should increment the index and len
+	 * variables instead of setting them explicitly.
+	 */             
+	ahc->msgout_index = 0;
+	ahc->msgout_len = 0;
+
+	if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+		ahc_build_transfer_msg(ahc, devinfo);
+	else
+		panic("ahc_intr: AWAITING target message with no message");
+
+	ahc->msgout_index = 0;
+	ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahc_softc *
+ahc_alloc(void *platform_arg, char *name)
+{
+	struct  ahc_softc *ahc;
+	int	i;
+
+#ifndef	__FreeBSD__
+	ahc = kmalloc(sizeof(*ahc), GFP_ATOMIC);
+	if (!ahc) {
+		printk("aic7xxx: cannot malloc softc!\n");
+		kfree(name);
+		return NULL;
+	}
+#else
+	ahc = device_get_softc((device_t)platform_arg);
+#endif
+	memset(ahc, 0, sizeof(*ahc));
+	ahc->seep_config = kmalloc(sizeof(*ahc->seep_config), GFP_ATOMIC);
+	if (ahc->seep_config == NULL) {
+#ifndef	__FreeBSD__
+		kfree(ahc);
+#endif
+		kfree(name);
+		return (NULL);
+	}
+	LIST_INIT(&ahc->pending_scbs);
+	/* We don't know our unit number until the OSM sets it */
+	ahc->name = name;
+	ahc->unit = -1;
+	ahc->description = NULL;
+	ahc->channel = 'A';
+	ahc->channel_b = 'B';
+	ahc->chip = AHC_NONE;
+	ahc->features = AHC_FENONE;
+	ahc->bugs = AHC_BUGNONE;
+	ahc->flags = AHC_FNONE;
+	/*
+	 * Default to all error reporting enabled with the
+	 * sequencer operating at its fastest speed.
+	 * The bus attach code may modify this.
+	 */
+	ahc->seqctl = FASTMODE;
+
+	for (i = 0; i < AHC_NUM_TARGETS; i++)
+		TAILQ_INIT(&ahc->untagged_queues[i]);
+	if (ahc_platform_alloc(ahc, platform_arg) != 0) {
+		ahc_free(ahc);
+		ahc = NULL;
+	}
+	return (ahc);
+}
+
+int
+ahc_softc_init(struct ahc_softc *ahc)
+{
+
+	/* The IRQMS bit is only valid on VL and EISA chips */
+	if ((ahc->chip & AHC_PCI) == 0)
+		ahc->unpause = ahc_inb(ahc, HCNTRL) & IRQMS;
+	else
+		ahc->unpause = 0;
+	ahc->pause = ahc->unpause | PAUSE; 
+	/* XXX The shared scb data stuff should be deprecated */
+	if (ahc->scb_data == NULL) {
+		ahc->scb_data = kzalloc(sizeof(*ahc->scb_data), GFP_ATOMIC);
+		if (ahc->scb_data == NULL)
+			return (ENOMEM);
+	}
+
+	return (0);
+}
+
+void
+ahc_set_unit(struct ahc_softc *ahc, int unit)
+{
+	ahc->unit = unit;
+}
+
+void
+ahc_set_name(struct ahc_softc *ahc, char *name)
+{
+	if (ahc->name != NULL)
+		kfree(ahc->name);
+	ahc->name = name;
+}
+
+void
+ahc_free(struct ahc_softc *ahc)
+{
+	int i;
+
+	switch (ahc->init_level) {
+	default:
+	case 5:
+		ahc_shutdown(ahc);
+		/* FALLTHROUGH */
+	case 4:
+		ahc_dmamap_unload(ahc, ahc->shared_data_dmat,
+				  ahc->shared_data_dmamap);
+		/* FALLTHROUGH */
+	case 3:
+		ahc_dmamem_free(ahc, ahc->shared_data_dmat, ahc->qoutfifo,
+				ahc->shared_data_dmamap);
+		ahc_dmamap_destroy(ahc, ahc->shared_data_dmat,
+				   ahc->shared_data_dmamap);
+		/* FALLTHROUGH */
+	case 2:
+		ahc_dma_tag_destroy(ahc, ahc->shared_data_dmat);
+	case 1:
+#ifndef __linux__
+		ahc_dma_tag_destroy(ahc, ahc->buffer_dmat);
+#endif
+		break;
+	case 0:
+		break;
+	}
+
+#ifndef __linux__
+	ahc_dma_tag_destroy(ahc, ahc->parent_dmat);
+#endif
+	ahc_platform_free(ahc);
+	ahc_fini_scbdata(ahc);
+	for (i = 0; i < AHC_NUM_TARGETS; i++) {
+		struct ahc_tmode_tstate *tstate;
+
+		tstate = ahc->enabled_targets[i];
+		if (tstate != NULL) {
+#ifdef AHC_TARGET_MODE
+			int j;
+
+			for (j = 0; j < AHC_NUM_LUNS; j++) {
+				struct ahc_tmode_lstate *lstate;
+
+				lstate = tstate->enabled_luns[j];
+				if (lstate != NULL) {
+					xpt_free_path(lstate->path);
+					kfree(lstate);
+				}
+			}
+#endif
+			kfree(tstate);
+		}
+	}
+#ifdef AHC_TARGET_MODE
+	if (ahc->black_hole != NULL) {
+		xpt_free_path(ahc->black_hole->path);
+		kfree(ahc->black_hole);
+	}
+#endif
+	if (ahc->name != NULL)
+		kfree(ahc->name);
+	if (ahc->seep_config != NULL)
+		kfree(ahc->seep_config);
+#ifndef __FreeBSD__
+	kfree(ahc);
+#endif
+	return;
+}
+
+static void
+ahc_shutdown(void *arg)
+{
+	struct	ahc_softc *ahc;
+	int	i;
+
+	ahc = (struct ahc_softc *)arg;
+
+	/* This will reset most registers to 0, but not all */
+	ahc_reset(ahc, /*reinit*/FALSE);
+	ahc_outb(ahc, SCSISEQ, 0);
+	ahc_outb(ahc, SXFRCTL0, 0);
+	ahc_outb(ahc, DSPCISTATUS, 0);
+
+	for (i = TARG_SCSIRATE; i < SCSICONF; i++)
+		ahc_outb(ahc, i, 0);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset.  If "reinit" is
+ * non-zero, this reset occurred after initial configuration
+ * and the caller requests that the chip be fully reinitialized
+ * to a runable state.  Chip interrupts are *not* enabled after
+ * a reinitialization.  The caller must enable interrupts via
+ * ahc_intr_enable().
+ */
+int
+ahc_reset(struct ahc_softc *ahc, int reinit)
+{
+	u_int	sblkctl;
+	u_int	sxfrctl1_a, sxfrctl1_b;
+	int	error;
+	int	wait;
+	
+	/*
+	 * Preserve the value of the SXFRCTL1 register for all channels.
+	 * It contains settings that affect termination and we don't want
+	 * to disturb the integrity of the bus.
+	 */
+	ahc_pause(ahc);
+	sxfrctl1_b = 0;
+	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) {
+		u_int sblkctl;
+
+		/*
+		 * Save channel B's settings in case this chip
+		 * is setup for TWIN channel operation.
+		 */
+		sblkctl = ahc_inb(ahc, SBLKCTL);
+		ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+		sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
+		ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+	}
+	sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
+
+	ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
+
+	/*
+	 * Ensure that the reset has finished.  We delay 1000us
+	 * prior to reading the register to make sure the chip
+	 * has sufficiently completed its reset to handle register
+	 * accesses.
+	 */
+	wait = 1000;
+	do {
+		ahc_delay(1000);
+	} while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
+
+	if (wait == 0) {
+		printk("%s: WARNING - Failed chip reset!  "
+		       "Trying to initialize anyway.\n", ahc_name(ahc));
+	}
+	ahc_outb(ahc, HCNTRL, ahc->pause);
+
+	/* Determine channel configuration */
+	sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
+	/* No Twin Channel PCI cards */
+	if ((ahc->chip & AHC_PCI) != 0)
+		sblkctl &= ~SELBUSB;
+	switch (sblkctl) {
+	case 0:
+		/* Single Narrow Channel */
+		break;
+	case 2:
+		/* Wide Channel */
+		ahc->features |= AHC_WIDE;
+		break;
+	case 8:
+		/* Twin Channel */
+		ahc->features |= AHC_TWIN;
+		break;
+	default:
+		printk(" Unsupported adapter type.  Ignoring\n");
+		return(-1);
+	}
+
+	/*
+	 * Reload sxfrctl1.
+	 *
+	 * We must always initialize STPWEN to 1 before we
+	 * restore the saved values.  STPWEN is initialized
+	 * to a tri-state condition which can only be cleared
+	 * by turning it on.
+	 */
+	if ((ahc->features & AHC_TWIN) != 0) {
+		u_int sblkctl;
+
+		sblkctl = ahc_inb(ahc, SBLKCTL);
+		ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+		ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
+		ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+	}
+	ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
+
+	error = 0;
+	if (reinit != 0)
+		/*
+		 * If a recovery action has forced a chip reset,
+		 * re-initialize the chip to our liking.
+		 */
+		error = ahc->bus_chip_init(ahc);
+#ifdef AHC_DUMP_SEQ
+	else 
+		ahc_dumpseq(ahc);
+#endif
+
+	return (error);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahc_probe_scbs(struct ahc_softc *ahc) {
+	int i;
+
+	for (i = 0; i < AHC_SCB_MAX; i++) {
+
+		ahc_outb(ahc, SCBPTR, i);
+		ahc_outb(ahc, SCB_BASE, i);
+		if (ahc_inb(ahc, SCB_BASE) != i)
+			break;
+		ahc_outb(ahc, SCBPTR, 0);
+		if (ahc_inb(ahc, SCB_BASE) != 0)
+			break;
+	}
+	return (i);
+}
+
+static void
+ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 
+{
+	dma_addr_t *baddr;
+
+	baddr = (dma_addr_t *)arg;
+	*baddr = segs->ds_addr;
+}
+
+static void
+ahc_build_free_scb_list(struct ahc_softc *ahc)
+{
+	int scbsize;
+	int i;
+
+	scbsize = 32;
+	if ((ahc->flags & AHC_LSCBS_ENABLED) != 0)
+		scbsize = 64;
+
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		int j;
+
+		ahc_outb(ahc, SCBPTR, i);
+
+		/*
+		 * Touch all SCB bytes to avoid parity errors
+		 * should one of our debugging routines read
+		 * an otherwise uninitiatlized byte.
+		 */
+		for (j = 0; j < scbsize; j++)
+			ahc_outb(ahc, SCB_BASE+j, 0xFF);
+
+		/* Clear the control byte. */
+		ahc_outb(ahc, SCB_CONTROL, 0);
+
+		/* Set the next pointer */
+		if ((ahc->flags & AHC_PAGESCBS) != 0)
+			ahc_outb(ahc, SCB_NEXT, i+1);
+		else 
+			ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+		/* Make the tag number, SCSIID, and lun invalid */
+		ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+		ahc_outb(ahc, SCB_SCSIID, 0xFF);
+		ahc_outb(ahc, SCB_LUN, 0xFF);
+	}
+
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		/* SCB 0 heads the free list. */
+		ahc_outb(ahc, FREE_SCBH, 0);
+	} else {
+		/* No free list. */
+		ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
+	}
+
+	/* Make sure that the last SCB terminates the free list */
+	ahc_outb(ahc, SCBPTR, i-1);
+	ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+}
+
+static int
+ahc_init_scbdata(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+
+	scb_data = ahc->scb_data;
+	SLIST_INIT(&scb_data->free_scbs);
+	SLIST_INIT(&scb_data->sg_maps);
+
+	/* Allocate SCB resources */
+	scb_data->scbarray = kzalloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC,
+				GFP_ATOMIC);
+	if (scb_data->scbarray == NULL)
+		return (ENOMEM);
+
+	/* Determine the number of hardware SCBs and initialize them */
+
+	scb_data->maxhscbs = ahc_probe_scbs(ahc);
+	if (ahc->scb_data->maxhscbs == 0) {
+		printk("%s: No SCB space found\n", ahc_name(ahc));
+		return (ENXIO);
+	}
+
+	/*
+	 * Create our DMA tags.  These tags define the kinds of device
+	 * accessible memory allocations and memory mappings we will
+	 * need to perform during normal operation.
+	 *
+	 * Unless we need to further restrict the allocation, we rely
+	 * on the restrictions of the parent dmat, hence the common
+	 * use of MAXADDR and MAXSIZE.
+	 */
+
+	/* DMA tag for our hardware scb structures */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->hscb_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Allocation for our hscbs */
+	if (ahc_dmamem_alloc(ahc, scb_data->hscb_dmat,
+			     (void **)&scb_data->hscbs,
+			     BUS_DMA_NOWAIT, &scb_data->hscb_dmamap) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* And permanently map them */
+	ahc_dmamap_load(ahc, scb_data->hscb_dmat, scb_data->hscb_dmamap,
+			scb_data->hscbs,
+			AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+			ahc_dmamap_cb, &scb_data->hscb_busaddr, /*flags*/0);
+
+	scb_data->init_level++;
+
+	/* DMA tag for our sense buffers */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->sense_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Allocate them */
+	if (ahc_dmamem_alloc(ahc, scb_data->sense_dmat,
+			     (void **)&scb_data->sense,
+			     BUS_DMA_NOWAIT, &scb_data->sense_dmamap) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* And permanently map them */
+	ahc_dmamap_load(ahc, scb_data->sense_dmat, scb_data->sense_dmamap,
+			scb_data->sense,
+			AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+			ahc_dmamap_cb, &scb_data->sense_busaddr, /*flags*/0);
+
+	scb_data->init_level++;
+
+	/* DMA tag for our S/G structures.  We allocate in page sized chunks */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/8,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       PAGE_SIZE, /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->sg_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Perform initial CCB allocation */
+	memset(scb_data->hscbs, 0,
+	       AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb));
+	ahc_alloc_scbs(ahc);
+
+	if (scb_data->numscbs == 0) {
+		printk("%s: ahc_init_scbdata - "
+		       "Unable to allocate initial scbs\n",
+		       ahc_name(ahc));
+		goto error_exit;
+	}
+
+	/*
+	 * Reserve the next queued SCB.
+	 */
+	ahc->next_queued_scb = ahc_get_scb(ahc);
+
+	/*
+	 * Note that we were successful
+	 */
+	return (0); 
+
+error_exit:
+
+	return (ENOMEM);
+}
+
+static void
+ahc_fini_scbdata(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+
+	scb_data = ahc->scb_data;
+	if (scb_data == NULL)
+		return;
+
+	switch (scb_data->init_level) {
+	default:
+	case 7:
+	{
+		struct sg_map_node *sg_map;
+
+		while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
+			SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+			ahc_dmamap_unload(ahc, scb_data->sg_dmat,
+					  sg_map->sg_dmamap);
+			ahc_dmamem_free(ahc, scb_data->sg_dmat,
+					sg_map->sg_vaddr,
+					sg_map->sg_dmamap);
+			kfree(sg_map);
+		}
+		ahc_dma_tag_destroy(ahc, scb_data->sg_dmat);
+	}
+	case 6:
+		ahc_dmamap_unload(ahc, scb_data->sense_dmat,
+				  scb_data->sense_dmamap);
+	case 5:
+		ahc_dmamem_free(ahc, scb_data->sense_dmat, scb_data->sense,
+				scb_data->sense_dmamap);
+		ahc_dmamap_destroy(ahc, scb_data->sense_dmat,
+				   scb_data->sense_dmamap);
+	case 4:
+		ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
+	case 3:
+		ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
+				  scb_data->hscb_dmamap);
+	case 2:
+		ahc_dmamem_free(ahc, scb_data->hscb_dmat, scb_data->hscbs,
+				scb_data->hscb_dmamap);
+		ahc_dmamap_destroy(ahc, scb_data->hscb_dmat,
+				   scb_data->hscb_dmamap);
+	case 1:
+		ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
+		break;
+	case 0:
+		break;
+	}
+	if (scb_data->scbarray != NULL)
+		kfree(scb_data->scbarray);
+}
+
+static void
+ahc_alloc_scbs(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+	struct scb *next_scb;
+	struct sg_map_node *sg_map;
+	dma_addr_t physaddr;
+	struct ahc_dma_seg *segs;
+	int newcount;
+	int i;
+
+	scb_data = ahc->scb_data;
+	if (scb_data->numscbs >= AHC_SCB_MAX_ALLOC)
+		/* Can't allocate any more */
+		return;
+
+	next_scb = &scb_data->scbarray[scb_data->numscbs];
+
+	sg_map = kmalloc(sizeof(*sg_map), GFP_ATOMIC);
+
+	if (sg_map == NULL)
+		return;
+
+	/* Allocate S/G space for the next batch of SCBS */
+	if (ahc_dmamem_alloc(ahc, scb_data->sg_dmat,
+			     (void **)&sg_map->sg_vaddr,
+			     BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {
+		kfree(sg_map);
+		return;
+	}
+
+	SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+	ahc_dmamap_load(ahc, scb_data->sg_dmat, sg_map->sg_dmamap,
+			sg_map->sg_vaddr, PAGE_SIZE, ahc_dmamap_cb,
+			&sg_map->sg_physaddr, /*flags*/0);
+
+	segs = sg_map->sg_vaddr;
+	physaddr = sg_map->sg_physaddr;
+
+	newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
+	newcount = min(newcount, (AHC_SCB_MAX_ALLOC - scb_data->numscbs));
+	for (i = 0; i < newcount; i++) {
+		struct scb_platform_data *pdata;
+#ifndef __linux__
+		int error;
+#endif
+		pdata = kmalloc(sizeof(*pdata), GFP_ATOMIC);
+		if (pdata == NULL)
+			break;
+		next_scb->platform_data = pdata;
+		next_scb->sg_map = sg_map;
+		next_scb->sg_list = segs;
+		/*
+		 * The sequencer always starts with the second entry.
+		 * The first entry is embedded in the scb.
+		 */
+		next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
+		next_scb->ahc_softc = ahc;
+		next_scb->flags = SCB_FREE;
+#ifndef __linux__
+		error = ahc_dmamap_create(ahc, ahc->buffer_dmat, /*flags*/0,
+					  &next_scb->dmamap);
+		if (error != 0)
+			break;
+#endif
+		next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
+		next_scb->hscb->tag = ahc->scb_data->numscbs;
+		SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
+				  next_scb, links.sle);
+		segs += AHC_NSEG;
+		physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
+		next_scb++;
+		ahc->scb_data->numscbs++;
+	}
+}
+
+void
+ahc_controller_info(struct ahc_softc *ahc, char *buf)
+{
+	int len;
+
+	len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
+	buf += len;
+	if ((ahc->features & AHC_TWIN) != 0)
+ 		len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
+			      "B SCSI Id=%d, primary %c, ",
+			      ahc->our_id, ahc->our_id_b,
+			      (ahc->flags & AHC_PRIMARY_CHANNEL) + 'A');
+	else {
+		const char *speed;
+		const char *type;
+
+		speed = "";
+		if ((ahc->features & AHC_ULTRA) != 0) {
+			speed = "Ultra ";
+		} else if ((ahc->features & AHC_DT) != 0) {
+			speed = "Ultra160 ";
+		} else if ((ahc->features & AHC_ULTRA2) != 0) {
+			speed = "Ultra2 ";
+		}
+		if ((ahc->features & AHC_WIDE) != 0) {
+			type = "Wide";
+		} else {
+			type = "Single";
+		}
+		len = sprintf(buf, "%s%s Channel %c, SCSI Id=%d, ",
+			      speed, type, ahc->channel, ahc->our_id);
+	}
+	buf += len;
+
+	if ((ahc->flags & AHC_PAGESCBS) != 0)
+		sprintf(buf, "%d/%d SCBs",
+			ahc->scb_data->maxhscbs, AHC_MAX_QUEUE);
+	else
+		sprintf(buf, "%d SCBs", ahc->scb_data->maxhscbs);
+}
+
+int
+ahc_chip_init(struct ahc_softc *ahc)
+{
+	int	 term;
+	int	 error;
+	u_int	 i;
+	u_int	 scsi_conf;
+	u_int	 scsiseq_template;
+	uint32_t physaddr;
+
+	ahc_outb(ahc, SEQ_FLAGS, 0);
+	ahc_outb(ahc, SEQ_FLAGS2, 0);
+
+	/* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
+	if (ahc->features & AHC_TWIN) {
+
+		/*
+		 * Setup Channel B first.
+		 */
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+		term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
+		ahc_outb(ahc, SCSIID, ahc->our_id_b);
+		scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+		ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+					|term|ahc->seltime_b|ENSTIMER|ACTNEGEN);
+		if ((ahc->features & AHC_ULTRA2) != 0)
+			ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+		ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+		ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+		/* Select Channel A */
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+	}
+	term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+	else
+		ahc_outb(ahc, SCSIID, ahc->our_id);
+	scsi_conf = ahc_inb(ahc, SCSICONF);
+	ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+				|term|ahc->seltime
+				|ENSTIMER|ACTNEGEN);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+	ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+	ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+	/* There are no untagged SCBs active yet. */
+	for (i = 0; i < 16; i++) {
+		ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, 0));
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			int lun;
+
+			/*
+			 * The SCB based BTT allows an entry per
+			 * target and lun pair.
+			 */
+			for (lun = 1; lun < AHC_NUM_LUNS; lun++)
+				ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, lun));
+		}
+	}
+
+	/* All of our queues are empty */
+	for (i = 0; i < 256; i++)
+		ahc->qoutfifo[i] = SCB_LIST_NULL;
+	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_PREREAD);
+
+	for (i = 0; i < 256; i++)
+		ahc->qinfifo[i] = SCB_LIST_NULL;
+
+	if ((ahc->features & AHC_MULTI_TID) != 0) {
+		ahc_outb(ahc, TARGID, 0);
+		ahc_outb(ahc, TARGID + 1, 0);
+	}
+
+	/*
+	 * Tell the sequencer where it can find our arrays in memory.
+	 */
+	physaddr = ahc->scb_data->hscb_busaddr;
+	ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+	physaddr = ahc->shared_data_busaddr;
+	ahc_outb(ahc, SHARED_DATA_ADDR, physaddr & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 1, (physaddr >> 8) & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 2, (physaddr >> 16) & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+	/*
+	 * Initialize the group code to command length table.
+	 * This overrides the values in TARG_SCSIRATE, so only
+	 * setup the table after we have processed that information.
+	 */
+	ahc_outb(ahc, CMDSIZE_TABLE, 5);
+	ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
+	ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
+	ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
+	ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
+	ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
+	ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
+	ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
+		
+	if ((ahc->features & AHC_HS_MAILBOX) != 0)
+		ahc_outb(ahc, HS_MAILBOX, 0);
+
+	/* Tell the sequencer of our initial queue positions */
+	if ((ahc->features & AHC_TARGETMODE) != 0) {
+		ahc->tqinfifonext = 1;
+		ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
+		ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+	}
+	ahc->qinfifonext = 0;
+	ahc->qoutfifonext = 0;
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+		ahc_outb(ahc, SNSCB_QOFF, ahc->qinfifonext);
+		ahc_outb(ahc, SDSCB_QOFF, 0);
+	} else {
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+		ahc_outb(ahc, QINPOS, ahc->qinfifonext);
+		ahc_outb(ahc, QOUTPOS, ahc->qoutfifonext);
+	}
+
+	/* We don't have any waiting selections */
+	ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
+
+	/* Our disconnection list is empty too */
+	ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
+
+	/* Message out buffer starts empty */
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+
+	/*
+	 * Setup the allowed SCSI Sequences based on operational mode.
+	 * If we are a target, we'll enable select in operations once
+	 * we've had a lun enabled.
+	 */
+	scsiseq_template = ENSELO|ENAUTOATNO|ENAUTOATNP;
+	if ((ahc->flags & AHC_INITIATORROLE) != 0)
+		scsiseq_template |= ENRSELI;
+	ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
+
+	/* Initialize our list of free SCBs. */
+	ahc_build_free_scb_list(ahc);
+
+	/*
+	 * Tell the sequencer which SCB will be the next one it receives.
+	 */
+	ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+	/*
+	 * Load the Sequencer program and Enable the adapter
+	 * in "fast" mode.
+	 */
+	if (bootverbose)
+		printk("%s: Downloading Sequencer Program...",
+		       ahc_name(ahc));
+
+	error = ahc_loadseq(ahc);
+	if (error != 0)
+		return (error);
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		int wait;
+
+		/*
+		 * Wait for up to 500ms for our transceivers
+		 * to settle.  If the adapter does not have
+		 * a cable attached, the transceivers may
+		 * never settle, so don't complain if we
+		 * fail here.
+		 */
+		for (wait = 5000;
+		     (ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+		     wait--)
+			ahc_delay(100);
+	}
+	ahc_restart(ahc);
+	return (0);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahc_init(struct ahc_softc *ahc)
+{
+	int	 max_targ;
+	u_int	 i;
+	u_int	 scsi_conf;
+	u_int	 ultraenb;
+	u_int	 discenable;
+	u_int	 tagenable;
+	size_t	 driver_data_size;
+
+#ifdef AHC_DEBUG
+	if ((ahc_debug & AHC_DEBUG_SEQUENCER) != 0)
+		ahc->flags |= AHC_SEQUENCER_DEBUG;
+#endif
+
+#ifdef AHC_PRINT_SRAM
+	printk("Scratch Ram:");
+	for (i = 0x20; i < 0x5f; i++) {
+		if (((i % 8) == 0) && (i != 0)) {
+			printk ("\n              ");
+		}
+		printk (" 0x%x", ahc_inb(ahc, i));
+	}
+	if ((ahc->features & AHC_MORE_SRAM) != 0) {
+		for (i = 0x70; i < 0x7f; i++) {
+			if (((i % 8) == 0) && (i != 0)) {
+				printk ("\n              ");
+			}
+			printk (" 0x%x", ahc_inb(ahc, i));
+		}
+	}
+	printk ("\n");
+	/*
+	 * Reading uninitialized scratch ram may
+	 * generate parity errors.
+	 */
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+	ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+#endif
+	max_targ = 15;
+
+	/*
+	 * Assume we have a board at this stage and it has been reset.
+	 */
+	if ((ahc->flags & AHC_USEDEFAULTS) != 0)
+		ahc->our_id = ahc->our_id_b = 7;
+	
+	/*
+	 * Default to allowing initiator operations.
+	 */
+	ahc->flags |= AHC_INITIATORROLE;
+
+	/*
+	 * Only allow target mode features if this unit has them enabled.
+	 */
+	if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
+		ahc->features &= ~AHC_TARGETMODE;
+
+#ifndef __linux__
+	/* DMA tag for mapping buffers into device visible space. */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/ahc->flags & AHC_39BIT_ADDRESSING
+					? (dma_addr_t)0x7FFFFFFFFFULL
+					: BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       /*maxsize*/(AHC_NSEG - 1) * PAGE_SIZE,
+			       /*nsegments*/AHC_NSEG,
+			       /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
+			       /*flags*/BUS_DMA_ALLOCNOW,
+			       &ahc->buffer_dmat) != 0) {
+		return (ENOMEM);
+	}
+#endif
+
+	ahc->init_level++;
+
+	/*
+	 * DMA tag for our command fifos and other data in system memory
+	 * the card's sequencer must be able to access.  For initiator
+	 * roles, we need to allocate space for the qinfifo and qoutfifo.
+	 * The qinfifo and qoutfifo are composed of 256 1 byte elements. 
+	 * When providing for the target mode role, we must additionally
+	 * provide space for the incoming target command fifo and an extra
+	 * byte to deal with a dma bug in some chip versions.
+	 */
+	driver_data_size = 2 * 256 * sizeof(uint8_t);
+	if ((ahc->features & AHC_TARGETMODE) != 0)
+		driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd)
+				 + /*DMA WideOdd Bug Buffer*/1;
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       driver_data_size,
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &ahc->shared_data_dmat) != 0) {
+		return (ENOMEM);
+	}
+
+	ahc->init_level++;
+
+	/* Allocation of driver data */
+	if (ahc_dmamem_alloc(ahc, ahc->shared_data_dmat,
+			     (void **)&ahc->qoutfifo,
+			     BUS_DMA_NOWAIT, &ahc->shared_data_dmamap) != 0) {
+		return (ENOMEM);
+	}
+
+	ahc->init_level++;
+
+	/* And permanently map it in */
+	ahc_dmamap_load(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+			ahc->qoutfifo, driver_data_size, ahc_dmamap_cb,
+			&ahc->shared_data_busaddr, /*flags*/0);
+
+	if ((ahc->features & AHC_TARGETMODE) != 0) {
+		ahc->targetcmds = (struct target_cmd *)ahc->qoutfifo;
+		ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[AHC_TMODE_CMDS];
+		ahc->dma_bug_buf = ahc->shared_data_busaddr
+				 + driver_data_size - 1;
+		/* All target command blocks start out invalid. */
+		for (i = 0; i < AHC_TMODE_CMDS; i++)
+			ahc->targetcmds[i].cmd_valid = 0;
+		ahc_sync_tqinfifo(ahc, BUS_DMASYNC_PREREAD);
+		ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[256];
+	}
+	ahc->qinfifo = &ahc->qoutfifo[256];
+
+	ahc->init_level++;
+
+	/* Allocate SCB data now that buffer_dmat is initialized */
+	if (ahc->scb_data->maxhscbs == 0)
+		if (ahc_init_scbdata(ahc) != 0)
+			return (ENOMEM);
+
+	/*
+	 * Allocate a tstate to house information for our
+	 * initiator presence on the bus as well as the user
+	 * data for any target mode initiator.
+	 */
+	if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
+		printk("%s: unable to allocate ahc_tmode_tstate.  "
+		       "Failing attach\n", ahc_name(ahc));
+		return (ENOMEM);
+	}
+
+	if ((ahc->features & AHC_TWIN) != 0) {
+		if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
+			printk("%s: unable to allocate ahc_tmode_tstate.  "
+			       "Failing attach\n", ahc_name(ahc));
+			return (ENOMEM);
+		}
+	}
+
+	if (ahc->scb_data->maxhscbs < AHC_SCB_MAX_ALLOC) {
+		ahc->flags |= AHC_PAGESCBS;
+	} else {
+		ahc->flags &= ~AHC_PAGESCBS;
+	}
+
+#ifdef AHC_DEBUG
+	if (ahc_debug & AHC_SHOW_MISC) {
+		printk("%s: hardware scb %u bytes; kernel scb %u bytes; "
+		       "ahc_dma %u bytes\n",
+			ahc_name(ahc),
+			(u_int)sizeof(struct hardware_scb),
+			(u_int)sizeof(struct scb),
+			(u_int)sizeof(struct ahc_dma_seg));
+	}
+#endif /* AHC_DEBUG */
+
+	/*
+	 * Look at the information that board initialization or
+	 * the board bios has left us.
+	 */
+	if (ahc->features & AHC_TWIN) {
+		scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+		if ((scsi_conf & RESET_SCSI) != 0
+		 && (ahc->flags & AHC_INITIATORROLE) != 0)
+			ahc->flags |= AHC_RESET_BUS_B;
+	}
+
+	scsi_conf = ahc_inb(ahc, SCSICONF);
+	if ((scsi_conf & RESET_SCSI) != 0
+	 && (ahc->flags & AHC_INITIATORROLE) != 0)
+		ahc->flags |= AHC_RESET_BUS_A;
+
+	ultraenb = 0;	
+	tagenable = ALL_TARGETS_MASK;
+
+	/* Grab the disconnection disable table and invert it for our needs */
+	if ((ahc->flags & AHC_USEDEFAULTS) != 0) {
+		printk("%s: Host Adapter Bios disabled.  Using default SCSI "
+			"device parameters\n", ahc_name(ahc));
+		ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
+			      AHC_TERM_ENB_A|AHC_TERM_ENB_B;
+		discenable = ALL_TARGETS_MASK;
+		if ((ahc->features & AHC_ULTRA) != 0)
+			ultraenb = ALL_TARGETS_MASK;
+	} else {
+		discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
+			   | ahc_inb(ahc, DISC_DSB));
+		if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
+			ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
+				      | ahc_inb(ahc, ULTRA_ENB);
+	}
+
+	if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+		max_targ = 7;
+
+	for (i = 0; i <= max_targ; i++) {
+		struct ahc_initiator_tinfo *tinfo;
+		struct ahc_tmode_tstate *tstate;
+		u_int our_id;
+		u_int target_id;
+		char channel;
+
+		channel = 'A';
+		our_id = ahc->our_id;
+		target_id = i;
+		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+			channel = 'B';
+			our_id = ahc->our_id_b;
+			target_id = i % 8;
+		}
+		tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+					    target_id, &tstate);
+		/* Default to async narrow across the board */
+		memset(tinfo, 0, sizeof(*tinfo));
+		if (ahc->flags & AHC_USEDEFAULTS) {
+			if ((ahc->features & AHC_WIDE) != 0)
+				tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+			/*
+			 * These will be truncated when we determine the
+			 * connection type we have with the target.
+			 */
+			tinfo->user.period = ahc_syncrates->period;
+			tinfo->user.offset = MAX_OFFSET;
+		} else {
+			u_int scsirate;
+			uint16_t mask;
+
+			/* Take the settings leftover in scratch RAM. */
+			scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
+			mask = (0x01 << i);
+			if ((ahc->features & AHC_ULTRA2) != 0) {
+				u_int offset;
+				u_int maxsync;
+
+				if ((scsirate & SOFS) == 0x0F) {
+					/*
+					 * Haven't negotiated yet,
+					 * so the format is different.
+					 */
+					scsirate = (scsirate & SXFR) >> 4
+						 | (ultraenb & mask)
+						  ? 0x08 : 0x0
+						 | (scsirate & WIDEXFER);
+					offset = MAX_OFFSET_ULTRA2;
+				} else
+					offset = ahc_inb(ahc, TARG_OFFSET + i);
+				if ((scsirate & ~WIDEXFER) == 0 && offset != 0)
+					/* Set to the lowest sync rate, 5MHz */
+					scsirate |= 0x1c;
+				maxsync = AHC_SYNCRATE_ULTRA2;
+				if ((ahc->features & AHC_DT) != 0)
+					maxsync = AHC_SYNCRATE_DT;
+				tinfo->user.period =
+				    ahc_find_period(ahc, scsirate, maxsync);
+				if (offset == 0)
+					tinfo->user.period = 0;
+				else
+					tinfo->user.offset = MAX_OFFSET;
+				if ((scsirate & SXFR_ULTRA2) <= 8/*10MHz*/
+				 && (ahc->features & AHC_DT) != 0)
+					tinfo->user.ppr_options =
+					    MSG_EXT_PPR_DT_REQ;
+			} else if ((scsirate & SOFS) != 0) {
+				if ((scsirate & SXFR) == 0x40
+				 && (ultraenb & mask) != 0) {
+					/* Treat 10MHz as a non-ultra speed */
+					scsirate &= ~SXFR;
+				 	ultraenb &= ~mask;
+				}
+				tinfo->user.period = 
+				    ahc_find_period(ahc, scsirate,
+						    (ultraenb & mask)
+						   ? AHC_SYNCRATE_ULTRA
+						   : AHC_SYNCRATE_FAST);
+				if (tinfo->user.period != 0)
+					tinfo->user.offset = MAX_OFFSET;
+			}
+			if (tinfo->user.period == 0)
+				tinfo->user.offset = 0;
+			if ((scsirate & WIDEXFER) != 0
+			 && (ahc->features & AHC_WIDE) != 0)
+				tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+			tinfo->user.protocol_version = 4;
+			if ((ahc->features & AHC_DT) != 0)
+				tinfo->user.transport_version = 3;
+			else
+				tinfo->user.transport_version = 2;
+			tinfo->goal.protocol_version = 2;
+			tinfo->goal.transport_version = 2;
+			tinfo->curr.protocol_version = 2;
+			tinfo->curr.transport_version = 2;
+		}
+		tstate->ultraenb = 0;
+	}
+	ahc->user_discenable = discenable;
+	ahc->user_tagenable = tagenable;
+
+	return (ahc->bus_chip_init(ahc));
+}
+
+void
+ahc_intr_enable(struct ahc_softc *ahc, int enable)
+{
+	u_int hcntrl;
+
+	hcntrl = ahc_inb(ahc, HCNTRL);
+	hcntrl &= ~INTEN;
+	ahc->pause &= ~INTEN;
+	ahc->unpause &= ~INTEN;
+	if (enable) {
+		hcntrl |= INTEN;
+		ahc->pause |= INTEN;
+		ahc->unpause |= INTEN;
+	}
+	ahc_outb(ahc, HCNTRL, hcntrl);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahc_pause_and_flushwork(struct ahc_softc *ahc)
+{
+	int intstat;
+	int maxloops;
+	int paused;
+
+	maxloops = 1000;
+	ahc->flags |= AHC_ALL_INTERRUPTS;
+	paused = FALSE;
+	do {
+		if (paused) {
+			ahc_unpause(ahc);
+			/*
+			 * Give the sequencer some time to service
+			 * any active selections.
+			 */
+			ahc_delay(500);
+		}
+		ahc_intr(ahc);
+		ahc_pause(ahc);
+		paused = TRUE;
+		ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+		intstat = ahc_inb(ahc, INTSTAT);
+		if ((intstat & INT_PEND) == 0) {
+			ahc_clear_critical_section(ahc);
+			intstat = ahc_inb(ahc, INTSTAT);
+		}
+	} while (--maxloops
+	      && (intstat != 0xFF || (ahc->features & AHC_REMOVABLE) == 0)
+	      && ((intstat & INT_PEND) != 0
+	       || (ahc_inb(ahc, SSTAT0) & (SELDO|SELINGO)) != 0));
+	if (maxloops == 0) {
+		printk("Infinite interrupt loop, INTSTAT = %x",
+		       ahc_inb(ahc, INTSTAT));
+	}
+	ahc_platform_flushwork(ahc);
+	ahc->flags &= ~AHC_ALL_INTERRUPTS;
+}
+
+#ifdef CONFIG_PM
+int
+ahc_suspend(struct ahc_softc *ahc)
+{
+
+	ahc_pause_and_flushwork(ahc);
+
+	if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+		ahc_unpause(ahc);
+		return (EBUSY);
+	}
+
+#ifdef AHC_TARGET_MODE
+	/*
+	 * XXX What about ATIOs that have not yet been serviced?
+	 * Perhaps we should just refuse to be suspended if we
+	 * are acting in a target role.
+	 */
+	if (ahc->pending_device != NULL) {
+		ahc_unpause(ahc);
+		return (EBUSY);
+	}
+#endif
+	ahc_shutdown(ahc);
+	return (0);
+}
+
+int
+ahc_resume(struct ahc_softc *ahc)
+{
+
+	ahc_reset(ahc, /*reinit*/TRUE);
+	ahc_intr_enable(ahc, TRUE); 
+	ahc_restart(ahc);
+	return (0);
+}
+#endif
+/************************** Busy Target Table *********************************/
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+static u_int
+ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+	u_int scbid;
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		scbid = ahc_inb(ahc, BUSY_TARGETS + target_offset);
+	}
+
+	return (scbid);
+}
+
+static void
+ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		ahc_outb(ahc, BUSY_TARGETS + target_offset, SCB_LIST_NULL);
+	}
+}
+
+static void
+ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
+{
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		ahc_outb(ahc, BUSY_TARGETS + target_offset, scbid);
+	}
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
+	      char channel, int lun, u_int tag, role_t role)
+{
+	int targ = SCB_GET_TARGET(ahc, scb);
+	char chan = SCB_GET_CHANNEL(ahc, scb);
+	int slun = SCB_GET_LUN(scb);
+	int match;
+
+	match = ((chan == channel) || (channel == ALL_CHANNELS));
+	if (match != 0)
+		match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+	if (match != 0)
+		match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+	if (match != 0) {
+#ifdef AHC_TARGET_MODE
+		int group;
+
+		group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+		if (role == ROLE_INITIATOR) {
+			match = (group != XPT_FC_GROUP_TMODE)
+			      && ((tag == scb->hscb->tag)
+			       || (tag == SCB_LIST_NULL));
+		} else if (role == ROLE_TARGET) {
+			match = (group == XPT_FC_GROUP_TMODE)
+			      && ((tag == scb->io_ctx->csio.tag_id)
+			       || (tag == SCB_LIST_NULL));
+		}
+#else /* !AHC_TARGET_MODE */
+		match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+#endif /* AHC_TARGET_MODE */
+	}
+
+	return match;
+}
+
+static void
+ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+	int	target;
+	char	channel;
+	int	lun;
+
+	target = SCB_GET_TARGET(ahc, scb);
+	lun = SCB_GET_LUN(scb);
+	channel = SCB_GET_CHANNEL(ahc, scb);
+	
+	ahc_search_qinfifo(ahc, target, channel, lun,
+			   /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+			   CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+	ahc_platform_freeze_devq(ahc, scb);
+}
+
+void
+ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct scb *prev_scb;
+
+	prev_scb = NULL;
+	if (ahc_qinfifo_count(ahc) != 0) {
+		u_int prev_tag;
+		uint8_t prev_pos;
+
+		prev_pos = ahc->qinfifonext - 1;
+		prev_tag = ahc->qinfifo[prev_pos];
+		prev_scb = ahc_lookup_scb(ahc, prev_tag);
+	}
+	ahc_qinfifo_requeue(ahc, prev_scb, scb);
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+	} else {
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+	}
+}
+
+static void
+ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
+		    struct scb *scb)
+{
+	if (prev_scb == NULL) {
+		ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+	} else {
+		prev_scb->hscb->next = scb->hscb->tag;
+		ahc_sync_scb(ahc, prev_scb, 
+			     BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+	}
+	ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+	scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+	ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahc_qinfifo_count(struct ahc_softc *ahc)
+{
+	uint8_t qinpos;
+	uint8_t diff;
+
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		qinpos = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinpos);
+	} else
+		qinpos = ahc_inb(ahc, QINPOS);
+	diff = ahc->qinfifonext - qinpos;
+	return (diff);
+}
+
+int
+ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
+		   int lun, u_int tag, role_t role, uint32_t status,
+		   ahc_search_action action)
+{
+	struct	scb *scb;
+	struct	scb *prev_scb;
+	uint8_t qinstart;
+	uint8_t qinpos;
+	uint8_t qintail;
+	uint8_t next;
+	uint8_t prev;
+	uint8_t curscbptr;
+	int	found;
+	int	have_qregs;
+
+	qintail = ahc->qinfifonext;
+	have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
+	if (have_qregs) {
+		qinstart = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinstart);
+	} else
+		qinstart = ahc_inb(ahc, QINPOS);
+	qinpos = qinstart;
+	found = 0;
+	prev_scb = NULL;
+
+	if (action == SEARCH_COMPLETE) {
+		/*
+		 * Don't attempt to run any queued untagged transactions
+		 * until we are done with the abort process.
+		 */
+		ahc_freeze_untagged_queues(ahc);
+	}
+
+	/*
+	 * Start with an empty queue.  Entries that are not chosen
+	 * for removal will be re-added to the queue as we go.
+	 */
+	ahc->qinfifonext = qinpos;
+	ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+	while (qinpos != qintail) {
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
+		if (scb == NULL) {
+			printk("qinpos = %d, SCB index = %d\n",
+				qinpos, ahc->qinfifo[qinpos]);
+			panic("Loop 1\n");
+		}
+
+		if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) {
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+			{
+				cam_status ostat;
+				cam_status cstat;
+
+				ostat = ahc_get_transaction_status(scb);
+				if (ostat == CAM_REQ_INPROG)
+					ahc_set_transaction_status(scb, status);
+				cstat = ahc_get_transaction_status(scb);
+				if (cstat != CAM_REQ_CMP)
+					ahc_freeze_scb(scb);
+				if ((scb->flags & SCB_ACTIVE) == 0)
+					printk("Inactive SCB in qinfifo\n");
+				ahc_done(ahc, scb);
+
+				/* FALLTHROUGH */
+			}
+			case SEARCH_REMOVE:
+				break;
+			case SEARCH_COUNT:
+				ahc_qinfifo_requeue(ahc, prev_scb, scb);
+				prev_scb = scb;
+				break;
+			}
+		} else {
+			ahc_qinfifo_requeue(ahc, prev_scb, scb);
+			prev_scb = scb;
+		}
+		qinpos++;
+	}
+
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+	} else {
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+	}
+
+	if (action != SEARCH_COUNT
+	 && (found != 0)
+	 && (qinstart != ahc->qinfifonext)) {
+		/*
+		 * The sequencer may be in the process of dmaing
+		 * down the SCB at the beginning of the queue.
+		 * This could be problematic if either the first,
+		 * or the second SCB is removed from the queue
+		 * (the first SCB includes a pointer to the "next"
+		 * SCB to dma). If we have removed any entries, swap
+		 * the first element in the queue with the next HSCB
+		 * so the sequencer will notice that NEXT_QUEUED_SCB
+		 * has changed during its dma attempt and will retry
+		 * the DMA.
+		 */
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinstart]);
+
+		if (scb == NULL) {
+			printk("found = %d, qinstart = %d, qinfifionext = %d\n",
+				found, qinstart, ahc->qinfifonext);
+			panic("First/Second Qinfifo fixup\n");
+		}
+		/*
+		 * ahc_swap_with_next_hscb forces our next pointer to
+		 * point to the reserved SCB for future commands.  Save
+		 * and restore our original next pointer to maintain
+		 * queue integrity.
+		 */
+		next = scb->hscb->next;
+		ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
+		ahc_swap_with_next_hscb(ahc, scb);
+		scb->hscb->next = next;
+		ahc->qinfifo[qinstart] = scb->hscb->tag;
+
+		/* Tell the card about the new head of the qinfifo. */
+		ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+
+		/* Fixup the tail "next" pointer. */
+		qintail = ahc->qinfifonext - 1;
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qintail]);
+		scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+	}
+
+	/*
+	 * Search waiting for selection list.
+	 */
+	curscbptr = ahc_inb(ahc, SCBPTR);
+	next = ahc_inb(ahc, WAITING_SCBH);  /* Start at head of list. */
+	prev = SCB_LIST_NULL;
+
+	while (next != SCB_LIST_NULL) {
+		uint8_t scb_index;
+
+		ahc_outb(ahc, SCBPTR, next);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		if (scb_index >= ahc->scb_data->numscbs) {
+			printk("Waiting List inconsistency. "
+			       "SCB index == %d, yet numscbs == %d.",
+			       scb_index, ahc->scb_data->numscbs);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+		}
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printk("scb_index = %d, next = %d\n",
+				scb_index, next);
+			panic("Waiting List traversal\n");
+		}
+		if (ahc_match_scb(ahc, scb, target, channel,
+				  lun, SCB_LIST_NULL, role)) {
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+			{
+				cam_status ostat;
+				cam_status cstat;
+
+				ostat = ahc_get_transaction_status(scb);
+				if (ostat == CAM_REQ_INPROG)
+					ahc_set_transaction_status(scb,
+								   status);
+				cstat = ahc_get_transaction_status(scb);
+				if (cstat != CAM_REQ_CMP)
+					ahc_freeze_scb(scb);
+				if ((scb->flags & SCB_ACTIVE) == 0)
+					printk("Inactive SCB in Waiting List\n");
+				ahc_done(ahc, scb);
+				/* FALLTHROUGH */
+			}
+			case SEARCH_REMOVE:
+				next = ahc_rem_wscb(ahc, next, prev);
+				break;
+			case SEARCH_COUNT:
+				prev = next;
+				next = ahc_inb(ahc, SCB_NEXT);
+				break;
+			}
+		} else {
+			
+			prev = next;
+			next = ahc_inb(ahc, SCB_NEXT);
+		}
+	}
+	ahc_outb(ahc, SCBPTR, curscbptr);
+
+	found += ahc_search_untagged_queues(ahc, /*ahc_io_ctx_t*/NULL, target,
+					    channel, lun, status, action);
+
+	if (action == SEARCH_COMPLETE)
+		ahc_release_untagged_queues(ahc);
+	return (found);
+}
+
+int
+ahc_search_untagged_queues(struct ahc_softc *ahc, ahc_io_ctx_t ctx,
+			   int target, char channel, int lun, uint32_t status,
+			   ahc_search_action action)
+{
+	struct	scb *scb;
+	int	maxtarget;
+	int	found;
+	int	i;
+
+	if (action == SEARCH_COMPLETE) {
+		/*
+		 * Don't attempt to run any queued untagged transactions
+		 * until we are done with the abort process.
+		 */
+		ahc_freeze_untagged_queues(ahc);
+	}
+
+	found = 0;
+	i = 0;
+	if ((ahc->flags & AHC_SCB_BTT) == 0) {
+
+		maxtarget = 16;
+		if (target != CAM_TARGET_WILDCARD) {
+
+			i = target;
+			if (channel == 'B')
+				i += 8;
+			maxtarget = i + 1;
+		}
+	} else {
+		maxtarget = 0;
+	}
+
+	for (; i < maxtarget; i++) {
+		struct scb_tailq *untagged_q;
+		struct scb *next_scb;
+
+		untagged_q = &(ahc->untagged_queues[i]);
+		next_scb = TAILQ_FIRST(untagged_q);
+		while (next_scb != NULL) {
+
+			scb = next_scb;
+			next_scb = TAILQ_NEXT(scb, links.tqe);
+
+			/*
+			 * The head of the list may be the currently
+			 * active untagged command for a device.
+			 * We're only searching for commands that
+			 * have not been started.  A transaction
+			 * marked active but still in the qinfifo
+			 * is removed by the qinfifo scanning code
+			 * above.
+			 */
+			if ((scb->flags & SCB_ACTIVE) != 0)
+				continue;
+
+			if (ahc_match_scb(ahc, scb, target, channel, lun,
+					  SCB_LIST_NULL, ROLE_INITIATOR) == 0
+			 || (ctx != NULL && ctx != scb->io_ctx))
+				continue;
+
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+			{
+				cam_status ostat;
+				cam_status cstat;
+
+				ostat = ahc_get_transaction_status(scb);
+				if (ostat == CAM_REQ_INPROG)
+					ahc_set_transaction_status(scb, status);
+				cstat = ahc_get_transaction_status(scb);
+				if (cstat != CAM_REQ_CMP)
+					ahc_freeze_scb(scb);
+				if ((scb->flags & SCB_ACTIVE) == 0)
+					printk("Inactive SCB in untaggedQ\n");
+				ahc_done(ahc, scb);
+				break;
+			}
+			case SEARCH_REMOVE:
+				scb->flags &= ~SCB_UNTAGGEDQ;
+				TAILQ_REMOVE(untagged_q, scb, links.tqe);
+				break;
+			case SEARCH_COUNT:
+				break;
+			}
+		}
+	}
+
+	if (action == SEARCH_COMPLETE)
+		ahc_release_untagged_queues(ahc);
+	return (found);
+}
+
+int
+ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
+		     int lun, u_int tag, int stop_on_first, int remove,
+		     int save_state)
+{
+	struct	scb *scbp;
+	u_int	next;
+	u_int	prev;
+	u_int	count;
+	u_int	active_scb;
+
+	count = 0;
+	next = ahc_inb(ahc, DISCONNECTED_SCBH);
+	prev = SCB_LIST_NULL;
+
+	if (save_state) {
+		/* restore this when we're done */
+		active_scb = ahc_inb(ahc, SCBPTR);
+	} else
+		/* Silence compiler */
+		active_scb = SCB_LIST_NULL;
+
+	while (next != SCB_LIST_NULL) {
+		u_int scb_index;
+
+		ahc_outb(ahc, SCBPTR, next);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		if (scb_index >= ahc->scb_data->numscbs) {
+			printk("Disconnected List inconsistency. "
+			       "SCB index == %d, yet numscbs == %d.",
+			       scb_index, ahc->scb_data->numscbs);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+		}
+
+		if (next == prev) {
+			panic("Disconnected List Loop. "
+			      "cur SCBPTR == %x, prev SCBPTR == %x.",
+			      next, prev);
+		}
+		scbp = ahc_lookup_scb(ahc, scb_index);
+		if (ahc_match_scb(ahc, scbp, target, channel, lun,
+				  tag, ROLE_INITIATOR)) {
+			count++;
+			if (remove) {
+				next =
+				    ahc_rem_scb_from_disc_list(ahc, prev, next);
+			} else {
+				prev = next;
+				next = ahc_inb(ahc, SCB_NEXT);
+			}
+			if (stop_on_first)
+				break;
+		} else {
+			prev = next;
+			next = ahc_inb(ahc, SCB_NEXT);
+		}
+	}
+	if (save_state)
+		ahc_outb(ahc, SCBPTR, active_scb);
+	return (count);
+}
+
+/*
+ * Remove an SCB from the on chip list of disconnected transactions.
+ * This is empty/unused if we are not performing SCB paging.
+ */
+static u_int
+ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
+{
+	u_int next;
+
+	ahc_outb(ahc, SCBPTR, scbptr);
+	next = ahc_inb(ahc, SCB_NEXT);
+
+	ahc_outb(ahc, SCB_CONTROL, 0);
+
+	ahc_add_curscb_to_free_list(ahc);
+
+	if (prev != SCB_LIST_NULL) {
+		ahc_outb(ahc, SCBPTR, prev);
+		ahc_outb(ahc, SCB_NEXT, next);
+	} else
+		ahc_outb(ahc, DISCONNECTED_SCBH, next);
+
+	return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs.  This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
+{
+	/*
+	 * Invalidate the tag so that our abort
+	 * routines don't think it's active.
+	 */
+	ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
+		ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+	}
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
+{
+	u_int curscb, next;
+
+	/*
+	 * Select the SCB we want to abort and
+	 * pull the next pointer out of it.
+	 */
+	curscb = ahc_inb(ahc, SCBPTR);
+	ahc_outb(ahc, SCBPTR, scbpos);
+	next = ahc_inb(ahc, SCB_NEXT);
+
+	/* Clear the necessary fields */
+	ahc_outb(ahc, SCB_CONTROL, 0);
+
+	ahc_add_curscb_to_free_list(ahc);
+
+	/* update the waiting list */
+	if (prev == SCB_LIST_NULL) {
+		/* First in the list */
+		ahc_outb(ahc, WAITING_SCBH, next); 
+
+		/*
+		 * Ensure we aren't attempting to perform
+		 * selection for this entry.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else {
+		/*
+		 * Select the scb that pointed to us 
+		 * and update its next pointer.
+		 */
+		ahc_outb(ahc, SCBPTR, prev);
+		ahc_outb(ahc, SCB_NEXT, next);
+	}
+
+	/*
+	 * Point us back at the original scb position.
+	 */
+	ahc_outb(ahc, SCBPTR, curscb);
+	return next;
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG.  This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+static int
+ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+	       int lun, u_int tag, role_t role, uint32_t status)
+{
+	struct	scb *scbp;
+	struct	scb *scbp_next;
+	u_int	active_scb;
+	int	i, j;
+	int	maxtarget;
+	int	minlun;
+	int	maxlun;
+
+	int	found;
+
+	/*
+	 * Don't attempt to run any queued untagged transactions
+	 * until we are done with the abort process.
+	 */
+	ahc_freeze_untagged_queues(ahc);
+
+	/* restore this when we're done */
+	active_scb = ahc_inb(ahc, SCBPTR);
+
+	found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
+				   role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+	/*
+	 * Clean out the busy target table for any untagged commands.
+	 */
+	i = 0;
+	maxtarget = 16;
+	if (target != CAM_TARGET_WILDCARD) {
+		i = target;
+		if (channel == 'B')
+			i += 8;
+		maxtarget = i + 1;
+	}
+
+	if (lun == CAM_LUN_WILDCARD) {
+
+		/*
+		 * Unless we are using an SCB based
+		 * busy targets table, there is only
+		 * one table entry for all luns of
+		 * a target.
+		 */
+		minlun = 0;
+		maxlun = 1;
+		if ((ahc->flags & AHC_SCB_BTT) != 0)
+			maxlun = AHC_NUM_LUNS;
+	} else {
+		minlun = lun;
+		maxlun = lun + 1;
+	}
+
+	if (role != ROLE_TARGET) {
+		for (;i < maxtarget; i++) {
+			for (j = minlun;j < maxlun; j++) {
+				u_int scbid;
+				u_int tcl;
+
+				tcl = BUILD_TCL(i << 4, j);
+				scbid = ahc_index_busy_tcl(ahc, tcl);
+				scbp = ahc_lookup_scb(ahc, scbid);
+				if (scbp == NULL
+				 || ahc_match_scb(ahc, scbp, target, channel,
+						  lun, tag, role) == 0)
+					continue;
+				ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, j));
+			}
+		}
+
+		/*
+		 * Go through the disconnected list and remove any entries we
+		 * have queued for completion, 0'ing their control byte too.
+		 * We save the active SCB and restore it ourselves, so there
+		 * is no reason for this search to restore it too.
+		 */
+		ahc_search_disc_list(ahc, target, channel, lun, tag,
+				     /*stop_on_first*/FALSE, /*remove*/TRUE,
+				     /*save_state*/FALSE);
+	}
+
+	/*
+	 * Go through the hardware SCB array looking for commands that
+	 * were active but not on any list.  In some cases, these remnants
+	 * might not still have mappings in the scbindex array (e.g. unexpected
+	 * bus free with the same scb queued for an abort).  Don't hold this
+	 * against them.
+	 */
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		u_int scbid;
+
+		ahc_outb(ahc, SCBPTR, i);
+		scbid = ahc_inb(ahc, SCB_TAG);
+		scbp = ahc_lookup_scb(ahc, scbid);
+		if ((scbp == NULL && scbid != SCB_LIST_NULL)
+		 || (scbp != NULL
+		  && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)))
+			ahc_add_curscb_to_free_list(ahc);
+	}
+
+	/*
+	 * Go through the pending CCB list and look for
+	 * commands for this target that are still active.
+	 * These are other tagged commands that were
+	 * disconnected when the reset occurred.
+	 */
+	scbp_next = LIST_FIRST(&ahc->pending_scbs);
+	while (scbp_next != NULL) {
+		scbp = scbp_next;
+		scbp_next = LIST_NEXT(scbp, pending_links);
+		if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) {
+			cam_status ostat;
+
+			ostat = ahc_get_transaction_status(scbp);
+			if (ostat == CAM_REQ_INPROG)
+				ahc_set_transaction_status(scbp, status);
+			if (ahc_get_transaction_status(scbp) != CAM_REQ_CMP)
+				ahc_freeze_scb(scbp);
+			if ((scbp->flags & SCB_ACTIVE) == 0)
+				printk("Inactive SCB on pending list\n");
+			ahc_done(ahc, scbp);
+			found++;
+		}
+	}
+	ahc_outb(ahc, SCBPTR, active_scb);
+	ahc_platform_abort_scbs(ahc, target, channel, lun, tag, role, status);
+	ahc_release_untagged_queues(ahc);
+	return found;
+}
+
+static void
+ahc_reset_current_bus(struct ahc_softc *ahc)
+{
+	uint8_t scsiseq;
+
+	ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
+	scsiseq = ahc_inb(ahc, SCSISEQ);
+	ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
+	ahc_flush_device_writes(ahc);
+	ahc_delay(AHC_BUSRESET_DELAY);
+	/* Turn off the bus reset */
+	ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
+
+	ahc_clear_intstat(ahc);
+
+	/* Re-enable reset interrupts */
+	ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
+}
+
+int
+ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
+{
+	struct	ahc_devinfo devinfo;
+	u_int	initiator, target, max_scsiid;
+	u_int	sblkctl;
+	u_int	scsiseq;
+	u_int	simode1;
+	int	found;
+	int	restart_needed;
+	char	cur_channel;
+
+	ahc->pending_device = NULL;
+
+	ahc_compile_devinfo(&devinfo,
+			    CAM_TARGET_WILDCARD,
+			    CAM_TARGET_WILDCARD,
+			    CAM_LUN_WILDCARD,
+			    channel, ROLE_UNKNOWN);
+	ahc_pause(ahc);
+
+	/* Make sure the sequencer is in a safe location. */
+	ahc_clear_critical_section(ahc);
+
+	/*
+	 * Run our command complete fifos to ensure that we perform
+	 * completion processing on any commands that 'completed'
+	 * before the reset occurred.
+	 */
+	ahc_run_qoutfifo(ahc);
+#ifdef AHC_TARGET_MODE
+	/*
+	 * XXX - In Twin mode, the tqinfifo may have commands
+	 *	 for an unaffected channel in it.  However, if
+	 *	 we have run out of ATIO resources to drain that
+	 *	 queue, we may not get them all out here.  Further,
+	 *	 the blocked transactions for the reset channel
+	 *	 should just be killed off, irrespecitve of whether
+	 *	 we are blocked on ATIO resources.  Write a routine
+	 *	 to compact the tqinfifo appropriately.
+	 */
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		ahc_run_tqinfifo(ahc, /*paused*/TRUE);
+	}
+#endif
+
+	/*
+	 * Reset the bus if we are initiating this reset
+	 */
+	sblkctl = ahc_inb(ahc, SBLKCTL);
+	cur_channel = 'A';
+	if ((ahc->features & AHC_TWIN) != 0
+	 && ((sblkctl & SELBUSB) != 0))
+	    cur_channel = 'B';
+	scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+	if (cur_channel != channel) {
+		/* Case 1: Command for another bus is active
+		 * Stealthily reset the other bus without
+		 * upsetting the current bus.
+		 */
+		ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
+		simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#ifdef AHC_TARGET_MODE
+		/*
+		 * Bus resets clear ENSELI, so we cannot
+		 * defer re-enabling bus reset interrupts
+		 * if we are in target mode.
+		 */
+		if ((ahc->flags & AHC_TARGETROLE) != 0)
+			simode1 |= ENSCSIRST;
+#endif
+		ahc_outb(ahc, SIMODE1, simode1);
+		if (initiate_reset)
+			ahc_reset_current_bus(ahc);
+		ahc_clear_intstat(ahc);
+		ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+		ahc_outb(ahc, SBLKCTL, sblkctl);
+		restart_needed = FALSE;
+	} else {
+		/* Case 2: A command from this bus is active or we're idle */
+		simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#ifdef AHC_TARGET_MODE
+		/*
+		 * Bus resets clear ENSELI, so we cannot
+		 * defer re-enabling bus reset interrupts
+		 * if we are in target mode.
+		 */
+		if ((ahc->flags & AHC_TARGETROLE) != 0)
+			simode1 |= ENSCSIRST;
+#endif
+		ahc_outb(ahc, SIMODE1, simode1);
+		if (initiate_reset)
+			ahc_reset_current_bus(ahc);
+		ahc_clear_intstat(ahc);
+		ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+		restart_needed = TRUE;
+	}
+
+	/*
+	 * Clean up all the state information for the
+	 * pending transactions on this bus.
+	 */
+	found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel,
+			       CAM_LUN_WILDCARD, SCB_LIST_NULL,
+			       ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+	max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
+
+#ifdef AHC_TARGET_MODE
+	/*
+	 * Send an immediate notify ccb to all target more peripheral
+	 * drivers affected by this action.
+	 */
+	for (target = 0; target <= max_scsiid; target++) {
+		struct ahc_tmode_tstate* tstate;
+		u_int lun;
+
+		tstate = ahc->enabled_targets[target];
+		if (tstate == NULL)
+			continue;
+		for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+			struct ahc_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[lun];
+			if (lstate == NULL)
+				continue;
+
+			ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD,
+					       EVENT_TYPE_BUS_RESET, /*arg*/0);
+			ahc_send_lstate_events(ahc, lstate);
+		}
+	}
+#endif
+	/* Notify the XPT that a bus reset occurred */
+	ahc_send_async(ahc, devinfo.channel, CAM_TARGET_WILDCARD,
+		       CAM_LUN_WILDCARD, AC_BUS_RESET);
+
+	/*
+	 * Revert to async/narrow transfers until we renegotiate.
+	 */
+	for (target = 0; target <= max_scsiid; target++) {
+
+		if (ahc->enabled_targets[target] == NULL)
+			continue;
+		for (initiator = 0; initiator <= max_scsiid; initiator++) {
+			struct ahc_devinfo devinfo;
+
+			ahc_compile_devinfo(&devinfo, target, initiator,
+					    CAM_LUN_WILDCARD,
+					    channel, ROLE_UNKNOWN);
+			ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+				      AHC_TRANS_CUR, /*paused*/TRUE);
+			ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
+					 /*period*/0, /*offset*/0,
+					 /*ppr_options*/0, AHC_TRANS_CUR,
+					 /*paused*/TRUE);
+		}
+	}
+
+	if (restart_needed)
+		ahc_restart(ahc);
+	else
+		ahc_unpause(ahc);
+	return found;
+}
+
+
+/***************************** Residual Processing ****************************/
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+static void
+ahc_calc_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct hardware_scb *hscb;
+	struct status_pkt *spkt;
+	uint32_t sgptr;
+	uint32_t resid_sgptr;
+	uint32_t resid;
+
+	/*
+	 * 5 cases.
+	 * 1) No residual.
+	 *    SG_RESID_VALID clear in sgptr.
+	 * 2) Transferless command
+	 * 3) Never performed any transfers.
+	 *    sgptr has SG_FULL_RESID set.
+	 * 4) No residual but target did not
+	 *    save data pointers after the
+	 *    last transfer, so sgptr was
+	 *    never updated.
+	 * 5) We have a partial residual.
+	 *    Use residual_sgptr to determine
+	 *    where we are.
+	 */
+
+	hscb = scb->hscb;
+	sgptr = ahc_le32toh(hscb->sgptr);
+	if ((sgptr & SG_RESID_VALID) == 0)
+		/* Case 1 */
+		return;
+	sgptr &= ~SG_RESID_VALID;
+
+	if ((sgptr & SG_LIST_NULL) != 0)
+		/* Case 2 */
+		return;
+
+	spkt = &hscb->shared_data.status;
+	resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);
+	if ((sgptr & SG_FULL_RESID) != 0) {
+		/* Case 3 */
+		resid = ahc_get_transfer_length(scb);
+	} else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+		/* Case 4 */
+		return;
+	} else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+		panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+	} else {
+		struct ahc_dma_seg *sg;
+
+		/*
+		 * Remainder of the SG where the transfer
+		 * stopped.  
+		 */
+		resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
+		sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
+
+		/* The residual sg_ptr always points to the next sg */
+		sg--;
+
+		/*
+		 * Add up the contents of all residual
+		 * SG segments that are after the SG where
+		 * the transfer stopped.
+		 */
+		while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {
+			sg++;
+			resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+		}
+	}
+	if ((scb->flags & SCB_SENSE) == 0)
+		ahc_set_residual(scb, resid);
+	else
+		ahc_set_sense_residual(scb, resid);
+
+#ifdef AHC_DEBUG
+	if ((ahc_debug & AHC_SHOW_MISC) != 0) {
+		ahc_print_path(ahc, scb);
+		printk("Handled %sResidual of %d bytes\n",
+		       (scb->flags & SCB_SENSE) ? "Sense " : "", resid);
+	}
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHC_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
+		       u_int initiator_id, u_int event_type, u_int event_arg)
+{
+	struct ahc_tmode_event *event;
+	int pending;
+
+	xpt_freeze_devq(lstate->path, /*count*/1);
+	if (lstate->event_w_idx >= lstate->event_r_idx)
+		pending = lstate->event_w_idx - lstate->event_r_idx;
+	else
+		pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
+			- (lstate->event_r_idx - lstate->event_w_idx);
+
+	if (event_type == EVENT_TYPE_BUS_RESET
+	 || event_type == MSG_BUS_DEV_RESET) {
+		/*
+		 * Any earlier events are irrelevant, so reset our buffer.
+		 * This has the effect of allowing us to deal with reset
+		 * floods (an external device holding down the reset line)
+		 * without losing the event that is really interesting.
+		 */
+		lstate->event_r_idx = 0;
+		lstate->event_w_idx = 0;
+		xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+	}
+
+	if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
+		xpt_print_path(lstate->path);
+		printk("immediate event %x:%x lost\n",
+		       lstate->event_buffer[lstate->event_r_idx].event_type,
+		       lstate->event_buffer[lstate->event_r_idx].event_arg);
+		lstate->event_r_idx++;
+		if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+			lstate->event_r_idx = 0;
+		xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+	}
+
+	event = &lstate->event_buffer[lstate->event_w_idx];
+	event->initiator_id = initiator_id;
+	event->event_type = event_type;
+	event->event_arg = event_arg;
+	lstate->event_w_idx++;
+	if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+		lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahc_send_lstate_events(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate)
+{
+	struct ccb_hdr *ccbh;
+	struct ccb_immed_notify *inot;
+
+	while (lstate->event_r_idx != lstate->event_w_idx
+	    && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+		struct ahc_tmode_event *event;
+
+		event = &lstate->event_buffer[lstate->event_r_idx];
+		SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+		inot = (struct ccb_immed_notify *)ccbh;
+		switch (event->event_type) {
+		case EVENT_TYPE_BUS_RESET:
+			ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+			break;
+		default:
+			ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+			inot->message_args[0] = event->event_type;
+			inot->message_args[1] = event->event_arg;
+			break;
+		}
+		inot->initiator_id = event->initiator_id;
+		inot->sense_len = 0;
+		xpt_done((union ccb *)inot);
+		lstate->event_r_idx++;
+		if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+			lstate->event_r_idx = 0;
+	}
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHC_DUMP_SEQ
+void
+ahc_dumpseq(struct ahc_softc* ahc)
+{
+	int i;
+
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+	for (i = 0; i < ahc->instruction_ram_size; i++) {
+		uint8_t ins_bytes[4];
+
+		ahc_insb(ahc, SEQRAM, ins_bytes, 4);
+		printk("0x%08x\n", ins_bytes[0] << 24
+				 | ins_bytes[1] << 16
+				 | ins_bytes[2] << 8
+				 | ins_bytes[3]);
+	}
+}
+#endif
+
+static int
+ahc_loadseq(struct ahc_softc *ahc)
+{
+	struct	cs cs_table[num_critical_sections];
+	u_int	begin_set[num_critical_sections];
+	u_int	end_set[num_critical_sections];
+	const struct patch *cur_patch;
+	u_int	cs_count;
+	u_int	cur_cs;
+	u_int	i;
+	u_int	skip_addr;
+	u_int	sg_prefetch_cnt;
+	int	downloaded;
+	uint8_t	download_consts[7];
+
+	/*
+	 * Start out with 0 critical sections
+	 * that apply to this firmware load.
+	 */
+	cs_count = 0;
+	cur_cs = 0;
+	memset(begin_set, 0, sizeof(begin_set));
+	memset(end_set, 0, sizeof(end_set));
+
+	/* Setup downloadable constant table */
+	download_consts[QOUTFIFO_OFFSET] = 0;
+	if (ahc->targetcmds != NULL)
+		download_consts[QOUTFIFO_OFFSET] += 32;
+	download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1;
+	download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1;
+	download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1);
+	sg_prefetch_cnt = ahc->pci_cachesize;
+	if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg)))
+		sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg);
+	download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+	download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1);
+	download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1);
+
+	cur_patch = patches;
+	downloaded = 0;
+	skip_addr = 0;
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+
+	for (i = 0; i < sizeof(seqprog)/4; i++) {
+		if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
+			/*
+			 * Don't download this instruction as it
+			 * is in a patch that was removed.
+			 */
+			continue;
+		}
+
+		if (downloaded == ahc->instruction_ram_size) {
+			/*
+			 * We're about to exceed the instruction
+			 * storage capacity for this chip.  Fail
+			 * the load.
+			 */
+			printk("\n%s: Program too large for instruction memory "
+			       "size of %d!\n", ahc_name(ahc),
+			       ahc->instruction_ram_size);
+			return (ENOMEM);
+		}
+
+		/*
+		 * Move through the CS table until we find a CS
+		 * that might apply to this instruction.
+		 */
+		for (; cur_cs < num_critical_sections; cur_cs++) {
+			if (critical_sections[cur_cs].end <= i) {
+				if (begin_set[cs_count] == TRUE
+				 && end_set[cs_count] == FALSE) {
+					cs_table[cs_count].end = downloaded;
+				 	end_set[cs_count] = TRUE;
+					cs_count++;
+				}
+				continue;
+			}
+			if (critical_sections[cur_cs].begin <= i
+			 && begin_set[cs_count] == FALSE) {
+				cs_table[cs_count].begin = downloaded;
+				begin_set[cs_count] = TRUE;
+			}
+			break;
+		}
+		ahc_download_instr(ahc, i, download_consts);
+		downloaded++;
+	}
+
+	ahc->num_critical_sections = cs_count;
+	if (cs_count != 0) {
+
+		cs_count *= sizeof(struct cs);
+		ahc->critical_sections = kmalloc(cs_count, GFP_ATOMIC);
+		if (ahc->critical_sections == NULL)
+			panic("ahc_loadseq: Could not malloc");
+		memcpy(ahc->critical_sections, cs_table, cs_count);
+	}
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+
+	if (bootverbose) {
+		printk(" %d instructions downloaded\n", downloaded);
+		printk("%s: Features 0x%x, Bugs 0x%x, Flags 0x%x\n",
+		       ahc_name(ahc), ahc->features, ahc->bugs, ahc->flags);
+	}
+	return (0);
+}
+
+static int
+ahc_check_patch(struct ahc_softc *ahc, const struct patch **start_patch,
+		u_int start_instr, u_int *skip_addr)
+{
+	const struct patch *cur_patch;
+	const struct patch *last_patch;
+	u_int	num_patches;
+
+	num_patches = ARRAY_SIZE(patches);
+	last_patch = &patches[num_patches];
+	cur_patch = *start_patch;
+
+	while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+		if (cur_patch->patch_func(ahc) == 0) {
+
+			/* Start rejecting code */
+			*skip_addr = start_instr + cur_patch->skip_instr;
+			cur_patch += cur_patch->skip_patch;
+		} else {
+			/* Accepted this patch.  Advance to the next
+			 * one and wait for our intruction pointer to
+			 * hit this point.
+			 */
+			cur_patch++;
+		}
+	}
+
+	*start_patch = cur_patch;
+	if (start_instr < *skip_addr)
+		/* Still skipping */
+		return (0);
+
+	return (1);
+}
+
+static void
+ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
+{
+	union	ins_formats instr;
+	struct	ins_format1 *fmt1_ins;
+	struct	ins_format3 *fmt3_ins;
+	u_int	opcode;
+
+	/*
+	 * The firmware is always compiled into a little endian format.
+	 */
+	instr.integer = ahc_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+	fmt1_ins = &instr.format1;
+	fmt3_ins = NULL;
+
+	/* Pull the opcode */
+	opcode = instr.format1.opcode;
+	switch (opcode) {
+	case AIC_OP_JMP:
+	case AIC_OP_JC:
+	case AIC_OP_JNC:
+	case AIC_OP_CALL:
+	case AIC_OP_JNE:
+	case AIC_OP_JNZ:
+	case AIC_OP_JE:
+	case AIC_OP_JZ:
+	{
+		const struct patch *cur_patch;
+		int address_offset;
+		u_int address;
+		u_int skip_addr;
+		u_int i;
+
+		fmt3_ins = &instr.format3;
+		address_offset = 0;
+		address = fmt3_ins->address;
+		cur_patch = patches;
+		skip_addr = 0;
+
+		for (i = 0; i < address;) {
+
+			ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
+
+			if (skip_addr > i) {
+				int end_addr;
+
+				end_addr = min(address, skip_addr);
+				address_offset += end_addr - i;
+				i = skip_addr;
+			} else {
+				i++;
+			}
+		}
+		address -= address_offset;
+		fmt3_ins->address = address;
+		/* FALLTHROUGH */
+	}
+	case AIC_OP_OR:
+	case AIC_OP_AND:
+	case AIC_OP_XOR:
+	case AIC_OP_ADD:
+	case AIC_OP_ADC:
+	case AIC_OP_BMOV:
+		if (fmt1_ins->parity != 0) {
+			fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+		}
+		fmt1_ins->parity = 0;
+		if ((ahc->features & AHC_CMD_CHAN) == 0
+		 && opcode == AIC_OP_BMOV) {
+			/*
+			 * Block move was added at the same time
+			 * as the command channel.  Verify that
+			 * this is only a move of a single element
+			 * and convert the BMOV to a MOV
+			 * (AND with an immediate of FF).
+			 */
+			if (fmt1_ins->immediate != 1)
+				panic("%s: BMOV not supported\n",
+				      ahc_name(ahc));
+			fmt1_ins->opcode = AIC_OP_AND;
+			fmt1_ins->immediate = 0xff;
+		}
+		/* FALLTHROUGH */
+	case AIC_OP_ROL:
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			int i, count;
+
+			/* Calculate odd parity for the instruction */
+			for (i = 0, count = 0; i < 31; i++) {
+				uint32_t mask;
+
+				mask = 0x01 << i;
+				if ((instr.integer & mask) != 0)
+					count++;
+			}
+			if ((count & 0x01) == 0)
+				instr.format1.parity = 1;
+		} else {
+			/* Compress the instruction for older sequencers */
+			if (fmt3_ins != NULL) {
+				instr.integer =
+					fmt3_ins->immediate
+				      | (fmt3_ins->source << 8)
+				      | (fmt3_ins->address << 16)
+				      |	(fmt3_ins->opcode << 25);
+			} else {
+				instr.integer =
+					fmt1_ins->immediate
+				      | (fmt1_ins->source << 8)
+				      | (fmt1_ins->destination << 16)
+				      |	(fmt1_ins->ret << 24)
+				      |	(fmt1_ins->opcode << 25);
+			}
+		}
+		/* The sequencer is a little endian cpu */
+		instr.integer = ahc_htole32(instr.integer);
+		ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+		break;
+	default:
+		panic("Unknown opcode encountered in seq program");
+		break;
+	}
+}
+
+int
+ahc_print_register(const ahc_reg_parse_entry_t *table, u_int num_entries,
+		   const char *name, u_int address, u_int value,
+		   u_int *cur_column, u_int wrap_point)
+{
+	int	printed;
+	u_int	printed_mask;
+
+	if (cur_column != NULL && *cur_column >= wrap_point) {
+		printk("\n");
+		*cur_column = 0;
+	}
+	printed  = printk("%s[0x%x]", name, value);
+	if (table == NULL) {
+		printed += printk(" ");
+		*cur_column += printed;
+		return (printed);
+	}
+	printed_mask = 0;
+	while (printed_mask != 0xFF) {
+		int entry;
+
+		for (entry = 0; entry < num_entries; entry++) {
+			if (((value & table[entry].mask)
+			  != table[entry].value)
+			 || ((printed_mask & table[entry].mask)
+			  == table[entry].mask))
+				continue;
+
+			printed += printk("%s%s",
+					  printed_mask == 0 ? ":(" : "|",
+					  table[entry].name);
+			printed_mask |= table[entry].mask;
+			
+			break;
+		}
+		if (entry >= num_entries)
+			break;
+	}
+	if (printed_mask != 0)
+		printed += printk(") ");
+	else
+		printed += printk(" ");
+	if (cur_column != NULL)
+		*cur_column += printed;
+	return (printed);
+}
+
+void
+ahc_dump_card_state(struct ahc_softc *ahc)
+{
+	struct	scb *scb;
+	struct	scb_tailq *untagged_q;
+	u_int	cur_col;
+	int	paused;
+	int	target;
+	int	maxtarget;
+	int	i;
+	uint8_t last_phase;
+	uint8_t qinpos;
+	uint8_t qintail;
+	uint8_t qoutpos;
+	uint8_t scb_index;
+	uint8_t saved_scbptr;
+
+	if (ahc_is_paused(ahc)) {
+		paused = 1;
+	} else {
+		paused = 0;
+		ahc_pause(ahc);
+	}
+
+	saved_scbptr = ahc_inb(ahc, SCBPTR);
+	last_phase = ahc_inb(ahc, LASTPHASE);
+	printk(">>>>>>>>>>>>>>>>>> Dump Card State Begins <<<<<<<<<<<<<<<<<\n"
+	       "%s: Dumping Card State %s, at SEQADDR 0x%x\n",
+	       ahc_name(ahc), ahc_lookup_phase_entry(last_phase)->phasemsg,
+	       ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+	if (paused)
+		printk("Card was paused\n");
+	printk("ACCUM = 0x%x, SINDEX = 0x%x, DINDEX = 0x%x, ARG_2 = 0x%x\n",
+	       ahc_inb(ahc, ACCUM), ahc_inb(ahc, SINDEX), ahc_inb(ahc, DINDEX),
+	       ahc_inb(ahc, ARG_2));
+	printk("HCNT = 0x%x SCBPTR = 0x%x\n", ahc_inb(ahc, HCNT),
+	       ahc_inb(ahc, SCBPTR));
+	cur_col = 0;
+	if ((ahc->features & AHC_DT) != 0)
+		ahc_scsiphase_print(ahc_inb(ahc, SCSIPHASE), &cur_col, 50);
+	ahc_scsisigi_print(ahc_inb(ahc, SCSISIGI), &cur_col, 50);
+	ahc_error_print(ahc_inb(ahc, ERROR), &cur_col, 50);
+	ahc_scsibusl_print(ahc_inb(ahc, SCSIBUSL), &cur_col, 50);
+	ahc_lastphase_print(ahc_inb(ahc, LASTPHASE), &cur_col, 50);
+	ahc_scsiseq_print(ahc_inb(ahc, SCSISEQ), &cur_col, 50);
+	ahc_sblkctl_print(ahc_inb(ahc, SBLKCTL), &cur_col, 50);
+	ahc_scsirate_print(ahc_inb(ahc, SCSIRATE), &cur_col, 50);
+	ahc_seqctl_print(ahc_inb(ahc, SEQCTL), &cur_col, 50);
+	ahc_seq_flags_print(ahc_inb(ahc, SEQ_FLAGS), &cur_col, 50);
+	ahc_sstat0_print(ahc_inb(ahc, SSTAT0), &cur_col, 50);
+	ahc_sstat1_print(ahc_inb(ahc, SSTAT1), &cur_col, 50);
+	ahc_sstat2_print(ahc_inb(ahc, SSTAT2), &cur_col, 50);
+	ahc_sstat3_print(ahc_inb(ahc, SSTAT3), &cur_col, 50);
+	ahc_simode0_print(ahc_inb(ahc, SIMODE0), &cur_col, 50);
+	ahc_simode1_print(ahc_inb(ahc, SIMODE1), &cur_col, 50);
+	ahc_sxfrctl0_print(ahc_inb(ahc, SXFRCTL0), &cur_col, 50);
+	ahc_dfcntrl_print(ahc_inb(ahc, DFCNTRL), &cur_col, 50);
+	ahc_dfstatus_print(ahc_inb(ahc, DFSTATUS), &cur_col, 50);
+	if (cur_col != 0)
+		printk("\n");
+	printk("STACK:");
+	for (i = 0; i < STACK_SIZE; i++)
+		printk(" 0x%x", ahc_inb(ahc, STACK)|(ahc_inb(ahc, STACK) << 8));
+	printk("\nSCB count = %d\n", ahc->scb_data->numscbs);
+	printk("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
+	printk("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
+	/* QINFIFO */
+	printk("QINFIFO entries: ");
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		qinpos = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinpos);
+	} else
+		qinpos = ahc_inb(ahc, QINPOS);
+	qintail = ahc->qinfifonext;
+	while (qinpos != qintail) {
+		printk("%d ", ahc->qinfifo[qinpos]);
+		qinpos++;
+	}
+	printk("\n");
+
+	printk("Waiting Queue entries: ");
+	scb_index = ahc_inb(ahc, WAITING_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printk("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printk("\n");
+
+	printk("Disconnected Queue entries: ");
+	scb_index = ahc_inb(ahc, DISCONNECTED_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printk("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printk("\n");
+		
+	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+	printk("QOUTFIFO entries: ");
+	qoutpos = ahc->qoutfifonext;
+	i = 0;
+	while (ahc->qoutfifo[qoutpos] != SCB_LIST_NULL && i++ < 256) {
+		printk("%d ", ahc->qoutfifo[qoutpos]);
+		qoutpos++;
+	}
+	printk("\n");
+
+	printk("Sequencer Free SCB List: ");
+	scb_index = ahc_inb(ahc, FREE_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printk("%d ", scb_index);
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printk("\n");
+
+	printk("Sequencer SCB Info: ");
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		ahc_outb(ahc, SCBPTR, i);
+		cur_col  = printk("\n%3d ", i);
+
+		ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL), &cur_col, 60);
+		ahc_scb_scsiid_print(ahc_inb(ahc, SCB_SCSIID), &cur_col, 60);
+		ahc_scb_lun_print(ahc_inb(ahc, SCB_LUN), &cur_col, 60);
+		ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+	}
+	printk("\n");
+
+	printk("Pending list: ");
+	i = 0;
+	LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+		if (i++ > 256)
+			break;
+		cur_col  = printk("\n%3d ", scb->hscb->tag);
+		ahc_scb_control_print(scb->hscb->control, &cur_col, 60);
+		ahc_scb_scsiid_print(scb->hscb->scsiid, &cur_col, 60);
+		ahc_scb_lun_print(scb->hscb->lun, &cur_col, 60);
+		if ((ahc->flags & AHC_PAGESCBS) == 0) {
+			ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+			printk("(");
+			ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL),
+					      &cur_col, 60);
+			ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+			printk(")");
+		}
+	}
+	printk("\n");
+
+	printk("Kernel Free SCB list: ");
+	i = 0;
+	SLIST_FOREACH(scb, &ahc->scb_data->free_scbs, links.sle) {
+		if (i++ > 256)
+			break;
+		printk("%d ", scb->hscb->tag);
+	}
+	printk("\n");
+
+	maxtarget = (ahc->features & (AHC_WIDE|AHC_TWIN)) ? 15 : 7;
+	for (target = 0; target <= maxtarget; target++) {
+		untagged_q = &ahc->untagged_queues[target];
+		if (TAILQ_FIRST(untagged_q) == NULL)
+			continue;
+		printk("Untagged Q(%d): ", target);
+		i = 0;
+		TAILQ_FOREACH(scb, untagged_q, links.tqe) {
+			if (i++ > 256)
+				break;
+			printk("%d ", scb->hscb->tag);
+		}
+		printk("\n");
+	}
+
+	ahc_platform_dump_card_state(ahc);
+	printk("\n<<<<<<<<<<<<<<<<< Dump Card State Ends >>>>>>>>>>>>>>>>>>\n");
+	ahc_outb(ahc, SCBPTR, saved_scbptr);
+	if (paused == 0)
+		ahc_unpause(ahc);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHC_TARGET_MODE
+cam_status
+ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
+		    struct ahc_tmode_tstate **tstate,
+		    struct ahc_tmode_lstate **lstate,
+		    int notfound_failure)
+{
+
+	if ((ahc->features & AHC_TARGETMODE) == 0)
+		return (CAM_REQ_INVALID);
+
+	/*
+	 * Handle the 'black hole' device that sucks up
+	 * requests to unattached luns on enabled targets.
+	 */
+	if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+	 && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+		*tstate = NULL;
+		*lstate = ahc->black_hole;
+	} else {
+		u_int max_id;
+
+		max_id = (ahc->features & AHC_WIDE) ? 16 : 8;
+		if (ccb->ccb_h.target_id >= max_id)
+			return (CAM_TID_INVALID);
+
+		if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
+			return (CAM_LUN_INVALID);
+
+		*tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+		*lstate = NULL;
+		if (*tstate != NULL)
+			*lstate =
+			    (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+	}
+
+	if (notfound_failure != 0 && *lstate == NULL)
+		return (CAM_PATH_INVALID);
+
+	return (CAM_REQ_CMP);
+}
+
+void
+ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+	struct	   ahc_tmode_tstate *tstate;
+	struct	   ahc_tmode_lstate *lstate;
+	struct	   ccb_en_lun *cel;
+	cam_status status;
+	u_long	   s;
+	u_int	   target;
+	u_int	   lun;
+	u_int	   target_mask;
+	u_int	   our_id;
+	int	   error;
+	char	   channel;
+
+	status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+				     /*notfound_failure*/FALSE);
+
+	if (status != CAM_REQ_CMP) {
+		ccb->ccb_h.status = status;
+		return;
+	}
+
+	if (cam_sim_bus(sim) == 0)
+		our_id = ahc->our_id;
+	else
+		our_id = ahc->our_id_b;
+
+	if (ccb->ccb_h.target_id != our_id) {
+		/*
+		 * our_id represents our initiator ID, or
+		 * the ID of the first target to have an
+		 * enabled lun in target mode.  There are
+		 * two cases that may preclude enabling a
+		 * target id other than our_id.
+		 *
+		 *   o our_id is for an active initiator role.
+		 *     Since the hardware does not support
+		 *     reselections to the initiator role at
+		 *     anything other than our_id, and our_id
+		 *     is used by the hardware to indicate the
+		 *     ID to use for both select-out and
+		 *     reselect-out operations, the only target
+		 *     ID we can support in this mode is our_id.
+		 *
+		 *   o The MULTARGID feature is not available and
+		 *     a previous target mode ID has been enabled.
+		 */
+		if ((ahc->features & AHC_MULTIROLE) != 0) {
+
+			if ((ahc->features & AHC_MULTI_TID) != 0
+		   	 && (ahc->flags & AHC_INITIATORROLE) != 0) {
+				/*
+				 * Only allow additional targets if
+				 * the initiator role is disabled.
+				 * The hardware cannot handle a re-select-in
+				 * on the initiator id during a re-select-out
+				 * on a different target id.
+				 */
+				status = CAM_TID_INVALID;
+			} else if ((ahc->flags & AHC_INITIATORROLE) != 0
+				|| ahc->enabled_luns > 0) {
+				/*
+				 * Only allow our target id to change
+				 * if the initiator role is not configured
+				 * and there are no enabled luns which
+				 * are attached to the currently registered
+				 * scsi id.
+				 */
+				status = CAM_TID_INVALID;
+			}
+		} else if ((ahc->features & AHC_MULTI_TID) == 0
+			&& ahc->enabled_luns > 0) {
+
+			status = CAM_TID_INVALID;
+		}
+	}
+
+	if (status != CAM_REQ_CMP) {
+		ccb->ccb_h.status = status;
+		return;
+	}
+
+	/*
+	 * We now have an id that is valid.
+	 * If we aren't in target mode, switch modes.
+	 */
+	if ((ahc->flags & AHC_TARGETROLE) == 0
+	 && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+		u_long	 s;
+		ahc_flag saved_flags;
+
+		printk("Configuring Target Mode\n");
+		ahc_lock(ahc, &s);
+		if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+			ccb->ccb_h.status = CAM_BUSY;
+			ahc_unlock(ahc, &s);
+			return;
+		}
+		saved_flags = ahc->flags;
+		ahc->flags |= AHC_TARGETROLE;
+		if ((ahc->features & AHC_MULTIROLE) == 0)
+			ahc->flags &= ~AHC_INITIATORROLE;
+		ahc_pause(ahc);
+		error = ahc_loadseq(ahc);
+		if (error != 0) {
+			/*
+			 * Restore original configuration and notify
+			 * the caller that we cannot support target mode.
+			 * Since the adapter started out in this
+			 * configuration, the firmware load will succeed,
+			 * so there is no point in checking ahc_loadseq's
+			 * return value.
+			 */
+			ahc->flags = saved_flags;
+			(void)ahc_loadseq(ahc);
+			ahc_restart(ahc);
+			ahc_unlock(ahc, &s);
+			ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+			return;
+		}
+		ahc_restart(ahc);
+		ahc_unlock(ahc, &s);
+	}
+	cel = &ccb->cel;
+	target = ccb->ccb_h.target_id;
+	lun = ccb->ccb_h.target_lun;
+	channel = SIM_CHANNEL(ahc, sim);
+	target_mask = 0x01 << target;
+	if (channel == 'B')
+		target_mask <<= 8;
+
+	if (cel->enable != 0) {
+		u_int scsiseq;
+
+		/* Are we already enabled?? */
+		if (lstate != NULL) {
+			xpt_print_path(ccb->ccb_h.path);
+			printk("Lun already enabled\n");
+			ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+			return;
+		}
+
+		if (cel->grp6_len != 0
+		 || cel->grp7_len != 0) {
+			/*
+			 * Don't (yet?) support vendor
+			 * specific commands.
+			 */
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			printk("Non-zero Group Codes\n");
+			return;
+		}
+
+		/*
+		 * Seems to be okay.
+		 * Setup our data structures.
+		 */
+		if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+			tstate = ahc_alloc_tstate(ahc, target, channel);
+			if (tstate == NULL) {
+				xpt_print_path(ccb->ccb_h.path);
+				printk("Couldn't allocate tstate\n");
+				ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+				return;
+			}
+		}
+		lstate = kzalloc(sizeof(*lstate), GFP_ATOMIC);
+		if (lstate == NULL) {
+			xpt_print_path(ccb->ccb_h.path);
+			printk("Couldn't allocate lstate\n");
+			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+			return;
+		}
+		status = xpt_create_path(&lstate->path, /*periph*/NULL,
+					 xpt_path_path_id(ccb->ccb_h.path),
+					 xpt_path_target_id(ccb->ccb_h.path),
+					 xpt_path_lun_id(ccb->ccb_h.path));
+		if (status != CAM_REQ_CMP) {
+			kfree(lstate);
+			xpt_print_path(ccb->ccb_h.path);
+			printk("Couldn't allocate path\n");
+			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+			return;
+		}
+		SLIST_INIT(&lstate->accept_tios);
+		SLIST_INIT(&lstate->immed_notifies);
+		ahc_lock(ahc, &s);
+		ahc_pause(ahc);
+		if (target != CAM_TARGET_WILDCARD) {
+			tstate->enabled_luns[lun] = lstate;
+			ahc->enabled_luns++;
+
+			if ((ahc->features & AHC_MULTI_TID) != 0) {
+				u_int targid_mask;
+
+				targid_mask = ahc_inb(ahc, TARGID)
+					    | (ahc_inb(ahc, TARGID + 1) << 8);
+
+				targid_mask |= target_mask;
+				ahc_outb(ahc, TARGID, targid_mask);
+				ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+				
+				ahc_update_scsiid(ahc, targid_mask);
+			} else {
+				u_int our_id;
+				char  channel;
+
+				channel = SIM_CHANNEL(ahc, sim);
+				our_id = SIM_SCSI_ID(ahc, sim);
+
+				/*
+				 * This can only happen if selections
+				 * are not enabled
+				 */
+				if (target != our_id) {
+					u_int sblkctl;
+					char  cur_channel;
+					int   swap;
+
+					sblkctl = ahc_inb(ahc, SBLKCTL);
+					cur_channel = (sblkctl & SELBUSB)
+						    ? 'B' : 'A';
+					if ((ahc->features & AHC_TWIN) == 0)
+						cur_channel = 'A';
+					swap = cur_channel != channel;
+					if (channel == 'A')
+						ahc->our_id = target;
+					else
+						ahc->our_id_b = target;
+
+					if (swap)
+						ahc_outb(ahc, SBLKCTL,
+							 sblkctl ^ SELBUSB);
+
+					ahc_outb(ahc, SCSIID, target);
+
+					if (swap)
+						ahc_outb(ahc, SBLKCTL, sblkctl);
+				}
+			}
+		} else
+			ahc->black_hole = lstate;
+		/* Allow select-in operations */
+		if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
+			scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+			scsiseq |= ENSELI;
+			ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+			scsiseq = ahc_inb(ahc, SCSISEQ);
+			scsiseq |= ENSELI;
+			ahc_outb(ahc, SCSISEQ, scsiseq);
+		}
+		ahc_unpause(ahc);
+		ahc_unlock(ahc, &s);
+		ccb->ccb_h.status = CAM_REQ_CMP;
+		xpt_print_path(ccb->ccb_h.path);
+		printk("Lun now enabled for target mode\n");
+	} else {
+		struct scb *scb;
+		int i, empty;
+
+		if (lstate == NULL) {
+			ccb->ccb_h.status = CAM_LUN_INVALID;
+			return;
+		}
+
+		ahc_lock(ahc, &s);
+		
+		ccb->ccb_h.status = CAM_REQ_CMP;
+		LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+			struct ccb_hdr *ccbh;
+
+			ccbh = &scb->io_ctx->ccb_h;
+			if (ccbh->func_code == XPT_CONT_TARGET_IO
+			 && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+				printk("CTIO pending\n");
+				ccb->ccb_h.status = CAM_REQ_INVALID;
+				ahc_unlock(ahc, &s);
+				return;
+			}
+		}
+
+		if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+			printk("ATIOs pending\n");
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+		}
+
+		if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+			printk("INOTs pending\n");
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+		}
+
+		if (ccb->ccb_h.status != CAM_REQ_CMP) {
+			ahc_unlock(ahc, &s);
+			return;
+		}
+
+		xpt_print_path(ccb->ccb_h.path);
+		printk("Target mode disabled\n");
+		xpt_free_path(lstate->path);
+		kfree(lstate);
+
+		ahc_pause(ahc);
+		/* Can we clean up the target too? */
+		if (target != CAM_TARGET_WILDCARD) {
+			tstate->enabled_luns[lun] = NULL;
+			ahc->enabled_luns--;
+			for (empty = 1, i = 0; i < 8; i++)
+				if (tstate->enabled_luns[i] != NULL) {
+					empty = 0;
+					break;
+				}
+
+			if (empty) {
+				ahc_free_tstate(ahc, target, channel,
+						/*force*/FALSE);
+				if (ahc->features & AHC_MULTI_TID) {
+					u_int targid_mask;
+
+					targid_mask = ahc_inb(ahc, TARGID)
+						    | (ahc_inb(ahc, TARGID + 1)
+						       << 8);
+
+					targid_mask &= ~target_mask;
+					ahc_outb(ahc, TARGID, targid_mask);
+					ahc_outb(ahc, TARGID+1,
+					 	 (targid_mask >> 8));
+					ahc_update_scsiid(ahc, targid_mask);
+				}
+			}
+		} else {
+
+			ahc->black_hole = NULL;
+
+			/*
+			 * We can't allow selections without
+			 * our black hole device.
+			 */
+			empty = TRUE;
+		}
+		if (ahc->enabled_luns == 0) {
+			/* Disallow select-in */
+			u_int scsiseq;
+
+			scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+			scsiseq &= ~ENSELI;
+			ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+			scsiseq = ahc_inb(ahc, SCSISEQ);
+			scsiseq &= ~ENSELI;
+			ahc_outb(ahc, SCSISEQ, scsiseq);
+
+			if ((ahc->features & AHC_MULTIROLE) == 0) {
+				printk("Configuring Initiator Mode\n");
+				ahc->flags &= ~AHC_TARGETROLE;
+				ahc->flags |= AHC_INITIATORROLE;
+				/*
+				 * Returning to a configuration that
+				 * fit previously will always succeed.
+				 */
+				(void)ahc_loadseq(ahc);
+				ahc_restart(ahc);
+				/*
+				 * Unpaused.  The extra unpause
+				 * that follows is harmless.
+				 */
+			}
+		}
+		ahc_unpause(ahc);
+		ahc_unlock(ahc, &s);
+	}
+}
+
+static void
+ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
+{
+	u_int scsiid_mask;
+	u_int scsiid;
+
+	if ((ahc->features & AHC_MULTI_TID) == 0)
+		panic("ahc_update_scsiid called on non-multitid unit\n");
+
+	/*
+	 * Since we will rely on the TARGID mask
+	 * for selection enables, ensure that OID
+	 * in SCSIID is not set to some other ID
+	 * that we don't want to allow selections on.
+	 */
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
+	else
+		scsiid = ahc_inb(ahc, SCSIID);
+	scsiid_mask = 0x1 << (scsiid & OID);
+	if ((targid_mask & scsiid_mask) == 0) {
+		u_int our_id;
+
+		/* ffs counts from 1 */
+		our_id = ffs(targid_mask);
+		if (our_id == 0)
+			our_id = ahc->our_id;
+		else
+			our_id--;
+		scsiid &= TID;
+		scsiid |= our_id;
+	}
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
+	else
+		ahc_outb(ahc, SCSIID, scsiid);
+}
+
+static void
+ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
+{
+	struct target_cmd *cmd;
+
+	/*
+	 * If the card supports auto-access pause,
+	 * we can access the card directly regardless
+	 * of whether it is paused or not.
+	 */
+	if ((ahc->features & AHC_AUTOPAUSE) != 0)
+		paused = TRUE;
+
+	ahc_sync_tqinfifo(ahc, BUS_DMASYNC_POSTREAD);
+	while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
+
+		/*
+		 * Only advance through the queue if we
+		 * have the resources to process the command.
+		 */
+		if (ahc_handle_target_cmd(ahc, cmd) != 0)
+			break;
+
+		cmd->cmd_valid = 0;
+		ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+				ahc->shared_data_dmamap,
+				ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
+				sizeof(struct target_cmd),
+				BUS_DMASYNC_PREREAD);
+		ahc->tqinfifonext++;
+
+		/*
+		 * Lazily update our position in the target mode incoming
+		 * command queue as seen by the sequencer.
+		 */
+		if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+			if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+				u_int hs_mailbox;
+
+				hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
+				hs_mailbox &= ~HOST_TQINPOS;
+				hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
+				ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
+			} else {
+				if (!paused)
+					ahc_pause(ahc);	
+				ahc_outb(ahc, KERNEL_TQINPOS,
+					 ahc->tqinfifonext & HOST_TQINPOS);
+				if (!paused)
+					ahc_unpause(ahc);
+			}
+		}
+	}
+}
+
+static int
+ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
+{
+	struct	  ahc_tmode_tstate *tstate;
+	struct	  ahc_tmode_lstate *lstate;
+	struct	  ccb_accept_tio *atio;
+	uint8_t *byte;
+	int	  initiator;
+	int	  target;
+	int	  lun;
+
+	initiator = SCSIID_TARGET(ahc, cmd->scsiid);
+	target = SCSIID_OUR_ID(cmd->scsiid);
+	lun    = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+	byte = cmd->bytes;
+	tstate = ahc->enabled_targets[target];
+	lstate = NULL;
+	if (tstate != NULL)
+		lstate = tstate->enabled_luns[lun];
+
+	/*
+	 * Commands for disabled luns go to the black hole driver.
+	 */
+	if (lstate == NULL)
+		lstate = ahc->black_hole;
+
+	atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+	if (atio == NULL) {
+		ahc->flags |= AHC_TQINFIFO_BLOCKED;
+		/*
+		 * Wait for more ATIOs from the peripheral driver for this lun.
+		 */
+		if (bootverbose)
+			printk("%s: ATIOs exhausted\n", ahc_name(ahc));
+		return (1);
+	} else
+		ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
+#if 0
+	printk("Incoming command from %d for %d:%d%s\n",
+	       initiator, target, lun,
+	       lstate == ahc->black_hole ? "(Black Holed)" : "");
+#endif
+	SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+	if (lstate == ahc->black_hole) {
+		/* Fill in the wildcards */
+		atio->ccb_h.target_id = target;
+		atio->ccb_h.target_lun = lun;
+	}
+
+	/*
+	 * Package it up and send it off to
+	 * whomever has this lun enabled.
+	 */
+	atio->sense_len = 0;
+	atio->init_id = initiator;
+	if (byte[0] != 0xFF) {
+		/* Tag was included */
+		atio->tag_action = *byte++;
+		atio->tag_id = *byte++;
+		atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+	} else {
+		atio->ccb_h.flags = 0;
+	}
+	byte++;
+
+	/* Okay.  Now determine the cdb size based on the command code */
+	switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+	case 0:
+		atio->cdb_len = 6;
+		break;
+	case 1:
+	case 2:
+		atio->cdb_len = 10;
+		break;
+	case 4:
+		atio->cdb_len = 16;
+		break;
+	case 5:
+		atio->cdb_len = 12;
+		break;
+	case 3:
+	default:
+		/* Only copy the opcode. */
+		atio->cdb_len = 1;
+		printk("Reserved or VU command code type encountered\n");
+		break;
+	}
+	
+	memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+	atio->ccb_h.status |= CAM_CDB_RECVD;
+
+	if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+		/*
+		 * We weren't allowed to disconnect.
+		 * We're hanging on the bus until a
+		 * continue target I/O comes in response
+		 * to this accept tio.
+		 */
+#if 0
+		printk("Received Immediate Command %d:%d:%d - %p\n",
+		       initiator, target, lun, ahc->pending_device);
+#endif
+		ahc->pending_device = lstate;
+		ahc_freeze_ccb((union ccb *)atio);
+		atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+	}
+	xpt_done((union ccb*)atio);
+	return (0);
+}
+
+#endif
diff --git a/drivers/scsi/aic7xxx/aic7xxx_inline.h b/drivers/scsi/aic7xxx/aic7xxx_inline.h
new file mode 100644
index 0000000..0b57b78
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_inline.h
@@ -0,0 +1,97 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_inline.h#43 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC7XXX_INLINE_H_
+#define _AIC7XXX_INLINE_H_
+
+/************************* Sequencer Execution Control ************************/
+int  ahc_is_paused(struct ahc_softc *ahc);
+void ahc_pause(struct ahc_softc *ahc);
+void ahc_unpause(struct ahc_softc *ahc);
+
+/************************** Memory mapping routines ***************************/
+void	ahc_sync_sglist(struct ahc_softc *ahc,
+			struct scb *scb, int op);
+
+/******************************** Debugging ***********************************/
+static inline char *ahc_name(struct ahc_softc *ahc);
+
+static inline char *ahc_name(struct ahc_softc *ahc)
+{
+	return (ahc->name);
+}
+
+/*********************** Miscellaneous Support Functions ***********************/
+
+struct ahc_initiator_tinfo *
+	ahc_fetch_transinfo(struct ahc_softc *ahc,
+			    char channel, u_int our_id,
+			    u_int remote_id,
+			    struct ahc_tmode_tstate **tstate);
+uint16_t
+	ahc_inw(struct ahc_softc *ahc, u_int port);
+void	ahc_outw(struct ahc_softc *ahc, u_int port,
+		 u_int value);
+uint32_t
+	ahc_inl(struct ahc_softc *ahc, u_int port);
+void	ahc_outl(struct ahc_softc *ahc, u_int port,
+		 uint32_t value);
+uint64_t
+	ahc_inq(struct ahc_softc *ahc, u_int port);
+void	ahc_outq(struct ahc_softc *ahc, u_int port,
+		 uint64_t value);
+struct scb*
+	ahc_get_scb(struct ahc_softc *ahc);
+void	ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+struct scb *
+	ahc_lookup_scb(struct ahc_softc *ahc, u_int tag);
+void	ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
+struct scsi_sense_data *
+	ahc_get_sense_buf(struct ahc_softc *ahc,
+			  struct scb *scb);
+
+/************************** Interrupt Processing ******************************/
+int	ahc_intr(struct ahc_softc *ahc);
+
+#endif  /* _AIC7XXX_INLINE_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
new file mode 100644
index 0000000..fc6a831
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -0,0 +1,2598 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.c#235 $
+ *
+ * Copyright (c) 1994 John Aycock
+ *   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, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 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), ...
+ *
+ * --------------------------------------------------------------------------
+ *
+ *  Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ *  Substantially modified to include support for wide and twin bus
+ *  adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ *  SCB paging, and other rework of the code.
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-2000 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ *---------------------------------------------------------------------------
+ *
+ *  Thanks also go to (in alphabetical order) the following:
+ *
+ *    Rory Bolt     - Sequencer bug fixes
+ *    Jay Estabrook - Initial DEC Alpha support
+ *    Doug Ledford  - Much needed abort/reset bug fixes
+ *    Kai Makisara  - DMAing of SCBs
+ *
+ *  A Boot time option was also added for not resetting the scsi bus.
+ *
+ *    Form:  aic7xxx=extended
+ *           aic7xxx=no_reset
+ *           aic7xxx=verbose
+ *
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ *
+ *  Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp
+ */
+
+/*
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
+ *
+ * Copyright (c) 1997-1999 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs.  Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ *  1: Import of the latest FreeBSD sequencer code for this driver
+ *  2: Modification of kernel code to accommodate different sequencer semantics
+ *  3: Extensive changes throughout kernel portion of driver to improve
+ *     abort/reset processing and error hanndling
+ *  4: Other work contributed by various people on the Internet
+ *  5: Changes to printk information and verbosity selection code
+ *  6: General reliability related changes, especially in IRQ management
+ *  7: Modifications to the default probe/attach order for supported cards
+ *  8: SMP friendliness has been improved
+ *
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include <scsi/scsicam.h>
+
+static struct scsi_transport_template *ahc_linux_transport_template = NULL;
+
+#include <linux/init.h>		/* __setup */
+#include <linux/mm.h>		/* For fetching system memory size */
+#include <linux/blkdev.h>		/* For block_size() */
+#include <linux/delay.h>	/* For ssleep/msleep */
+#include <linux/slab.h>
+
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY_MS
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY_MS
+#else
+#define AIC7XXX_RESET_DELAY 5000
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * The tag_commands is an array of 16 to allow for wide and twin adapters.
+ * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15
+ * for channel 1.
+ */
+typedef struct {
+	uint8_t tag_commands[16];	/* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0			tagged queuing disabled
+ * 1 <= n <= 253	n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full.  For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred).  On some devices, queue full is returned for a temporary
+ * resource shortage.  These devices will return queue full at varying
+ * depths.  The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3.  It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ *       to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+	{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+	{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#else
+#define AIC7XXX_CMDS_PER_DEVICE AHC_MAX_QUEUE
+#endif
+
+#define AIC7XXX_CONFIGED_TAG_COMMANDS {					\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE		\
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * 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
+
+void
+ahc_print_path(struct ahc_softc *ahc, struct scb *scb)
+{
+	printk("(scsi%d:%c:%d:%d): ",
+	       ahc->platform_data->host->host_no,
+	       scb != NULL ? SCB_GET_CHANNEL(ahc, scb) : 'X',
+	       scb != NULL ? SCB_GET_TARGET(ahc, scb) : -1,
+	       scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ *       cards in the system.  This should be fixed.  Exceptions to this
+ *       rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic7xxx_no_reset;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ *     0 == Use whatever is in the SEEPROM or default to off
+ *     1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic7xxx_extended;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers.  This is somewhat
+ * dubious at best.  To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations where stray PCI transactions with bad parity are
+ * the norm rather than the exception, the error messages can be overwhelming.
+ * It's included in the driver for completeness.
+ *   0	   = Shut off PCI parity check
+ *   non-0 = reverse polarity pci parity checking
+ */
+static uint32_t aic7xxx_pci_parity = ~0;
+
+/*
+ * There are lots of broken chipsets in the world.  Some of them will
+ * violate the PCI spec when we issue byte sized memory writes to our
+ * controller.  I/O mapped register access, if allowed by the given
+ * platform, will work in almost all cases.
+ */
+uint32_t aic7xxx_allow_memio = ~0;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ *   0 - 256ms
+ *   1 - 128ms
+ *   2 - 64ms
+ *   3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static uint32_t aic7xxx_seltime;
+
+/*
+ * Certain devices do not perform any aging on commands.  Should the
+ * device be saturated by commands in one portion of the disk, it is
+ * possible for transactions on far away sectors to never be serviced.
+ * To handle these devices, we can periodically send an ordered tag to
+ * force all outstanding transactions to be serviced prior to a new
+ * transaction.
+ */
+static uint32_t aic7xxx_periodic_otag;
+
+/*
+ * Module information and settable options.
+ */
+static char *aic7xxx = NULL;
+
+MODULE_AUTHOR("Maintainer: Hannes Reinecke <hare@suse.de>");
+MODULE_DESCRIPTION("Adaptec AIC77XX/78XX SCSI Host Bus Adapter driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(AIC7XXX_DRIVER_VERSION);
+module_param(aic7xxx, charp, 0444);
+MODULE_PARM_DESC(aic7xxx,
+"period-delimited options string:\n"
+"	verbose			Enable verbose/diagnostic logging\n"
+"	allow_memio		Allow device registers to be memory mapped\n"
+"	debug			Bitmask of debug values to enable\n"
+"	no_probe		Toggle EISA/VLB controller probing\n"
+"	probe_eisa_vl		Toggle EISA/VLB controller probing\n"
+"	no_reset		Suppress initial bus resets\n"
+"	extended		Enable extended geometry on all controllers\n"
+"	periodic_otag		Send an ordered tagged transaction\n"
+"				periodically to prevent tag starvation.\n"
+"				This may be required by some older disk\n"
+"				drives or RAID arrays.\n"
+"	tag_info:<tag_str>	Set per-target tag depth\n"
+"	global_tag_depth:<int>	Global tag depth for every target\n"
+"				on every bus\n"
+"	seltime:<int>		Selection Timeout\n"
+"				(0/256ms,1/128ms,2/64ms,3/32ms)\n"
+"\n"
+"	Sample modprobe configuration file:\n"
+"	#	Toggle EISA/VLB probing\n"
+"	#	Set tag depth on Controller 1/Target 1 to 10 tags\n"
+"	#	Shorten the selection timeout to 128ms\n"
+"\n"
+"	options aic7xxx 'aic7xxx=probe_eisa_vl.tag_info:{{}.{.10}}.seltime:1'\n"
+);
+
+static void ahc_linux_handle_scsi_status(struct ahc_softc *,
+					 struct scsi_device *,
+					 struct scb *);
+static void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc,
+					 struct scsi_cmnd *cmd);
+static void ahc_linux_freeze_simq(struct ahc_softc *ahc);
+static void ahc_linux_release_simq(struct ahc_softc *ahc);
+static int  ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag);
+static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc);
+static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc,
+				     struct ahc_devinfo *devinfo);
+static void ahc_linux_device_queue_depth(struct scsi_device *);
+static int ahc_linux_run_command(struct ahc_softc*,
+				 struct ahc_linux_device *,
+				 struct scsi_cmnd *);
+static void ahc_linux_setup_tag_info_global(char *p);
+static int  aic7xxx_setup(char *s);
+
+static int ahc_linux_unit;
+
+
+/************************** OS Utility Wrappers *******************************/
+void
+ahc_delay(long usec)
+{
+	/*
+	 * udelay on Linux can have problems for
+	 * multi-millisecond waits.  Wait at most
+	 * 1024us per call.
+	 */
+	while (usec > 0) {
+		udelay(usec % 1024);
+		usec -= 1024;
+	}
+}
+
+/***************************** Low Level I/O **********************************/
+uint8_t
+ahc_inb(struct ahc_softc * ahc, long port)
+{
+	uint8_t x;
+
+	if (ahc->tag == BUS_SPACE_MEMIO) {
+		x = readb(ahc->bsh.maddr + port);
+	} else {
+		x = inb(ahc->bsh.ioport + port);
+	}
+	mb();
+	return (x);
+}
+
+void
+ahc_outb(struct ahc_softc * ahc, long port, uint8_t val)
+{
+	if (ahc->tag == BUS_SPACE_MEMIO) {
+		writeb(val, ahc->bsh.maddr + port);
+	} else {
+		outb(val, ahc->bsh.ioport + port);
+	}
+	mb();
+}
+
+void
+ahc_outsb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+	int i;
+
+	/*
+	 * There is probably a more efficient way to do this on Linux
+	 * but we don't use this for anything speed critical and this
+	 * should work.
+	 */
+	for (i = 0; i < count; i++)
+		ahc_outb(ahc, port, *array++);
+}
+
+void
+ahc_insb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+	int i;
+
+	/*
+	 * There is probably a more efficient way to do this on Linux
+	 * but we don't use this for anything speed critical and this
+	 * should work.
+	 */
+	for (i = 0; i < count; i++)
+		*array++ = ahc_inb(ahc, port);
+}
+
+/********************************* Inlines ************************************/
+static void ahc_linux_unmap_scb(struct ahc_softc*, struct scb*);
+
+static int ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+		 		      struct ahc_dma_seg *sg,
+				      dma_addr_t addr, bus_size_t len);
+
+static void
+ahc_linux_unmap_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct scsi_cmnd *cmd;
+
+	cmd = scb->io_ctx;
+	ahc_sync_sglist(ahc, scb, BUS_DMASYNC_POSTWRITE);
+
+	scsi_dma_unmap(cmd);
+}
+
+static int
+ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+		  struct ahc_dma_seg *sg, dma_addr_t addr, bus_size_t len)
+{
+	int	 consumed;
+
+	if ((scb->sg_count + 1) > AHC_NSEG)
+		panic("Too few segs for dma mapping.  "
+		      "Increase AHC_NSEG\n");
+
+	consumed = 1;
+	sg->addr = ahc_htole32(addr & 0xFFFFFFFF);
+	scb->platform_data->xfer_len += len;
+
+	if (sizeof(dma_addr_t) > 4
+	 && (ahc->flags & AHC_39BIT_ADDRESSING) != 0)
+		len |= (addr >> 8) & AHC_SG_HIGH_ADDR_MASK;
+
+	sg->len = ahc_htole32(len);
+	return (consumed);
+}
+
+/*
+ * Return a string describing the driver.
+ */
+static const char *
+ahc_linux_info(struct Scsi_Host *host)
+{
+	static char buffer[512];
+	char	ahc_info[256];
+	char   *bp;
+	struct ahc_softc *ahc;
+
+	bp = &buffer[0];
+	ahc = *(struct ahc_softc **)host->hostdata;
+	memset(bp, 0, sizeof(buffer));
+	strcpy(bp, "Adaptec AIC7XXX EISA/VLB/PCI SCSI HBA DRIVER, Rev " AIC7XXX_DRIVER_VERSION "\n"
+			"        <");
+	strcat(bp, ahc->description);
+	strcat(bp, ">\n"
+			"        ");
+	ahc_controller_info(ahc, ahc_info);
+	strcat(bp, ahc_info);
+	strcat(bp, "\n");
+
+	return (bp);
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+static int
+ahc_linux_queue_lck(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd *))
+{
+	struct	 ahc_softc *ahc;
+	struct	 ahc_linux_device *dev = scsi_transport_device_data(cmd->device);
+	int rtn = SCSI_MLQUEUE_HOST_BUSY;
+	unsigned long flags;
+
+	ahc = *(struct ahc_softc **)cmd->device->host->hostdata;
+
+	ahc_lock(ahc, &flags);
+	if (ahc->platform_data->qfrozen == 0) {
+		cmd->scsi_done = scsi_done;
+		cmd->result = CAM_REQ_INPROG << 16;
+		rtn = ahc_linux_run_command(ahc, dev, cmd);
+	}
+	ahc_unlock(ahc, &flags);
+
+	return rtn;
+}
+
+static DEF_SCSI_QCMD(ahc_linux_queue)
+
+static inline struct scsi_target **
+ahc_linux_target_in_softc(struct scsi_target *starget)
+{
+	struct	ahc_softc *ahc =
+		*((struct ahc_softc **)dev_to_shost(&starget->dev)->hostdata);
+	unsigned int target_offset;
+
+	target_offset = starget->id;
+	if (starget->channel != 0)
+		target_offset += 8;
+
+	return &ahc->platform_data->starget[target_offset];
+}
+
+static int
+ahc_linux_target_alloc(struct scsi_target *starget)
+{
+	struct	ahc_softc *ahc =
+		*((struct ahc_softc **)dev_to_shost(&starget->dev)->hostdata);
+	struct seeprom_config *sc = ahc->seep_config;
+	unsigned long flags;
+	struct scsi_target **ahc_targp = ahc_linux_target_in_softc(starget);
+	unsigned short scsirate;
+	struct ahc_devinfo devinfo;
+	struct ahc_initiator_tinfo *tinfo;
+	struct ahc_tmode_tstate *tstate;
+	char channel = starget->channel + 'A';
+	unsigned int our_id = ahc->our_id;
+	unsigned int target_offset;
+
+	target_offset = starget->id;
+	if (starget->channel != 0)
+		target_offset += 8;
+	  
+	if (starget->channel)
+		our_id = ahc->our_id_b;
+
+	ahc_lock(ahc, &flags);
+
+	BUG_ON(*ahc_targp != NULL);
+
+	*ahc_targp = starget;
+
+	if (sc) {
+		int maxsync = AHC_SYNCRATE_DT;
+		int ultra = 0;
+		int flags = sc->device_flags[target_offset];
+
+		if (ahc->flags & AHC_NEWEEPROM_FMT) {
+		    if (flags & CFSYNCHISULTRA)
+			ultra = 1;
+		} else if (flags & CFULTRAEN)
+			ultra = 1;
+		/* AIC nutcase; 10MHz appears as ultra = 1, CFXFER = 0x04
+		 * change it to ultra=0, CFXFER = 0 */
+		if(ultra && (flags & CFXFER) == 0x04) {
+			ultra = 0;
+			flags &= ~CFXFER;
+		}
+	    
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			scsirate = (flags & CFXFER) | (ultra ? 0x8 : 0);
+		} else {
+			scsirate = (flags & CFXFER) << 4;
+			maxsync = ultra ? AHC_SYNCRATE_ULTRA : 
+				AHC_SYNCRATE_FAST;
+		}
+		spi_max_width(starget) = (flags & CFWIDEB) ? 1 : 0;
+		if (!(flags & CFSYNCH))
+			spi_max_offset(starget) = 0;
+		spi_min_period(starget) = 
+			ahc_find_period(ahc, scsirate, maxsync);
+
+		tinfo = ahc_fetch_transinfo(ahc, channel, ahc->our_id,
+					    starget->id, &tstate);
+	}
+	ahc_compile_devinfo(&devinfo, our_id, starget->id,
+			    CAM_LUN_WILDCARD, channel,
+			    ROLE_INITIATOR);
+	ahc_set_syncrate(ahc, &devinfo, NULL, 0, 0, 0,
+			 AHC_TRANS_GOAL, /*paused*/FALSE);
+	ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+		      AHC_TRANS_GOAL, /*paused*/FALSE);
+	ahc_unlock(ahc, &flags);
+
+	return 0;
+}
+
+static void
+ahc_linux_target_destroy(struct scsi_target *starget)
+{
+	struct scsi_target **ahc_targp = ahc_linux_target_in_softc(starget);
+
+	*ahc_targp = NULL;
+}
+
+static int
+ahc_linux_slave_alloc(struct scsi_device *sdev)
+{
+	struct	ahc_softc *ahc =
+		*((struct ahc_softc **)sdev->host->hostdata);
+	struct scsi_target *starget = sdev->sdev_target;
+	struct ahc_linux_device *dev;
+
+	if (bootverbose)
+		printk("%s: Slave Alloc %d\n", ahc_name(ahc), sdev->id);
+
+	dev = scsi_transport_device_data(sdev);
+	memset(dev, 0, sizeof(*dev));
+
+	/*
+	 * We start out life using untagged
+	 * transactions of which we allow one.
+	 */
+	dev->openings = 1;
+
+	/*
+	 * Set maxtags to 0.  This will be changed if we
+	 * later determine that we are dealing with
+	 * a tagged queuing capable device.
+	 */
+	dev->maxtags = 0;
+	
+	spi_period(starget) = 0;
+
+	return 0;
+}
+
+static int
+ahc_linux_slave_configure(struct scsi_device *sdev)
+{
+	struct	ahc_softc *ahc;
+
+	ahc = *((struct ahc_softc **)sdev->host->hostdata);
+
+	if (bootverbose)
+		sdev_printk(KERN_INFO, sdev, "Slave Configure\n");
+
+	ahc_linux_device_queue_depth(sdev);
+
+	/* Initial Domain Validation */
+	if (!spi_initial_dv(sdev->sdev_target))
+		spi_dv_device(sdev);
+
+	return 0;
+}
+
+#if defined(__i386__)
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+static int
+ahc_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+		    sector_t capacity, int geom[])
+{
+	uint8_t *bh;
+	int	 heads;
+	int	 sectors;
+	int	 cylinders;
+	int	 ret;
+	int	 extended;
+	struct	 ahc_softc *ahc;
+	u_int	 channel;
+
+	ahc = *((struct ahc_softc **)sdev->host->hostdata);
+	channel = sdev_channel(sdev);
+
+	bh = scsi_bios_ptable(bdev);
+	if (bh) {
+		ret = scsi_partsize(bh, capacity,
+				    &geom[2], &geom[0], &geom[1]);
+		kfree(bh);
+		if (ret != -1)
+			return (ret);
+	}
+	heads = 64;
+	sectors = 32;
+	cylinders = aic_sector_div(capacity, heads, sectors);
+
+	if (aic7xxx_extended != 0)
+		extended = 1;
+	else if (channel == 0)
+		extended = (ahc->flags & AHC_EXTENDED_TRANS_A) != 0;
+	else
+		extended = (ahc->flags & AHC_EXTENDED_TRANS_B) != 0;
+	if (extended && cylinders >= 1024) {
+		heads = 255;
+		sectors = 63;
+		cylinders = aic_sector_div(capacity, heads, sectors);
+	}
+	geom[0] = heads;
+	geom[1] = sectors;
+	geom[2] = cylinders;
+	return (0);
+}
+#endif
+
+/*
+ * Abort the current SCSI command(s).
+ */
+static int
+ahc_linux_abort(struct scsi_cmnd *cmd)
+{
+	int error;
+
+	error = ahc_linux_queue_recovery_cmd(cmd, SCB_ABORT);
+	if (error != 0)
+		printk("aic7xxx_abort returns 0x%x\n", error);
+	return (error);
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+static int
+ahc_linux_dev_reset(struct scsi_cmnd *cmd)
+{
+	int error;
+
+	error = ahc_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
+	if (error != 0)
+		printk("aic7xxx_dev_reset returns 0x%x\n", error);
+	return (error);
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+static int
+ahc_linux_bus_reset(struct scsi_cmnd *cmd)
+{
+	struct ahc_softc *ahc;
+	int    found;
+	unsigned long flags;
+
+	ahc = *(struct ahc_softc **)cmd->device->host->hostdata;
+
+	ahc_lock(ahc, &flags);
+	found = ahc_reset_channel(ahc, scmd_channel(cmd) + 'A',
+				  /*initiate reset*/TRUE);
+	ahc_unlock(ahc, &flags);
+
+	if (bootverbose)
+		printk("%s: SCSI bus reset delivered. "
+		       "%d SCBs aborted.\n", ahc_name(ahc), found);
+
+	return SUCCESS;
+}
+
+struct scsi_host_template aic7xxx_driver_template = {
+	.module			= THIS_MODULE,
+	.name			= "aic7xxx",
+	.proc_name		= "aic7xxx",
+	.show_info		= ahc_linux_show_info,
+	.write_info		= ahc_proc_write_seeprom,
+	.info			= ahc_linux_info,
+	.queuecommand		= ahc_linux_queue,
+	.eh_abort_handler	= ahc_linux_abort,
+	.eh_device_reset_handler = ahc_linux_dev_reset,
+	.eh_bus_reset_handler	= ahc_linux_bus_reset,
+#if defined(__i386__)
+	.bios_param		= ahc_linux_biosparam,
+#endif
+	.can_queue		= AHC_MAX_QUEUE,
+	.this_id		= -1,
+	.max_sectors		= 8192,
+	.cmd_per_lun		= 2,
+	.use_clustering		= ENABLE_CLUSTERING,
+	.slave_alloc		= ahc_linux_slave_alloc,
+	.slave_configure	= ahc_linux_slave_configure,
+	.target_alloc		= ahc_linux_target_alloc,
+	.target_destroy		= ahc_linux_target_destroy,
+};
+
+/**************************** Tasklet Handler *********************************/
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahc, cmd)						    \
+	((((cmd)->device->id << TID_SHIFT) & TID)			    \
+	| (((cmd)->device->channel == 0) ? (ahc)->our_id : (ahc)->our_id_b) \
+	| (((cmd)->device->channel == 0) ? 0 : TWIN_CHNLB))
+
+/******************************** Bus DMA *************************************/
+int
+ahc_dma_tag_create(struct ahc_softc *ahc, bus_dma_tag_t parent,
+		   bus_size_t alignment, bus_size_t boundary,
+		   dma_addr_t lowaddr, dma_addr_t highaddr,
+		   bus_dma_filter_t *filter, void *filterarg,
+		   bus_size_t maxsize, int nsegments,
+		   bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+	bus_dma_tag_t dmat;
+
+	dmat = kmalloc(sizeof(*dmat), GFP_ATOMIC);
+	if (dmat == NULL)
+		return (ENOMEM);
+
+	/*
+	 * Linux is very simplistic about DMA memory.  For now don't
+	 * maintain all specification information.  Once Linux supplies
+	 * better facilities for doing these operations, or the
+	 * needs of this particular driver change, we might need to do
+	 * more here.
+	 */
+	dmat->alignment = alignment;
+	dmat->boundary = boundary;
+	dmat->maxsize = maxsize;
+	*ret_tag = dmat;
+	return (0);
+}
+
+void
+ahc_dma_tag_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat)
+{
+	kfree(dmat);
+}
+
+int
+ahc_dmamem_alloc(struct ahc_softc *ahc, bus_dma_tag_t dmat, void** vaddr,
+		 int flags, bus_dmamap_t *mapp)
+{
+	*vaddr = pci_alloc_consistent(ahc->dev_softc,
+				      dmat->maxsize, mapp);
+	if (*vaddr == NULL)
+		return ENOMEM;
+	return 0;
+}
+
+void
+ahc_dmamem_free(struct ahc_softc *ahc, bus_dma_tag_t dmat,
+		void* vaddr, bus_dmamap_t map)
+{
+	pci_free_consistent(ahc->dev_softc, dmat->maxsize,
+			    vaddr, map);
+}
+
+int
+ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map,
+		void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+		void *cb_arg, int flags)
+{
+	/*
+	 * Assume for now that this will only be used during
+	 * initialization and not for per-transaction buffer mapping.
+	 */
+	bus_dma_segment_t stack_sg;
+
+	stack_sg.ds_addr = map;
+	stack_sg.ds_len = dmat->maxsize;
+	cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+	return (0);
+}
+
+void
+ahc_dmamap_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+}
+
+int
+ahc_dmamap_unload(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+	/* Nothing to do */
+	return (0);
+}
+
+static void
+ahc_linux_setup_tag_info_global(char *p)
+{
+	int tags, i, j;
+
+	tags = simple_strtoul(p + 1, NULL, 0) & 0xff;
+	printk("Setting Global Tags= %d\n", tags);
+
+	for (i = 0; i < ARRAY_SIZE(aic7xxx_tag_info); i++) {
+		for (j = 0; j < AHC_NUM_TARGETS; j++) {
+			aic7xxx_tag_info[i].tag_commands[j] = tags;
+		}
+	}
+}
+
+static void
+ahc_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value)
+{
+
+	if ((instance >= 0) && (targ >= 0)
+	 && (instance < ARRAY_SIZE(aic7xxx_tag_info))
+	 && (targ < AHC_NUM_TARGETS)) {
+		aic7xxx_tag_info[instance].tag_commands[targ] = value & 0xff;
+		if (bootverbose)
+			printk("tag_info[%d:%d] = %d\n", instance, targ, value);
+	}
+}
+
+static char *
+ahc_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth,
+		       void (*callback)(u_long, int, int, int32_t),
+		       u_long callback_arg)
+{
+	char	*tok_end;
+	char	*tok_end2;
+	int      i;
+	int      instance;
+	int	 targ;
+	int	 done;
+	char	 tok_list[] = {'.', ',', '{', '}', '\0'};
+
+	/* All options use a ':' name/arg separator */
+	if (*opt_arg != ':')
+		return (opt_arg);
+	opt_arg++;
+	instance = -1;
+	targ = -1;
+	done = FALSE;
+	/*
+	 * Restore separator that may be in
+	 * the middle of our option argument.
+	 */
+	tok_end = strchr(opt_arg, '\0');
+	if (tok_end < end)
+		*tok_end = ',';
+	while (!done) {
+		switch (*opt_arg) {
+		case '{':
+			if (instance == -1) {
+				instance = 0;
+			} else {
+				if (depth > 1) {
+					if (targ == -1)
+						targ = 0;
+				} else {
+					printk("Malformed Option %s\n",
+					       opt_name);
+					done = TRUE;
+				}
+			}
+			opt_arg++;
+			break;
+		case '}':
+			if (targ != -1)
+				targ = -1;
+			else if (instance != -1)
+				instance = -1;
+			opt_arg++;
+			break;
+		case ',':
+		case '.':
+			if (instance == -1)
+				done = TRUE;
+			else if (targ >= 0)
+				targ++;
+			else if (instance >= 0)
+				instance++;
+			opt_arg++;
+			break;
+		case '\0':
+			done = TRUE;
+			break;
+		default:
+			tok_end = end;
+			for (i = 0; tok_list[i]; i++) {
+				tok_end2 = strchr(opt_arg, tok_list[i]);
+				if ((tok_end2) && (tok_end2 < tok_end))
+					tok_end = tok_end2;
+			}
+			callback(callback_arg, instance, targ,
+				 simple_strtol(opt_arg, NULL, 0));
+			opt_arg = tok_end;
+			break;
+		}
+	}
+	return (opt_arg);
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic7xxx=stpwlev:1,extended
+ */
+static int
+aic7xxx_setup(char *s)
+{
+	int	i, n;
+	char   *p;
+	char   *end;
+
+	static const struct {
+		const char *name;
+		uint32_t *flag;
+	} options[] = {
+		{ "extended", &aic7xxx_extended },
+		{ "no_reset", &aic7xxx_no_reset },
+		{ "verbose", &aic7xxx_verbose },
+		{ "allow_memio", &aic7xxx_allow_memio},
+#ifdef AHC_DEBUG
+		{ "debug", &ahc_debug },
+#endif
+		{ "periodic_otag", &aic7xxx_periodic_otag },
+		{ "pci_parity", &aic7xxx_pci_parity },
+		{ "seltime", &aic7xxx_seltime },
+		{ "tag_info", NULL },
+		{ "global_tag_depth", NULL },
+		{ "dv", NULL }
+	};
+
+	end = strchr(s, '\0');
+
+	/*
+	 * XXX ia64 gcc isn't smart enough to know that ARRAY_SIZE
+	 * will never be 0 in this case.
+	 */
+	n = 0;
+
+	while ((p = strsep(&s, ",.")) != NULL) {
+		if (*p == '\0')
+			continue;
+		for (i = 0; i < ARRAY_SIZE(options); i++) {
+
+			n = strlen(options[i].name);
+			if (strncmp(options[i].name, p, n) == 0)
+				break;
+		}
+		if (i == ARRAY_SIZE(options))
+			continue;
+
+		if (strncmp(p, "global_tag_depth", n) == 0) {
+			ahc_linux_setup_tag_info_global(p + n);
+		} else if (strncmp(p, "tag_info", n) == 0) {
+			s = ahc_parse_brace_option("tag_info", p + n, end,
+			    2, ahc_linux_setup_tag_info, 0);
+		} else if (p[n] == ':') {
+			*(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
+		} else if (strncmp(p, "verbose", n) == 0) {
+			*(options[i].flag) = 1;
+		} else {
+			*(options[i].flag) ^= 0xFFFFFFFF;
+		}
+	}
+	return 1;
+}
+
+__setup("aic7xxx=", aic7xxx_setup);
+
+uint32_t aic7xxx_verbose;
+
+int
+ahc_linux_register_host(struct ahc_softc *ahc, struct scsi_host_template *template)
+{
+	char	buf[80];
+	struct	Scsi_Host *host;
+	char	*new_name;
+	u_long	s;
+	int	retval;
+
+	template->name = ahc->description;
+	host = scsi_host_alloc(template, sizeof(struct ahc_softc *));
+	if (host == NULL)
+		return (ENOMEM);
+
+	*((struct ahc_softc **)host->hostdata) = ahc;
+	ahc->platform_data->host = host;
+	host->can_queue = AHC_MAX_QUEUE;
+	host->cmd_per_lun = 2;
+	/* XXX No way to communicate the ID for multiple channels */
+	host->this_id = ahc->our_id;
+	host->irq = ahc->platform_data->irq;
+	host->max_id = (ahc->features & AHC_WIDE) ? 16 : 8;
+	host->max_lun = AHC_NUM_LUNS;
+	host->max_channel = (ahc->features & AHC_TWIN) ? 1 : 0;
+	host->sg_tablesize = AHC_NSEG;
+	ahc_lock(ahc, &s);
+	ahc_set_unit(ahc, ahc_linux_unit++);
+	ahc_unlock(ahc, &s);
+	sprintf(buf, "scsi%d", host->host_no);
+	new_name = kmalloc(strlen(buf) + 1, GFP_ATOMIC);
+	if (new_name != NULL) {
+		strcpy(new_name, buf);
+		ahc_set_name(ahc, new_name);
+	}
+	host->unique_id = ahc->unit;
+	ahc_linux_initialize_scsi_bus(ahc);
+	ahc_intr_enable(ahc, TRUE);
+
+	host->transportt = ahc_linux_transport_template;
+
+	retval = scsi_add_host(host,
+			(ahc->dev_softc ? &ahc->dev_softc->dev : NULL));
+	if (retval) {
+		printk(KERN_WARNING "aic7xxx: scsi_add_host failed\n");
+		scsi_host_put(host);
+		return retval;
+	}
+
+	scsi_scan_host(host);
+	return 0;
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+void
+ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc)
+{
+	int i;
+	int numtarg;
+	unsigned long s;
+
+	i = 0;
+	numtarg = 0;
+
+	ahc_lock(ahc, &s);
+
+	if (aic7xxx_no_reset != 0)
+		ahc->flags &= ~(AHC_RESET_BUS_A|AHC_RESET_BUS_B);
+
+	if ((ahc->flags & AHC_RESET_BUS_A) != 0)
+		ahc_reset_channel(ahc, 'A', /*initiate_reset*/TRUE);
+	else
+		numtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+
+	if ((ahc->features & AHC_TWIN) != 0) {
+
+		if ((ahc->flags & AHC_RESET_BUS_B) != 0) {
+			ahc_reset_channel(ahc, 'B', /*initiate_reset*/TRUE);
+		} else {
+			if (numtarg == 0)
+				i = 8;
+			numtarg += 8;
+		}
+	}
+
+	/*
+	 * Force negotiation to async for all targets that
+	 * will not see an initial bus reset.
+	 */
+	for (; i < numtarg; i++) {
+		struct ahc_devinfo devinfo;
+		struct ahc_initiator_tinfo *tinfo;
+		struct ahc_tmode_tstate *tstate;
+		u_int our_id;
+		u_int target_id;
+		char channel;
+
+		channel = 'A';
+		our_id = ahc->our_id;
+		target_id = i;
+		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+			channel = 'B';
+			our_id = ahc->our_id_b;
+			target_id = i % 8;
+		}
+		tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+					    target_id, &tstate);
+		ahc_compile_devinfo(&devinfo, our_id, target_id,
+				    CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+		ahc_update_neg_request(ahc, &devinfo, tstate,
+				       tinfo, AHC_NEG_ALWAYS);
+	}
+	ahc_unlock(ahc, &s);
+	/* Give the bus some time to recover */
+	if ((ahc->flags & (AHC_RESET_BUS_A|AHC_RESET_BUS_B)) != 0) {
+		ahc_linux_freeze_simq(ahc);
+		msleep(AIC7XXX_RESET_DELAY);
+		ahc_linux_release_simq(ahc);
+	}
+}
+
+int
+ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
+{
+
+	ahc->platform_data =
+	    kzalloc(sizeof(struct ahc_platform_data), GFP_ATOMIC);
+	if (ahc->platform_data == NULL)
+		return (ENOMEM);
+	ahc->platform_data->irq = AHC_LINUX_NOIRQ;
+	ahc_lockinit(ahc);
+	ahc->seltime = (aic7xxx_seltime & 0x3) << 4;
+	ahc->seltime_b = (aic7xxx_seltime & 0x3) << 4;
+	if (aic7xxx_pci_parity == 0)
+		ahc->flags |= AHC_DISABLE_PCI_PERR;
+
+	return (0);
+}
+
+void
+ahc_platform_free(struct ahc_softc *ahc)
+{
+	struct scsi_target *starget;
+	int i;
+
+	if (ahc->platform_data != NULL) {
+		/* destroy all of the device and target objects */
+		for (i = 0; i < AHC_NUM_TARGETS; i++) {
+			starget = ahc->platform_data->starget[i];
+			if (starget != NULL) {
+				ahc->platform_data->starget[i] = NULL;
+ 			}
+ 		}
+
+		if (ahc->platform_data->irq != AHC_LINUX_NOIRQ)
+			free_irq(ahc->platform_data->irq, ahc);
+		if (ahc->tag == BUS_SPACE_PIO
+		 && ahc->bsh.ioport != 0)
+			release_region(ahc->bsh.ioport, 256);
+		if (ahc->tag == BUS_SPACE_MEMIO
+		 && ahc->bsh.maddr != NULL) {
+			iounmap(ahc->bsh.maddr);
+			release_mem_region(ahc->platform_data->mem_busaddr,
+					   0x1000);
+		}
+
+		if (ahc->platform_data->host)
+			scsi_host_put(ahc->platform_data->host);
+
+		kfree(ahc->platform_data);
+	}
+}
+
+void
+ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+	ahc_platform_abort_scbs(ahc, SCB_GET_TARGET(ahc, scb),
+				SCB_GET_CHANNEL(ahc, scb),
+				SCB_GET_LUN(scb), SCB_LIST_NULL,
+				ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev,
+		      struct ahc_devinfo *devinfo, ahc_queue_alg alg)
+{
+	struct ahc_linux_device *dev;
+	int was_queuing;
+	int now_queuing;
+
+	if (sdev == NULL)
+		return;
+	dev = scsi_transport_device_data(sdev);
+
+	was_queuing = dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED);
+	switch (alg) {
+	default:
+	case AHC_QUEUE_NONE:
+		now_queuing = 0;
+		break; 
+	case AHC_QUEUE_BASIC:
+		now_queuing = AHC_DEV_Q_BASIC;
+		break;
+	case AHC_QUEUE_TAGGED:
+		now_queuing = AHC_DEV_Q_TAGGED;
+		break;
+	}
+	if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) == 0
+	 && (was_queuing != now_queuing)
+	 && (dev->active != 0)) {
+		dev->flags |= AHC_DEV_FREEZE_TIL_EMPTY;
+		dev->qfrozen++;
+	}
+
+	dev->flags &= ~(AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED|AHC_DEV_PERIODIC_OTAG);
+	if (now_queuing) {
+		u_int usertags;
+
+		usertags = ahc_linux_user_tagdepth(ahc, devinfo);
+		if (!was_queuing) {
+			/*
+			 * Start out aggressively and allow our
+			 * dynamic queue depth algorithm to take
+			 * care of the rest.
+			 */
+			dev->maxtags = usertags;
+			dev->openings = dev->maxtags - dev->active;
+		}
+		if (dev->maxtags == 0) {
+			/*
+			 * Queueing is disabled by the user.
+			 */
+			dev->openings = 1;
+		} else if (alg == AHC_QUEUE_TAGGED) {
+			dev->flags |= AHC_DEV_Q_TAGGED;
+			if (aic7xxx_periodic_otag != 0)
+				dev->flags |= AHC_DEV_PERIODIC_OTAG;
+		} else
+			dev->flags |= AHC_DEV_Q_BASIC;
+	} else {
+		/* We can only have one opening. */
+		dev->maxtags = 0;
+		dev->openings =  1 - dev->active;
+	}
+	switch ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED))) {
+	case AHC_DEV_Q_BASIC:
+	case AHC_DEV_Q_TAGGED:
+		scsi_change_queue_depth(sdev,
+				dev->openings + dev->active);
+		break;
+	default:
+		/*
+		 * We allow the OS to queue 2 untagged transactions to
+		 * us at any time even though we can only execute them
+		 * serially on the controller/device.  This should
+		 * remove some latency.
+		 */
+		scsi_change_queue_depth(sdev, 2);
+		break;
+	}
+}
+
+int
+ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+			int lun, u_int tag, role_t role, uint32_t status)
+{
+	return 0;
+}
+
+static u_int
+ahc_linux_user_tagdepth(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	static int warned_user;
+	u_int tags;
+
+	tags = 0;
+	if ((ahc->user_discenable & devinfo->target_mask) != 0) {
+		if (ahc->unit >= ARRAY_SIZE(aic7xxx_tag_info)) {
+			if (warned_user == 0) {
+
+				printk(KERN_WARNING
+"aic7xxx: WARNING: Insufficient tag_info instances\n"
+"aic7xxx: for installed controllers. Using defaults\n"
+"aic7xxx: Please update the aic7xxx_tag_info array in\n"
+"aic7xxx: the aic7xxx_osm..c source file.\n");
+				warned_user++;
+			}
+			tags = AHC_MAX_QUEUE;
+		} else {
+			adapter_tag_info_t *tag_info;
+
+			tag_info = &aic7xxx_tag_info[ahc->unit];
+			tags = tag_info->tag_commands[devinfo->target_offset];
+			if (tags > AHC_MAX_QUEUE)
+				tags = AHC_MAX_QUEUE;
+		}
+	}
+	return (tags);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahc_linux_device_queue_depth(struct scsi_device *sdev)
+{
+	struct	ahc_devinfo devinfo;
+	u_int	tags;
+	struct ahc_softc *ahc = *((struct ahc_softc **)sdev->host->hostdata);
+
+	ahc_compile_devinfo(&devinfo,
+			    sdev->sdev_target->channel == 0
+			  ? ahc->our_id : ahc->our_id_b,
+			    sdev->sdev_target->id, sdev->lun,
+			    sdev->sdev_target->channel == 0 ? 'A' : 'B',
+			    ROLE_INITIATOR);
+	tags = ahc_linux_user_tagdepth(ahc, &devinfo);
+	if (tags != 0 && sdev->tagged_supported != 0) {
+
+		ahc_platform_set_tags(ahc, sdev, &devinfo, AHC_QUEUE_TAGGED);
+		ahc_send_async(ahc, devinfo.channel, devinfo.target,
+			       devinfo.lun, AC_TRANSFER_NEG);
+		ahc_print_devinfo(ahc, &devinfo);
+		printk("Tagged Queuing enabled.  Depth %d\n", tags);
+	} else {
+		ahc_platform_set_tags(ahc, sdev, &devinfo, AHC_QUEUE_NONE);
+		ahc_send_async(ahc, devinfo.channel, devinfo.target,
+			       devinfo.lun, AC_TRANSFER_NEG);
+	}
+}
+
+static int
+ahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev,
+		      struct scsi_cmnd *cmd)
+{
+	struct	 scb *scb;
+	struct	 hardware_scb *hscb;
+	struct	 ahc_initiator_tinfo *tinfo;
+	struct	 ahc_tmode_tstate *tstate;
+	uint16_t mask;
+	struct scb_tailq *untagged_q = NULL;
+	int nseg;
+
+	/*
+	 * Schedule us to run later.  The only reason we are not
+	 * running is because the whole controller Q is frozen.
+	 */
+	if (ahc->platform_data->qfrozen != 0)
+		return SCSI_MLQUEUE_HOST_BUSY;
+
+	/*
+	 * We only allow one untagged transaction
+	 * per target in the initiator role unless
+	 * we are storing a full busy target *lun*
+	 * table in SCB space.
+	 */
+	if (!(cmd->flags & SCMD_TAGGED)
+	    && (ahc->features & AHC_SCB_BTT) == 0) {
+		int target_offset;
+
+		target_offset = cmd->device->id + cmd->device->channel * 8;
+		untagged_q = &(ahc->untagged_queues[target_offset]);
+		if (!TAILQ_EMPTY(untagged_q))
+			/* if we're already executing an untagged command
+			 * we're busy to another */
+			return SCSI_MLQUEUE_DEVICE_BUSY;
+	}
+
+	nseg = scsi_dma_map(cmd);
+	if (nseg < 0)
+		return SCSI_MLQUEUE_HOST_BUSY;
+
+	/*
+	 * Get an scb to use.
+	 */
+	scb = ahc_get_scb(ahc);
+	if (!scb) {
+		scsi_dma_unmap(cmd);
+		return SCSI_MLQUEUE_HOST_BUSY;
+	}
+
+	scb->io_ctx = cmd;
+	scb->platform_data->dev = dev;
+	hscb = scb->hscb;
+	cmd->host_scribble = (char *)scb;
+
+	/*
+	 * Fill out basics of the HSCB.
+	 */
+	hscb->control = 0;
+	hscb->scsiid = BUILD_SCSIID(ahc, cmd);
+	hscb->lun = cmd->device->lun;
+	mask = SCB_GET_TARGET_MASK(ahc, scb);
+	tinfo = ahc_fetch_transinfo(ahc, SCB_GET_CHANNEL(ahc, scb),
+				    SCB_GET_OUR_ID(scb),
+				    SCB_GET_TARGET(ahc, scb), &tstate);
+	hscb->scsirate = tinfo->scsirate;
+	hscb->scsioffset = tinfo->curr.offset;
+	if ((tstate->ultraenb & mask) != 0)
+		hscb->control |= ULTRAENB;
+	
+	if ((ahc->user_discenable & mask) != 0)
+		hscb->control |= DISCENB;
+	
+	if ((tstate->auto_negotiate & mask) != 0) {
+		scb->flags |= SCB_AUTO_NEGOTIATE;
+		scb->hscb->control |= MK_MESSAGE;
+	}
+
+	if ((dev->flags & (AHC_DEV_Q_TAGGED|AHC_DEV_Q_BASIC)) != 0) {
+		if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH
+				&& (dev->flags & AHC_DEV_Q_TAGGED) != 0) {
+			hscb->control |= MSG_ORDERED_TASK;
+			dev->commands_since_idle_or_otag = 0;
+		} else {
+			hscb->control |= MSG_SIMPLE_TASK;
+		}
+	}
+
+	hscb->cdb_len = cmd->cmd_len;
+	if (hscb->cdb_len <= 12) {
+		memcpy(hscb->shared_data.cdb, cmd->cmnd, hscb->cdb_len);
+	} else {
+		memcpy(hscb->cdb32, cmd->cmnd, hscb->cdb_len);
+		scb->flags |= SCB_CDB32_PTR;
+	}
+
+	scb->platform_data->xfer_len = 0;
+	ahc_set_residual(scb, 0);
+	ahc_set_sense_residual(scb, 0);
+	scb->sg_count = 0;
+
+	if (nseg > 0) {
+		struct	ahc_dma_seg *sg;
+		struct	scatterlist *cur_seg;
+		int i;
+
+		/* Copy the segments into the SG list. */
+		sg = scb->sg_list;
+		/*
+		 * The sg_count may be larger than nseg if
+		 * a transfer crosses a 32bit page.
+		 */
+		scsi_for_each_sg(cmd, cur_seg, nseg, i) {
+			dma_addr_t addr;
+			bus_size_t len;
+			int consumed;
+
+			addr = sg_dma_address(cur_seg);
+			len = sg_dma_len(cur_seg);
+			consumed = ahc_linux_map_seg(ahc, scb,
+						     sg, addr, len);
+			sg += consumed;
+			scb->sg_count += consumed;
+		}
+		sg--;
+		sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+		/*
+		 * Reset the sg list pointer.
+		 */
+		scb->hscb->sgptr =
+			ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+		
+		/*
+		 * Copy the first SG into the "current"
+		 * data pointer area.
+		 */
+		scb->hscb->dataptr = scb->sg_list->addr;
+		scb->hscb->datacnt = scb->sg_list->len;
+	} else {
+		scb->hscb->sgptr = ahc_htole32(SG_LIST_NULL);
+		scb->hscb->dataptr = 0;
+		scb->hscb->datacnt = 0;
+		scb->sg_count = 0;
+	}
+
+	LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);
+	dev->openings--;
+	dev->active++;
+	dev->commands_issued++;
+	if ((dev->flags & AHC_DEV_PERIODIC_OTAG) != 0)
+		dev->commands_since_idle_or_otag++;
+	
+	scb->flags |= SCB_ACTIVE;
+	if (untagged_q) {
+		TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe);
+		scb->flags |= SCB_UNTAGGEDQ;
+	}
+	ahc_queue_scb(ahc, scb);
+	return 0;
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+irqreturn_t
+ahc_linux_isr(int irq, void *dev_id)
+{
+	struct	ahc_softc *ahc;
+	u_long	flags;
+	int	ours;
+
+	ahc = (struct ahc_softc *) dev_id;
+	ahc_lock(ahc, &flags); 
+	ours = ahc_intr(ahc);
+	ahc_unlock(ahc, &flags);
+	return IRQ_RETVAL(ours);
+}
+
+void
+ahc_platform_flushwork(struct ahc_softc *ahc)
+{
+
+}
+
+void
+ahc_send_async(struct ahc_softc *ahc, char channel,
+	       u_int target, u_int lun, ac_code code)
+{
+	switch (code) {
+	case AC_TRANSFER_NEG:
+	{
+		struct	scsi_target *starget;
+		struct	ahc_linux_target *targ;
+		struct	ahc_initiator_tinfo *tinfo;
+		struct	ahc_tmode_tstate *tstate;
+		int	target_offset;
+		unsigned int target_ppr_options;
+
+		BUG_ON(target == CAM_TARGET_WILDCARD);
+
+		tinfo = ahc_fetch_transinfo(ahc, channel,
+						channel == 'A' ? ahc->our_id
+							       : ahc->our_id_b,
+						target, &tstate);
+
+		/*
+		 * Don't bother reporting results while
+		 * negotiations are still pending.
+		 */
+		if (tinfo->curr.period != tinfo->goal.period
+		 || tinfo->curr.width != tinfo->goal.width
+		 || tinfo->curr.offset != tinfo->goal.offset
+		 || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+			if (bootverbose == 0)
+				break;
+
+		/*
+		 * Don't bother reporting results that
+		 * are identical to those last reported.
+		 */
+		target_offset = target;
+		if (channel == 'B')
+			target_offset += 8;
+		starget = ahc->platform_data->starget[target_offset];
+		if (starget == NULL)
+			break;
+		targ = scsi_transport_target_data(starget);
+
+		target_ppr_options =
+			(spi_dt(starget) ? MSG_EXT_PPR_DT_REQ : 0)
+			+ (spi_qas(starget) ? MSG_EXT_PPR_QAS_REQ : 0)
+			+ (spi_iu(starget) ?  MSG_EXT_PPR_IU_REQ : 0);
+
+		if (tinfo->curr.period == spi_period(starget)
+		    && tinfo->curr.width == spi_width(starget)
+		    && tinfo->curr.offset == spi_offset(starget)
+		 && tinfo->curr.ppr_options == target_ppr_options)
+			if (bootverbose == 0)
+				break;
+
+		spi_period(starget) = tinfo->curr.period;
+		spi_width(starget) = tinfo->curr.width;
+		spi_offset(starget) = tinfo->curr.offset;
+		spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_DT_REQ ? 1 : 0;
+		spi_qas(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_QAS_REQ ? 1 : 0;
+		spi_iu(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ ? 1 : 0;
+		spi_display_xfer_agreement(starget);
+		break;
+	}
+        case AC_SENT_BDR:
+	{
+		WARN_ON(lun != CAM_LUN_WILDCARD);
+		scsi_report_device_reset(ahc->platform_data->host,
+					 channel - 'A', target);
+		break;
+	}
+        case AC_BUS_RESET:
+		if (ahc->platform_data->host != NULL) {
+			scsi_report_bus_reset(ahc->platform_data->host,
+					      channel - 'A');
+		}
+                break;
+        default:
+                panic("ahc_send_async: Unexpected async event");
+        }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahc_done(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct scsi_cmnd *cmd;
+	struct	   ahc_linux_device *dev;
+
+	LIST_REMOVE(scb, pending_links);
+	if ((scb->flags & SCB_UNTAGGEDQ) != 0) {
+		struct scb_tailq *untagged_q;
+		int target_offset;
+
+		target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+		untagged_q = &(ahc->untagged_queues[target_offset]);
+		TAILQ_REMOVE(untagged_q, scb, links.tqe);
+		BUG_ON(!TAILQ_EMPTY(untagged_q));
+	} else if ((scb->flags & SCB_ACTIVE) == 0) {
+		/*
+		 * Transactions aborted from the untagged queue may
+		 * not have been dispatched to the controller, so
+		 * only check the SCB_ACTIVE flag for tagged transactions.
+		 */
+		printk("SCB %d done'd twice\n", scb->hscb->tag);
+		ahc_dump_card_state(ahc);
+		panic("Stopping for safety");
+	}
+	cmd = scb->io_ctx;
+	dev = scb->platform_data->dev;
+	dev->active--;
+	dev->openings++;
+	if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) {
+		cmd->result &= ~(CAM_DEV_QFRZN << 16);
+		dev->qfrozen--;
+	}
+	ahc_linux_unmap_scb(ahc, scb);
+
+	/*
+	 * Guard against stale sense data.
+	 * The Linux mid-layer assumes that sense
+	 * was retrieved anytime the first byte of
+	 * the sense buffer looks "sane".
+	 */
+	cmd->sense_buffer[0] = 0;
+	if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) {
+		uint32_t amount_xferred;
+
+		amount_xferred =
+		    ahc_get_transfer_length(scb) - ahc_get_residual(scb);
+		if ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) {
+#ifdef AHC_DEBUG
+			if ((ahc_debug & AHC_SHOW_MISC) != 0) {
+				ahc_print_path(ahc, scb);
+				printk("Set CAM_UNCOR_PARITY\n");
+			}
+#endif
+			ahc_set_transaction_status(scb, CAM_UNCOR_PARITY);
+#ifdef AHC_REPORT_UNDERFLOWS
+		/*
+		 * This code is disabled by default as some
+		 * clients of the SCSI system do not properly
+		 * initialize the underflow parameter.  This
+		 * results in spurious termination of commands
+		 * that complete as expected (e.g. underflow is
+		 * allowed as command can return variable amounts
+		 * of data.
+		 */
+		} else if (amount_xferred < scb->io_ctx->underflow) {
+			u_int i;
+
+			ahc_print_path(ahc, scb);
+			printk("CDB:");
+			for (i = 0; i < scb->io_ctx->cmd_len; i++)
+				printk(" 0x%x", scb->io_ctx->cmnd[i]);
+			printk("\n");
+			ahc_print_path(ahc, scb);
+			printk("Saw underflow (%ld of %ld bytes). "
+			       "Treated as error\n",
+				ahc_get_residual(scb),
+				ahc_get_transfer_length(scb));
+			ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+#endif
+		} else {
+			ahc_set_transaction_status(scb, CAM_REQ_CMP);
+		}
+	} else if (ahc_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) {
+		ahc_linux_handle_scsi_status(ahc, cmd->device, scb);
+	}
+
+	if (dev->openings == 1
+	 && ahc_get_transaction_status(scb) == CAM_REQ_CMP
+	 && ahc_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+		dev->tag_success_count++;
+	/*
+	 * Some devices deal with temporary internal resource
+	 * shortages by returning queue full.  When the queue
+	 * full occurrs, we throttle back.  Slowly try to get
+	 * back to our previous queue depth.
+	 */
+	if ((dev->openings + dev->active) < dev->maxtags
+	 && dev->tag_success_count > AHC_TAG_SUCCESS_INTERVAL) {
+		dev->tag_success_count = 0;
+		dev->openings++;
+	}
+
+	if (dev->active == 0)
+		dev->commands_since_idle_or_otag = 0;
+
+	if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+		printk("Recovery SCB completes\n");
+		if (ahc_get_transaction_status(scb) == CAM_BDR_SENT
+		 || ahc_get_transaction_status(scb) == CAM_REQ_ABORTED)
+			ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+
+		if (ahc->platform_data->eh_done)
+			complete(ahc->platform_data->eh_done);
+	}
+
+	ahc_free_scb(ahc, scb);
+	ahc_linux_queue_cmd_complete(ahc, cmd);
+}
+
+static void
+ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
+			     struct scsi_device *sdev, struct scb *scb)
+{
+	struct	ahc_devinfo devinfo;
+	struct ahc_linux_device *dev = scsi_transport_device_data(sdev);
+
+	ahc_compile_devinfo(&devinfo,
+			    ahc->our_id,
+			    sdev->sdev_target->id, sdev->lun,
+			    sdev->sdev_target->channel == 0 ? 'A' : 'B',
+			    ROLE_INITIATOR);
+	
+	/*
+	 * We don't currently trust the mid-layer to
+	 * properly deal with queue full or busy.  So,
+	 * when one occurs, we tell the mid-layer to
+	 * unconditionally requeue the command to us
+	 * so that we can retry it ourselves.  We also
+	 * implement our own throttling mechanism so
+	 * we don't clobber the device with too many
+	 * commands.
+	 */
+	switch (ahc_get_scsi_status(scb)) {
+	default:
+		break;
+	case SCSI_STATUS_CHECK_COND:
+	case SCSI_STATUS_CMD_TERMINATED:
+	{
+		struct scsi_cmnd *cmd;
+
+		/*
+		 * Copy sense information to the OS's cmd
+		 * structure if it is available.
+		 */
+		cmd = scb->io_ctx;
+		if (scb->flags & SCB_SENSE) {
+			u_int sense_size;
+
+			sense_size = min(sizeof(struct scsi_sense_data)
+				       - ahc_get_sense_residual(scb),
+					 (u_long)SCSI_SENSE_BUFFERSIZE);
+			memcpy(cmd->sense_buffer,
+			       ahc_get_sense_buf(ahc, scb), sense_size);
+			if (sense_size < SCSI_SENSE_BUFFERSIZE)
+				memset(&cmd->sense_buffer[sense_size], 0,
+				       SCSI_SENSE_BUFFERSIZE - sense_size);
+			cmd->result |= (DRIVER_SENSE << 24);
+#ifdef AHC_DEBUG
+			if (ahc_debug & AHC_SHOW_SENSE) {
+				int i;
+
+				printk("Copied %d bytes of sense data:",
+				       sense_size);
+				for (i = 0; i < sense_size; i++) {
+					if ((i & 0xF) == 0)
+						printk("\n");
+					printk("0x%x ", cmd->sense_buffer[i]);
+				}
+				printk("\n");
+			}
+#endif
+		}
+		break;
+	}
+	case SCSI_STATUS_QUEUE_FULL:
+	{
+		/*
+		 * By the time the core driver has returned this
+		 * command, all other commands that were queued
+		 * to us but not the device have been returned.
+		 * This ensures that dev->active is equal to
+		 * the number of commands actually queued to
+		 * the device.
+		 */
+		dev->tag_success_count = 0;
+		if (dev->active != 0) {
+			/*
+			 * Drop our opening count to the number
+			 * of commands currently outstanding.
+			 */
+			dev->openings = 0;
+/*
+			ahc_print_path(ahc, scb);
+			printk("Dropping tag count to %d\n", dev->active);
+ */
+			if (dev->active == dev->tags_on_last_queuefull) {
+
+				dev->last_queuefull_same_count++;
+				/*
+				 * If we repeatedly see a queue full
+				 * at the same queue depth, this
+				 * device has a fixed number of tag
+				 * slots.  Lock in this tag depth
+				 * so we stop seeing queue fulls from
+				 * this device.
+				 */
+				if (dev->last_queuefull_same_count
+				 == AHC_LOCK_TAGS_COUNT) {
+					dev->maxtags = dev->active;
+					ahc_print_path(ahc, scb);
+					printk("Locking max tag count at %d\n",
+					       dev->active);
+				}
+			} else {
+				dev->tags_on_last_queuefull = dev->active;
+				dev->last_queuefull_same_count = 0;
+			}
+			ahc_set_transaction_status(scb, CAM_REQUEUE_REQ);
+			ahc_set_scsi_status(scb, SCSI_STATUS_OK);
+			ahc_platform_set_tags(ahc, sdev, &devinfo,
+				     (dev->flags & AHC_DEV_Q_BASIC)
+				   ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
+			break;
+		}
+		/*
+		 * Drop down to a single opening, and treat this
+		 * as if the target returned BUSY SCSI status.
+		 */
+		dev->openings = 1;
+		ahc_set_scsi_status(scb, SCSI_STATUS_BUSY);
+		ahc_platform_set_tags(ahc, sdev, &devinfo,
+			     (dev->flags & AHC_DEV_Q_BASIC)
+			   ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED);
+		break;
+	}
+	}
+}
+
+static void
+ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, struct scsi_cmnd *cmd)
+{
+	/*
+	 * Map CAM error codes into Linux Error codes.  We
+	 * avoid the conversion so that the DV code has the
+	 * full error information available when making
+	 * state change decisions.
+	 */
+	{
+		u_int new_status;
+
+		switch (ahc_cmd_get_transaction_status(cmd)) {
+		case CAM_REQ_INPROG:
+		case CAM_REQ_CMP:
+		case CAM_SCSI_STATUS_ERROR:
+			new_status = DID_OK;
+			break;
+		case CAM_REQ_ABORTED:
+			new_status = DID_ABORT;
+			break;
+		case CAM_BUSY:
+			new_status = DID_BUS_BUSY;
+			break;
+		case CAM_REQ_INVALID:
+		case CAM_PATH_INVALID:
+			new_status = DID_BAD_TARGET;
+			break;
+		case CAM_SEL_TIMEOUT:
+			new_status = DID_NO_CONNECT;
+			break;
+		case CAM_SCSI_BUS_RESET:
+		case CAM_BDR_SENT:
+			new_status = DID_RESET;
+			break;
+		case CAM_UNCOR_PARITY:
+			new_status = DID_PARITY;
+			break;
+		case CAM_CMD_TIMEOUT:
+			new_status = DID_TIME_OUT;
+			break;
+		case CAM_UA_ABORT:
+		case CAM_REQ_CMP_ERR:
+		case CAM_AUTOSENSE_FAIL:
+		case CAM_NO_HBA:
+		case CAM_DATA_RUN_ERR:
+		case CAM_UNEXP_BUSFREE:
+		case CAM_SEQUENCE_FAIL:
+		case CAM_CCB_LEN_ERR:
+		case CAM_PROVIDE_FAIL:
+		case CAM_REQ_TERMIO:
+		case CAM_UNREC_HBA_ERROR:
+		case CAM_REQ_TOO_BIG:
+			new_status = DID_ERROR;
+			break;
+		case CAM_REQUEUE_REQ:
+			new_status = DID_REQUEUE;
+			break;
+		default:
+			/* We should never get here */
+			new_status = DID_ERROR;
+			break;
+		}
+
+		ahc_cmd_set_transaction_status(cmd, new_status);
+	}
+
+	cmd->scsi_done(cmd);
+}
+
+static void
+ahc_linux_freeze_simq(struct ahc_softc *ahc)
+{
+	unsigned long s;
+
+	ahc_lock(ahc, &s);
+	ahc->platform_data->qfrozen++;
+	if (ahc->platform_data->qfrozen == 1) {
+		scsi_block_requests(ahc->platform_data->host);
+
+		/* XXX What about Twin channels? */
+		ahc_platform_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+					CAM_LUN_WILDCARD, SCB_LIST_NULL,
+					ROLE_INITIATOR, CAM_REQUEUE_REQ);
+	}
+	ahc_unlock(ahc, &s);
+}
+
+static void
+ahc_linux_release_simq(struct ahc_softc *ahc)
+{
+	u_long s;
+	int    unblock_reqs;
+
+	unblock_reqs = 0;
+	ahc_lock(ahc, &s);
+	if (ahc->platform_data->qfrozen > 0)
+		ahc->platform_data->qfrozen--;
+	if (ahc->platform_data->qfrozen == 0)
+		unblock_reqs = 1;
+	ahc_unlock(ahc, &s);
+	/*
+	 * There is still a race here.  The mid-layer
+	 * should keep its own freeze count and use
+	 * a bottom half handler to run the queues
+	 * so we can unblock with our own lock held.
+	 */
+	if (unblock_reqs)
+		scsi_unblock_requests(ahc->platform_data->host);
+}
+
+static int
+ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
+{
+	struct ahc_softc *ahc;
+	struct ahc_linux_device *dev;
+	struct scb *pending_scb;
+	u_int  saved_scbptr;
+	u_int  active_scb_index;
+	u_int  last_phase;
+	u_int  saved_scsiid;
+	u_int  cdb_byte;
+	int    retval;
+	int    was_paused;
+	int    paused;
+	int    wait;
+	int    disconnected;
+	unsigned long flags;
+
+	pending_scb = NULL;
+	paused = FALSE;
+	wait = FALSE;
+	ahc = *(struct ahc_softc **)cmd->device->host->hostdata;
+
+	scmd_printk(KERN_INFO, cmd, "Attempting to queue a%s message\n",
+	       flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
+
+	printk("CDB:");
+	for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++)
+		printk(" 0x%x", cmd->cmnd[cdb_byte]);
+	printk("\n");
+
+	ahc_lock(ahc, &flags);
+
+	/*
+	 * First determine if we currently own this command.
+	 * Start by searching the device queue.  If not found
+	 * there, check the pending_scb list.  If not found
+	 * at all, and the system wanted us to just abort the
+	 * command, return success.
+	 */
+	dev = scsi_transport_device_data(cmd->device);
+
+	if (dev == NULL) {
+		/*
+		 * No target device for this command exists,
+		 * so we must not still own the command.
+		 */
+		printk("%s:%d:%d:%d: Is not an active device\n",
+		       ahc_name(ahc), cmd->device->channel, cmd->device->id,
+		       (u8)cmd->device->lun);
+		retval = SUCCESS;
+		goto no_cmd;
+	}
+
+	if ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0
+	 && ahc_search_untagged_queues(ahc, cmd, cmd->device->id,
+				       cmd->device->channel + 'A',
+				       (u8)cmd->device->lun,
+				       CAM_REQ_ABORTED, SEARCH_COMPLETE) != 0) {
+		printk("%s:%d:%d:%d: Command found on untagged queue\n",
+		       ahc_name(ahc), cmd->device->channel, cmd->device->id,
+		       (u8)cmd->device->lun);
+		retval = SUCCESS;
+		goto done;
+	}
+
+	/*
+	 * See if we can find a matching cmd in the pending list.
+	 */
+	LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+		if (pending_scb->io_ctx == cmd)
+			break;
+	}
+
+	if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {
+
+		/* Any SCB for this device will do for a target reset */
+		LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+		  	if (ahc_match_scb(ahc, pending_scb, scmd_id(cmd),
+					  scmd_channel(cmd) + 'A',
+					  CAM_LUN_WILDCARD,
+					  SCB_LIST_NULL, ROLE_INITIATOR))
+				break;
+		}
+	}
+
+	if (pending_scb == NULL) {
+		scmd_printk(KERN_INFO, cmd, "Command not found\n");
+		goto no_cmd;
+	}
+
+	if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+		/*
+		 * We can't queue two recovery actions using the same SCB
+		 */
+		retval = FAILED;
+		goto  done;
+	}
+
+	/*
+	 * Ensure that the card doesn't do anything
+	 * behind our back and that we didn't "just" miss
+	 * an interrupt that would affect this cmd.
+	 */
+	was_paused = ahc_is_paused(ahc);
+	ahc_pause_and_flushwork(ahc);
+	paused = TRUE;
+
+	if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+		scmd_printk(KERN_INFO, cmd, "Command already completed\n");
+		goto no_cmd;
+	}
+
+	printk("%s: At time of recovery, card was %spaused\n",
+	       ahc_name(ahc), was_paused ? "" : "not ");
+	ahc_dump_card_state(ahc);
+
+	disconnected = TRUE;
+	if (flag == SCB_ABORT) {
+		if (ahc_search_qinfifo(ahc, cmd->device->id,
+				       cmd->device->channel + 'A',
+				       cmd->device->lun,
+				       pending_scb->hscb->tag,
+				       ROLE_INITIATOR, CAM_REQ_ABORTED,
+				       SEARCH_COMPLETE) > 0) {
+			printk("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+			       ahc_name(ahc), cmd->device->channel,
+			       cmd->device->id, (u8)cmd->device->lun);
+			retval = SUCCESS;
+			goto done;
+		}
+	} else if (ahc_search_qinfifo(ahc, cmd->device->id,
+				      cmd->device->channel + 'A',
+				      cmd->device->lun,
+				      pending_scb->hscb->tag,
+				      ROLE_INITIATOR, /*status*/0,
+				      SEARCH_COUNT) > 0) {
+		disconnected = FALSE;
+	}
+
+	if (disconnected && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) {
+		struct scb *bus_scb;
+
+		bus_scb = ahc_lookup_scb(ahc, ahc_inb(ahc, SCB_TAG));
+		if (bus_scb == pending_scb)
+			disconnected = FALSE;
+		else if (flag != SCB_ABORT
+		      && ahc_inb(ahc, SAVED_SCSIID) == pending_scb->hscb->scsiid
+		      && ahc_inb(ahc, SAVED_LUN) == SCB_GET_LUN(pending_scb))
+			disconnected = FALSE;
+	}
+
+	/*
+	 * At this point, pending_scb is the scb associated with the
+	 * passed in command.  That command is currently active on the
+	 * bus, is in the disconnected state, or we're hoping to find
+	 * a command for the same target active on the bus to abuse to
+	 * send a BDR.  Queue the appropriate message based on which of
+	 * these states we are in.
+	 */
+	last_phase = ahc_inb(ahc, LASTPHASE);
+	saved_scbptr = ahc_inb(ahc, SCBPTR);
+	active_scb_index = ahc_inb(ahc, SCB_TAG);
+	saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+	if (last_phase != P_BUSFREE
+	 && (pending_scb->hscb->tag == active_scb_index
+	  || (flag == SCB_DEVICE_RESET
+	   && SCSIID_TARGET(ahc, saved_scsiid) == scmd_id(cmd)))) {
+
+		/*
+		 * We're active on the bus, so assert ATN
+		 * and hope that the target responds.
+		 */
+		pending_scb = ahc_lookup_scb(ahc, active_scb_index);
+		pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+		ahc_outb(ahc, MSG_OUT, HOST_MSG);
+		ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
+		scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n");
+		wait = TRUE;
+	} else if (disconnected) {
+
+		/*
+		 * Actually re-queue this SCB in an attempt
+		 * to select the device before it reconnects.
+		 * In either case (selection or reselection),
+		 * we will now issue the approprate message
+		 * to the timed-out device.
+		 *
+		 * Set the MK_MESSAGE control bit indicating
+		 * that we desire to send a message.  We
+		 * also set the disconnected flag since
+		 * in the paging case there is no guarantee
+		 * that our SCB control byte matches the
+		 * version on the card.  We don't want the
+		 * sequencer to abort the command thinking
+		 * an unsolicited reselection occurred.
+		 */
+		pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+		pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+
+		/*
+		 * Remove any cached copy of this SCB in the
+		 * disconnected list in preparation for the
+		 * queuing of our abort SCB.  We use the
+		 * same element in the SCB, SCB_NEXT, for
+		 * both the qinfifo and the disconnected list.
+		 */
+		ahc_search_disc_list(ahc, cmd->device->id,
+				     cmd->device->channel + 'A',
+				     cmd->device->lun, pending_scb->hscb->tag,
+				     /*stop_on_first*/TRUE,
+				     /*remove*/TRUE,
+				     /*save_state*/FALSE);
+
+		/*
+		 * In the non-paging case, the sequencer will
+		 * never re-reference the in-core SCB.
+		 * To make sure we are notified during
+		 * reselection, set the MK_MESSAGE flag in
+		 * the card's copy of the SCB.
+		 */
+		if ((ahc->flags & AHC_PAGESCBS) == 0) {
+			ahc_outb(ahc, SCBPTR, pending_scb->hscb->tag);
+			ahc_outb(ahc, SCB_CONTROL,
+				 ahc_inb(ahc, SCB_CONTROL)|MK_MESSAGE);
+		}
+
+		/*
+		 * Clear out any entries in the QINFIFO first
+		 * so we are the next SCB for this target
+		 * to run.
+		 */
+		ahc_search_qinfifo(ahc, cmd->device->id,
+				   cmd->device->channel + 'A',
+				   cmd->device->lun, SCB_LIST_NULL,
+				   ROLE_INITIATOR, CAM_REQUEUE_REQ,
+				   SEARCH_COMPLETE);
+		ahc_qinfifo_requeue_tail(ahc, pending_scb);
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+		ahc_print_path(ahc, pending_scb);
+		printk("Device is disconnected, re-queuing SCB\n");
+		wait = TRUE;
+	} else {
+		scmd_printk(KERN_INFO, cmd, "Unable to deliver message\n");
+		retval = FAILED;
+		goto done;
+	}
+
+no_cmd:
+	/*
+	 * Our assumption is that if we don't have the command, no
+	 * recovery action was required, so we return success.  Again,
+	 * the semantics of the mid-layer recovery engine are not
+	 * well defined, so this may change in time.
+	 */
+	retval = SUCCESS;
+done:
+	if (paused)
+		ahc_unpause(ahc);
+	if (wait) {
+		DECLARE_COMPLETION_ONSTACK(done);
+
+		ahc->platform_data->eh_done = &done;
+		ahc_unlock(ahc, &flags);
+
+		printk("Recovery code sleeping\n");
+		if (!wait_for_completion_timeout(&done, 5 * HZ)) {
+			ahc_lock(ahc, &flags);
+			ahc->platform_data->eh_done = NULL;
+			ahc_unlock(ahc, &flags);
+
+			printk("Timer Expired\n");
+			retval = FAILED;
+		}
+		printk("Recovery code awake\n");
+	} else
+		ahc_unlock(ahc, &flags);
+	return (retval);
+}
+
+void
+ahc_platform_dump_card_state(struct ahc_softc *ahc)
+{
+}
+
+static void ahc_linux_set_width(struct scsi_target *starget, int width)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+	struct ahc_devinfo devinfo;
+	unsigned long flags;
+
+	ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	ahc_lock(ahc, &flags);
+	ahc_set_width(ahc, &devinfo, width, AHC_TRANS_GOAL, FALSE);
+	ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_set_period(struct scsi_target *starget, int period)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+	struct ahc_tmode_tstate *tstate;
+	struct ahc_initiator_tinfo *tinfo 
+		= ahc_fetch_transinfo(ahc,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahc_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options;
+	unsigned long flags;
+	unsigned long offset = tinfo->goal.offset;
+	const struct ahc_syncrate *syncrate;
+
+	if (offset == 0)
+		offset = MAX_OFFSET;
+
+	if (period < 9)
+		period = 9;	/* 12.5ns is our minimum */
+	if (period == 9) {
+		if (spi_max_width(starget))
+			ppr_options |= MSG_EXT_PPR_DT_REQ;
+		else
+			/* need wide for DT and need DT for 12.5 ns */
+			period = 10;
+	}
+
+	ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+
+	/* all PPR requests apart from QAS require wide transfers */
+	if (ppr_options & ~MSG_EXT_PPR_QAS_REQ) {
+		if (spi_width(starget) == 0)
+			ppr_options &= MSG_EXT_PPR_QAS_REQ;
+	}
+
+	syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+	ahc_lock(ahc, &flags);
+	ahc_set_syncrate(ahc, &devinfo, syncrate, period, offset,
+			 ppr_options, AHC_TRANS_GOAL, FALSE);
+	ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_set_offset(struct scsi_target *starget, int offset)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+	struct ahc_tmode_tstate *tstate;
+	struct ahc_initiator_tinfo *tinfo 
+		= ahc_fetch_transinfo(ahc,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahc_devinfo devinfo;
+	unsigned int ppr_options = 0;
+	unsigned int period = 0;
+	unsigned long flags;
+	const struct ahc_syncrate *syncrate = NULL;
+
+	ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	if (offset != 0) {
+		syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+		period = tinfo->goal.period;
+		ppr_options = tinfo->goal.ppr_options;
+	}
+	ahc_lock(ahc, &flags);
+	ahc_set_syncrate(ahc, &devinfo, syncrate, period, offset,
+			 ppr_options, AHC_TRANS_GOAL, FALSE);
+	ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_set_dt(struct scsi_target *starget, int dt)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+	struct ahc_tmode_tstate *tstate;
+	struct ahc_initiator_tinfo *tinfo 
+		= ahc_fetch_transinfo(ahc,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahc_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_DT_REQ;
+	unsigned int period = tinfo->goal.period;
+	unsigned int width = tinfo->goal.width;
+	unsigned long flags;
+	const struct ahc_syncrate *syncrate;
+
+	if (dt && spi_max_width(starget)) {
+		ppr_options |= MSG_EXT_PPR_DT_REQ;
+		if (!width)
+			ahc_linux_set_width(starget, 1);
+	} else if (period == 9)
+		period = 10;	/* if resetting DT, period must be >= 25ns */
+
+	ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,AHC_SYNCRATE_DT);
+	ahc_lock(ahc, &flags);
+	ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->goal.offset,
+			 ppr_options, AHC_TRANS_GOAL, FALSE);
+	ahc_unlock(ahc, &flags);
+}
+
+#if 0
+/* FIXME: This code claims to support IU and QAS.  However, the actual
+ * sequencer code and aic7xxx_core have no support for these parameters and
+ * will get into a bad state if they're negotiated.  Do not enable this
+ * unless you know what you're doing */
+static void ahc_linux_set_qas(struct scsi_target *starget, int qas)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+	struct ahc_tmode_tstate *tstate;
+	struct ahc_initiator_tinfo *tinfo 
+		= ahc_fetch_transinfo(ahc,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahc_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_QAS_REQ;
+	unsigned int period = tinfo->goal.period;
+	unsigned long flags;
+	struct ahc_syncrate *syncrate;
+
+	if (qas)
+		ppr_options |= MSG_EXT_PPR_QAS_REQ;
+
+	ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+	ahc_lock(ahc, &flags);
+	ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->goal.offset,
+			 ppr_options, AHC_TRANS_GOAL, FALSE);
+	ahc_unlock(ahc, &flags);
+}
+
+static void ahc_linux_set_iu(struct scsi_target *starget, int iu)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata);
+	struct ahc_tmode_tstate *tstate;
+	struct ahc_initiator_tinfo *tinfo 
+		= ahc_fetch_transinfo(ahc,
+				      starget->channel + 'A',
+				      shost->this_id, starget->id, &tstate);
+	struct ahc_devinfo devinfo;
+	unsigned int ppr_options = tinfo->goal.ppr_options
+		& ~MSG_EXT_PPR_IU_REQ;
+	unsigned int period = tinfo->goal.period;
+	unsigned long flags;
+	struct ahc_syncrate *syncrate;
+
+	if (iu)
+		ppr_options |= MSG_EXT_PPR_IU_REQ;
+
+	ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0,
+			    starget->channel + 'A', ROLE_INITIATOR);
+	syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT);
+	ahc_lock(ahc, &flags);
+	ahc_set_syncrate(ahc, &devinfo, syncrate, period, tinfo->goal.offset,
+			 ppr_options, AHC_TRANS_GOAL, FALSE);
+	ahc_unlock(ahc, &flags);
+}
+#endif
+
+static void ahc_linux_get_signalling(struct Scsi_Host *shost)
+{
+	struct ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata;
+	unsigned long flags;
+	u8 mode;
+
+	if (!(ahc->features & AHC_ULTRA2)) {
+		/* non-LVD chipset, may not have SBLKCTL reg */
+		spi_signalling(shost) = 
+			ahc->features & AHC_HVD ?
+			SPI_SIGNAL_HVD :
+			SPI_SIGNAL_SE;
+		return;
+	}
+
+	ahc_lock(ahc, &flags);
+	ahc_pause(ahc);
+	mode = ahc_inb(ahc, SBLKCTL);
+	ahc_unpause(ahc);
+	ahc_unlock(ahc, &flags);
+
+	if (mode & ENAB40)
+		spi_signalling(shost) = SPI_SIGNAL_LVD;
+	else if (mode & ENAB20)
+		spi_signalling(shost) = SPI_SIGNAL_SE;
+	else
+		spi_signalling(shost) = SPI_SIGNAL_UNKNOWN;
+}
+
+static struct spi_function_template ahc_linux_transport_functions = {
+	.set_offset	= ahc_linux_set_offset,
+	.show_offset	= 1,
+	.set_period	= ahc_linux_set_period,
+	.show_period	= 1,
+	.set_width	= ahc_linux_set_width,
+	.show_width	= 1,
+	.set_dt		= ahc_linux_set_dt,
+	.show_dt	= 1,
+#if 0
+	.set_iu		= ahc_linux_set_iu,
+	.show_iu	= 1,
+	.set_qas	= ahc_linux_set_qas,
+	.show_qas	= 1,
+#endif
+	.get_signalling	= ahc_linux_get_signalling,
+};
+
+
+
+static int __init
+ahc_linux_init(void)
+{
+	/*
+	 * If we've been passed any parameters, process them now.
+	 */
+	if (aic7xxx)
+		aic7xxx_setup(aic7xxx);
+
+	ahc_linux_transport_template =
+		spi_attach_transport(&ahc_linux_transport_functions);
+	if (!ahc_linux_transport_template)
+		return -ENODEV;
+
+	scsi_transport_reserve_device(ahc_linux_transport_template,
+				      sizeof(struct ahc_linux_device));
+
+	ahc_linux_pci_init();
+	ahc_linux_eisa_init();
+	return 0;
+}
+
+static void
+ahc_linux_exit(void)
+{
+	ahc_linux_pci_exit();
+	ahc_linux_eisa_exit();
+	spi_release_transport(ahc_linux_transport_template);
+}
+
+module_init(ahc_linux_init);
+module_exit(ahc_linux_exit);
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h
new file mode 100644
index 0000000..bc4cca9
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h
@@ -0,0 +1,705 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ *   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, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 
+ * Copyright (c) 2000-2003 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#151 $
+ *
+ */
+#ifndef _AIC7XXX_LINUX_H_
+#define _AIC7XXX_LINUX_H_
+
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_spi.h>
+
+/* Core SCSI definitions */
+#define AIC_LIB_PREFIX ahc
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+#include "aiclib.h"
+
+/*********************************** Debugging ********************************/
+#ifdef CONFIG_AIC7XXX_DEBUG_ENABLE
+#ifdef CONFIG_AIC7XXX_DEBUG_MASK
+#define AHC_DEBUG 1
+#define AHC_DEBUG_OPTS CONFIG_AIC7XXX_DEBUG_MASK
+#else
+/*
+ * Compile in debugging code, but do not enable any printfs.
+ */
+#define AHC_DEBUG 1
+#endif
+/* No debugging code. */
+#endif
+
+/************************* Forward Declarations *******************************/
+struct ahc_softc;
+typedef struct pci_dev *ahc_dev_softc_t;
+typedef struct scsi_cmnd      *ahc_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahc_htobe16(x)	cpu_to_be16(x)
+#define ahc_htobe32(x)	cpu_to_be32(x)
+#define ahc_htobe64(x)	cpu_to_be64(x)
+#define ahc_htole16(x)	cpu_to_le16(x)
+#define ahc_htole32(x)	cpu_to_le32(x)
+#define ahc_htole64(x)	cpu_to_le64(x)
+
+#define ahc_be16toh(x)	be16_to_cpu(x)
+#define ahc_be32toh(x)	be32_to_cpu(x)
+#define ahc_be64toh(x)	be64_to_cpu(x)
+#define ahc_le16toh(x)	le16_to_cpu(x)
+#define ahc_le32toh(x)	le32_to_cpu(x)
+#define ahc_le64toh(x)	le64_to_cpu(x)
+
+/************************* Configuration Data *********************************/
+extern u_int aic7xxx_no_probe;
+extern u_int aic7xxx_allow_memio;
+extern struct scsi_host_template aic7xxx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+typedef uint32_t bus_size_t;
+
+typedef enum {
+	BUS_SPACE_MEMIO,
+	BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+	u_long		  ioport;
+	volatile uint8_t __iomem *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+	dma_addr_t	ds_addr;
+	bus_size_t	ds_len;
+} bus_dma_segment_t;
+
+struct ahc_linux_dma_tag
+{
+	bus_size_t	alignment;
+	bus_size_t	boundary;
+	bus_size_t	maxsize;
+};
+typedef struct ahc_linux_dma_tag* bus_dma_tag_t;
+
+typedef dma_addr_t bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, dma_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK		0x0
+#define BUS_DMA_NOWAIT		0x1
+#define BUS_DMA_ALLOCNOW	0x2
+#define BUS_DMA_LOAD_SEGS	0x4	/*
+					 * Argument is an S/G list not
+					 * a single buffer.
+					 */
+
+#define BUS_SPACE_MAXADDR	0xFFFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT	0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT	0xFFFFFFFF
+
+int	ahc_dma_tag_create(struct ahc_softc *, bus_dma_tag_t /*parent*/,
+			   bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+			   dma_addr_t /*lowaddr*/, dma_addr_t /*highaddr*/,
+			   bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+			   bus_size_t /*maxsize*/, int /*nsegments*/,
+			   bus_size_t /*maxsegsz*/, int /*flags*/,
+			   bus_dma_tag_t */*dma_tagp*/);
+
+void	ahc_dma_tag_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/);
+
+int	ahc_dmamem_alloc(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+			 void** /*vaddr*/, int /*flags*/,
+			 bus_dmamap_t* /*mapp*/);
+
+void	ahc_dmamem_free(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+			void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void	ahc_dmamap_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/,
+			   bus_dmamap_t /*map*/);
+
+int	ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t /*dmat*/,
+			bus_dmamap_t /*map*/, void * /*buf*/,
+			bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+			void */*callback_arg*/, int /*flags*/);
+
+int	ahc_dmamap_unload(struct ahc_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Operations performed by ahc_dmamap_sync().
+ */
+#define BUS_DMASYNC_PREREAD	0x01	/* pre-read synchronization */
+#define BUS_DMASYNC_POSTREAD	0x02	/* post-read synchronization */
+#define BUS_DMASYNC_PREWRITE	0x04	/* pre-write synchronization */
+#define BUS_DMASYNC_POSTWRITE	0x08	/* post-write synchronization */
+
+/*
+ * XXX
+ * ahc_dmamap_sync is only used on buffers allocated with
+ * the pci_alloc_consistent() API.  Although I'm not sure how
+ * this works on architectures with a write buffer, Linux does
+ * not have an API to sync "coherent" memory.  Perhaps we need
+ * to do an mb()?
+ */
+#define ahc_dmamap_sync(ahc, dma_tag, dmamap, offset, len, op)
+
+/********************************** Includes **********************************/
+#ifdef CONFIG_AIC7XXX_REG_PRETTY_PRINT
+#define AIC_DEBUG_REGISTERS 1
+#else
+#define AIC_DEBUG_REGISTERS 0
+#endif
+#include "aic7xxx.h"
+
+/***************************** Timer Facilities *******************************/
+static inline void
+ahc_scb_timer_reset(struct scb *scb, u_int usec)
+{
+}
+
+/***************************** SMP support ************************************/
+#include <linux/spinlock.h>
+
+#define AIC7XXX_DRIVER_VERSION "7.0"
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle.  The structure allocated for a device only becomes persistent
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+typedef enum {
+	AHC_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+	AHC_DEV_Q_BASIC		 = 0x10, /* Allow basic device queuing */
+	AHC_DEV_Q_TAGGED	 = 0x20, /* Allow full SCSI2 command queueing */
+	AHC_DEV_PERIODIC_OTAG	 = 0x40, /* Send OTAG to prevent starvation */
+} ahc_linux_dev_flags;
+
+struct ahc_linux_device {
+	/*
+	 * The number of transactions currently
+	 * queued to the device.
+	 */
+	int			active;
+
+	/*
+	 * The currently allowed number of 
+	 * transactions that can be queued to
+	 * the device.  Must be signed for
+	 * conversion from tagged to untagged
+	 * mode where the device may have more
+	 * than one outstanding active transaction.
+	 */
+	int			openings;
+
+	/*
+	 * A positive count indicates that this
+	 * device's queue is halted.
+	 */
+	u_int			qfrozen;
+	
+	/*
+	 * Cumulative command counter.
+	 */
+	u_long			commands_issued;
+
+	/*
+	 * The number of tagged transactions when
+	 * running at our current opening level
+	 * that have been successfully received by
+	 * this device since the last QUEUE FULL.
+	 */
+	u_int			tag_success_count;
+#define AHC_TAG_SUCCESS_INTERVAL 50
+
+	ahc_linux_dev_flags	flags;
+
+	/*
+	 * The high limit for the tags variable.
+	 */
+	u_int			maxtags;
+
+	/*
+	 * The computed number of tags outstanding
+	 * at the time of the last QUEUE FULL event.
+	 */
+	u_int			tags_on_last_queuefull;
+
+	/*
+	 * How many times we have seen a queue full
+	 * with the same number of tags.  This is used
+	 * to stop our adaptive queue depth algorithm
+	 * on devices with a fixed number of tags.
+	 */
+	u_int			last_queuefull_same_count;
+#define AHC_LOCK_TAGS_COUNT 50
+
+	/*
+	 * How many transactions have been queued
+	 * without the device going idle.  We use
+	 * this statistic to determine when to issue
+	 * an ordered tag to prevent transaction
+	 * starvation.  This statistic is only updated
+	 * if the AHC_DEV_PERIODIC_OTAG flag is set
+	 * on this device.
+	 */
+	u_int			commands_since_idle_or_otag;
+#define AHC_OTAG_THRESH	500
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require.  So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#define	AHC_NSEG 128
+
+/*
+ * Per-SCB OSM storage.
+ */
+struct scb_platform_data {
+	struct ahc_linux_device	*dev;
+	dma_addr_t		 buf_busaddr;
+	uint32_t		 xfer_len;
+	uint32_t		 sense_resid;	/* Auto-Sense residual */
+};
+
+/*
+ * Define a structure used for each host adapter.  All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+struct ahc_platform_data {
+	/*
+	 * Fields accessed from interrupt context.
+	 */
+	struct scsi_target *starget[AHC_NUM_TARGETS]; 
+
+	spinlock_t		 spin_lock;
+	u_int			 qfrozen;
+	struct completion	*eh_done;
+	struct Scsi_Host        *host;		/* pointer to scsi host */
+#define AHC_LINUX_NOIRQ	((uint32_t)~0)
+	uint32_t		 irq;		/* IRQ for this adapter */
+	uint32_t		 bios_address;
+	resource_size_t 	 mem_busaddr;	/* Mem Base Addr */
+};
+
+void ahc_delay(long);
+
+
+/***************************** Low Level I/O **********************************/
+uint8_t ahc_inb(struct ahc_softc * ahc, long port);
+void ahc_outb(struct ahc_softc * ahc, long port, uint8_t val);
+void ahc_outsb(struct ahc_softc * ahc, long port,
+	       uint8_t *, int count);
+void ahc_insb(struct ahc_softc * ahc, long port,
+	       uint8_t *, int count);
+
+/**************************** Initialization **********************************/
+int		ahc_linux_register_host(struct ahc_softc *,
+					struct scsi_host_template *);
+
+/******************************** Locking *************************************/
+/* Lock protecting internal data structures */
+
+static inline void
+ahc_lockinit(struct ahc_softc *ahc)
+{
+	spin_lock_init(&ahc->platform_data->spin_lock);
+}
+
+static inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	spin_lock_irqsave(&ahc->platform_data->spin_lock, *flags);
+}
+
+static inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	spin_unlock_irqrestore(&ahc->platform_data->spin_lock, *flags);
+}
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR		0x00
+#define PCIR_VENDOR		0x00
+#define PCIR_DEVICE		0x02
+#define PCIR_COMMAND		0x04
+#define PCIM_CMD_PORTEN		0x0001
+#define PCIM_CMD_MEMEN		0x0002
+#define PCIM_CMD_BUSMASTEREN	0x0004
+#define PCIM_CMD_MWRICEN	0x0010
+#define PCIM_CMD_PERRESPEN	0x0040
+#define	PCIM_CMD_SERRESPEN	0x0100
+#define PCIR_STATUS		0x06
+#define PCIR_REVID		0x08
+#define PCIR_PROGIF		0x09
+#define PCIR_SUBCLASS		0x0a
+#define PCIR_CLASS		0x0b
+#define PCIR_CACHELNSZ		0x0c
+#define PCIR_LATTIMER		0x0d
+#define PCIR_HEADERTYPE		0x0e
+#define PCIM_MFDEV		0x80
+#define PCIR_BIST		0x0f
+#define PCIR_CAP_PTR		0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS	0x10
+#define PCIR_SUBVEND_0	0x2c
+#define PCIR_SUBDEV_0	0x2e
+
+typedef enum
+{
+	AHC_POWER_STATE_D0,
+	AHC_POWER_STATE_D1,
+	AHC_POWER_STATE_D2,
+	AHC_POWER_STATE_D3
+} ahc_power_state;
+
+/**************************** VL/EISA Routines ********************************/
+#ifdef CONFIG_EISA
+int			 ahc_linux_eisa_init(void);
+void			 ahc_linux_eisa_exit(void);
+int			 aic7770_map_registers(struct ahc_softc *ahc,
+					       u_int port);
+int			 aic7770_map_int(struct ahc_softc *ahc, u_int irq);
+#else
+static inline int	ahc_linux_eisa_init(void) {
+	return -ENODEV;
+}
+static inline void	ahc_linux_eisa_exit(void) {
+}
+#endif
+
+/******************************* PCI Routines *********************************/
+#ifdef CONFIG_PCI
+int			 ahc_linux_pci_init(void);
+void			 ahc_linux_pci_exit(void);
+int			 ahc_pci_map_registers(struct ahc_softc *ahc);
+int			 ahc_pci_map_int(struct ahc_softc *ahc);
+
+uint32_t		 ahc_pci_read_config(ahc_dev_softc_t pci,
+					     int reg, int width);
+
+void			 ahc_pci_write_config(ahc_dev_softc_t pci,
+					      int reg, uint32_t value,
+					      int width);
+
+static inline int ahc_get_pci_function(ahc_dev_softc_t);
+static inline int
+ahc_get_pci_function(ahc_dev_softc_t pci)
+{
+	return (PCI_FUNC(pci->devfn));
+}
+
+static inline int ahc_get_pci_slot(ahc_dev_softc_t);
+static inline int
+ahc_get_pci_slot(ahc_dev_softc_t pci)
+{
+	return (PCI_SLOT(pci->devfn));
+}
+
+static inline int ahc_get_pci_bus(ahc_dev_softc_t);
+static inline int
+ahc_get_pci_bus(ahc_dev_softc_t pci)
+{
+	return (pci->bus->number);
+}
+#else
+static inline int ahc_linux_pci_init(void) {
+	return 0;
+}
+static inline void ahc_linux_pci_exit(void) {
+}
+#endif
+
+static inline void ahc_flush_device_writes(struct ahc_softc *);
+static inline void
+ahc_flush_device_writes(struct ahc_softc *ahc)
+{
+	/* XXX Is this sufficient for all architectures??? */
+	ahc_inb(ahc, INTSTAT);
+}
+
+/**************************** Proc FS Support *********************************/
+int	ahc_proc_write_seeprom(struct Scsi_Host *, char *, int);
+int	ahc_linux_show_info(struct seq_file *, struct Scsi_Host *);
+
+/*************************** Domain Validation ********************************/
+/*********************** Transaction Access Wrappers *************************/
+static inline void ahc_cmd_set_transaction_status(struct scsi_cmnd *, uint32_t);
+static inline void ahc_set_transaction_status(struct scb *, uint32_t);
+static inline void ahc_cmd_set_scsi_status(struct scsi_cmnd *, uint32_t);
+static inline void ahc_set_scsi_status(struct scb *, uint32_t);
+static inline uint32_t ahc_cmd_get_transaction_status(struct scsi_cmnd *cmd);
+static inline uint32_t ahc_get_transaction_status(struct scb *);
+static inline uint32_t ahc_cmd_get_scsi_status(struct scsi_cmnd *cmd);
+static inline uint32_t ahc_get_scsi_status(struct scb *);
+static inline void ahc_set_transaction_tag(struct scb *, int, u_int);
+static inline u_long ahc_get_transfer_length(struct scb *);
+static inline int ahc_get_transfer_dir(struct scb *);
+static inline void ahc_set_residual(struct scb *, u_long);
+static inline void ahc_set_sense_residual(struct scb *scb, u_long resid);
+static inline u_long ahc_get_residual(struct scb *);
+static inline u_long ahc_get_sense_residual(struct scb *);
+static inline int ahc_perform_autosense(struct scb *);
+static inline uint32_t ahc_get_sense_bufsize(struct ahc_softc *,
+					       struct scb *);
+static inline void ahc_notify_xfer_settings_change(struct ahc_softc *,
+						     struct ahc_devinfo *);
+static inline void ahc_platform_scb_free(struct ahc_softc *ahc,
+					   struct scb *scb);
+static inline void ahc_freeze_scb(struct scb *scb);
+
+static inline
+void ahc_cmd_set_transaction_status(struct scsi_cmnd *cmd, uint32_t status)
+{
+	cmd->result &= ~(CAM_STATUS_MASK << 16);
+	cmd->result |= status << 16;
+}
+
+static inline
+void ahc_set_transaction_status(struct scb *scb, uint32_t status)
+{
+	ahc_cmd_set_transaction_status(scb->io_ctx,status);
+}
+
+static inline
+void ahc_cmd_set_scsi_status(struct scsi_cmnd *cmd, uint32_t status)
+{
+	cmd->result &= ~0xFFFF;
+	cmd->result |= status;
+}
+
+static inline
+void ahc_set_scsi_status(struct scb *scb, uint32_t status)
+{
+	ahc_cmd_set_scsi_status(scb->io_ctx, status);
+}
+
+static inline
+uint32_t ahc_cmd_get_transaction_status(struct scsi_cmnd *cmd)
+{
+	return ((cmd->result >> 16) & CAM_STATUS_MASK);
+}
+
+static inline
+uint32_t ahc_get_transaction_status(struct scb *scb)
+{
+	return (ahc_cmd_get_transaction_status(scb->io_ctx));
+}
+
+static inline
+uint32_t ahc_cmd_get_scsi_status(struct scsi_cmnd *cmd)
+{
+	return (cmd->result & 0xFFFF);
+}
+
+static inline
+uint32_t ahc_get_scsi_status(struct scb *scb)
+{
+	return (ahc_cmd_get_scsi_status(scb->io_ctx));
+}
+
+static inline
+void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+	/*
+	 * Nothing to do for linux as the incoming transaction
+	 * has no concept of tag/non tagged, etc.
+	 */
+}
+
+static inline
+u_long ahc_get_transfer_length(struct scb *scb)
+{
+	return (scb->platform_data->xfer_len);
+}
+
+static inline
+int ahc_get_transfer_dir(struct scb *scb)
+{
+	return (scb->io_ctx->sc_data_direction);
+}
+
+static inline
+void ahc_set_residual(struct scb *scb, u_long resid)
+{
+	scsi_set_resid(scb->io_ctx, resid);
+}
+
+static inline
+void ahc_set_sense_residual(struct scb *scb, u_long resid)
+{
+	scb->platform_data->sense_resid = resid;
+}
+
+static inline
+u_long ahc_get_residual(struct scb *scb)
+{
+	return scsi_get_resid(scb->io_ctx);
+}
+
+static inline
+u_long ahc_get_sense_residual(struct scb *scb)
+{
+	return (scb->platform_data->sense_resid);
+}
+
+static inline
+int ahc_perform_autosense(struct scb *scb)
+{
+	/*
+	 * We always perform autosense in Linux.
+	 * On other platforms this is set on a
+	 * per-transaction basis.
+	 */
+	return (1);
+}
+
+static inline uint32_t
+ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb)
+{
+	return (sizeof(struct scsi_sense_data));
+}
+
+static inline void
+ahc_notify_xfer_settings_change(struct ahc_softc *ahc,
+				struct ahc_devinfo *devinfo)
+{
+	/* Nothing to do here for linux */
+}
+
+static inline void
+ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb)
+{
+}
+
+int	ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg);
+void	ahc_platform_free(struct ahc_softc *ahc);
+void	ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+
+static inline void
+ahc_freeze_scb(struct scb *scb)
+{
+	if ((scb->io_ctx->result & (CAM_DEV_QFRZN << 16)) == 0) {
+                scb->io_ctx->result |= CAM_DEV_QFRZN << 16;
+                scb->platform_data->dev->qfrozen++;
+        }
+}
+
+void	ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev,
+			      struct ahc_devinfo *devinfo, ahc_queue_alg);
+int	ahc_platform_abort_scbs(struct ahc_softc *ahc, int target,
+				char channel, int lun, u_int tag,
+				role_t role, uint32_t status);
+irqreturn_t
+	ahc_linux_isr(int irq, void *dev_id);
+void	ahc_platform_flushwork(struct ahc_softc *ahc);
+void	ahc_done(struct ahc_softc*, struct scb*);
+void	ahc_send_async(struct ahc_softc *, char channel,
+		       u_int target, u_int lun, ac_code);
+void	ahc_print_path(struct ahc_softc *, struct scb *);
+void	ahc_platform_dump_card_state(struct ahc_softc *ahc);
+
+#ifdef CONFIG_PCI
+#define AHC_PCI_CONFIG 1
+#else
+#define AHC_PCI_CONFIG 0
+#endif
+#define bootverbose aic7xxx_verbose
+extern u_int aic7xxx_verbose;
+#endif /* _AIC7XXX_LINUX_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
new file mode 100644
index 0000000..0fc14da
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
@@ -0,0 +1,470 @@
+/*
+ * Linux driver attachment glue for PCI based controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c#47 $
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_pci.h"
+
+/* Define the macro locally since it's different for different class of chips.
+*/
+#define ID(x)	ID_C(x, PCI_CLASS_STORAGE_SCSI)
+
+static const struct pci_device_id ahc_linux_pci_id_table[] = {
+	/* aic7850 based controllers */
+	ID(ID_AHA_2902_04_10_15_20C_30C),
+	/* aic7860 based controllers */
+	ID(ID_AHA_2930CU),
+	ID(ID_AHA_1480A & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK),
+	/* aic7870 based controllers */
+	ID(ID_AHA_2940),
+	ID(ID_AHA_3940),
+	ID(ID_AHA_398X),
+	ID(ID_AHA_2944),
+	ID(ID_AHA_3944),
+	ID(ID_AHA_4944),
+	/* aic7880 based controllers */
+	ID(ID_AHA_2940U & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_3940U & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_2944U & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_3944U & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_398XU & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_4944U & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_2930U & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK),
+	ID(ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK),
+	/* aic7890 based controllers */
+	ID(ID_AHA_2930U2),
+	ID(ID_AHA_2940U2B),
+	ID(ID_AHA_2940U2_OEM),
+	ID(ID_AHA_2940U2),
+	ID(ID_AHA_2950U2B),
+	ID16(ID_AIC7890_ARO & ID_AIC7895_ARO_MASK),
+	ID(ID_AAA_131U2),
+	/* aic7890 based controllers */
+	ID(ID_AHA_29160),
+	ID(ID_AHA_29160_CPQ),
+	ID(ID_AHA_29160N),
+	ID(ID_AHA_29160C),
+	ID(ID_AHA_29160B),
+	ID(ID_AHA_19160B),
+	ID(ID_AIC7892_ARO),
+	/* aic7892 based controllers */
+	ID(ID_AHA_2940U_DUAL),
+	ID(ID_AHA_3940AU),
+	ID(ID_AHA_3944AU),
+	ID(ID_AIC7895_ARO),
+	ID(ID_AHA_3950U2B_0),
+	ID(ID_AHA_3950U2B_1),
+	ID(ID_AHA_3950U2D_0),
+	ID(ID_AHA_3950U2D_1),
+	ID(ID_AIC7896_ARO),
+	/* aic7899 based controllers */
+	ID(ID_AHA_3960D),
+	ID(ID_AHA_3960D_CPQ),
+	ID(ID_AIC7899_ARO),
+	/* Generic chip probes for devices we don't know exactly. */
+	ID(ID_AIC7850 & ID_DEV_VENDOR_MASK),
+	ID(ID_AIC7855 & ID_DEV_VENDOR_MASK),
+	ID(ID_AIC7859 & ID_DEV_VENDOR_MASK),
+	ID(ID_AIC7860 & ID_DEV_VENDOR_MASK),
+	ID(ID_AIC7870 & ID_DEV_VENDOR_MASK),
+	ID(ID_AIC7880 & ID_DEV_VENDOR_MASK),
+ 	ID16(ID_AIC7890 & ID_9005_GENERIC_MASK),
+ 	ID16(ID_AIC7892 & ID_9005_GENERIC_MASK),
+	ID(ID_AIC7895 & ID_DEV_VENDOR_MASK),
+	ID16(ID_AIC7896 & ID_9005_GENERIC_MASK),
+	ID16(ID_AIC7899 & ID_9005_GENERIC_MASK),
+	ID(ID_AIC7810 & ID_DEV_VENDOR_MASK),
+	ID(ID_AIC7815 & ID_DEV_VENDOR_MASK),
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ahc_linux_pci_id_table);
+
+#ifdef CONFIG_PM
+static int
+ahc_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg)
+{
+	struct ahc_softc *ahc = pci_get_drvdata(pdev);
+	int rc;
+
+	if ((rc = ahc_suspend(ahc)))
+		return rc;
+
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+
+	if (mesg.event & PM_EVENT_SLEEP)
+		pci_set_power_state(pdev, PCI_D3hot);
+
+	return rc;
+}
+
+static int
+ahc_linux_pci_dev_resume(struct pci_dev *pdev)
+{
+	struct ahc_softc *ahc = pci_get_drvdata(pdev);
+	int rc;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	if ((rc = pci_enable_device(pdev))) {
+		dev_printk(KERN_ERR, &pdev->dev,
+			   "failed to enable device after resume (%d)\n", rc);
+		return rc;
+	}
+
+	pci_set_master(pdev);
+
+	ahc_pci_resume(ahc);
+
+	return (ahc_resume(ahc));
+}
+#endif
+
+static void
+ahc_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+	struct ahc_softc *ahc = pci_get_drvdata(pdev);
+	u_long s;
+
+	if (ahc->platform_data && ahc->platform_data->host)
+			scsi_remove_host(ahc->platform_data->host);
+
+	ahc_lock(ahc, &s);
+	ahc_intr_enable(ahc, FALSE);
+	ahc_unlock(ahc, &s);
+	ahc_free(ahc);
+}
+
+static void
+ahc_linux_pci_inherit_flags(struct ahc_softc *ahc)
+{
+	struct pci_dev *pdev = ahc->dev_softc, *master_pdev;
+	unsigned int master_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
+
+	master_pdev = pci_get_slot(pdev->bus, master_devfn);
+	if (master_pdev) {
+		struct ahc_softc *master = pci_get_drvdata(master_pdev);
+		if (master) {
+			ahc->flags &= ~AHC_BIOS_ENABLED; 
+			ahc->flags |= master->flags & AHC_BIOS_ENABLED;
+
+			ahc->flags &= ~AHC_PRIMARY_CHANNEL; 
+			ahc->flags |= master->flags & AHC_PRIMARY_CHANNEL;
+		} else
+			printk(KERN_ERR "aic7xxx: no multichannel peer found!\n");
+		pci_dev_put(master_pdev);
+	} 
+}
+
+static int
+ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	char		 buf[80];
+	const uint64_t	 mask_39bit = 0x7FFFFFFFFFULL;
+	struct		 ahc_softc *ahc;
+	ahc_dev_softc_t	 pci;
+	const struct ahc_pci_identity *entry;
+	char		*name;
+	int		 error;
+	struct device	*dev = &pdev->dev;
+
+	pci = pdev;
+	entry = ahc_find_pci_device(pci);
+	if (entry == NULL)
+		return (-ENODEV);
+
+	/*
+	 * Allocate a softc for this card and
+	 * set it up for attachment by our
+	 * common detect routine.
+	 */
+	sprintf(buf, "ahc_pci:%d:%d:%d",
+		ahc_get_pci_bus(pci),
+		ahc_get_pci_slot(pci),
+		ahc_get_pci_function(pci));
+	name = kstrdup(buf, GFP_ATOMIC);
+	if (name == NULL)
+		return (-ENOMEM);
+	ahc = ahc_alloc(NULL, name);
+	if (ahc == NULL)
+		return (-ENOMEM);
+	if (pci_enable_device(pdev)) {
+		ahc_free(ahc);
+		return (-ENODEV);
+	}
+	pci_set_master(pdev);
+
+	if (sizeof(dma_addr_t) > 4
+	    && ahc->features & AHC_LARGE_SCBS
+	    && dma_set_mask(dev, mask_39bit) == 0
+	    && dma_get_required_mask(dev) > DMA_BIT_MASK(32)) {
+		ahc->flags |= AHC_39BIT_ADDRESSING;
+	} else {
+		if (dma_set_mask(dev, DMA_BIT_MASK(32))) {
+			ahc_free(ahc);
+			printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n");
+                	return (-ENODEV);
+		}
+	}
+	ahc->dev_softc = pci;
+	error = ahc_pci_config(ahc, entry);
+	if (error != 0) {
+		ahc_free(ahc);
+		return (-error);
+	}
+
+	/*
+	 * Second Function PCI devices need to inherit some
+	 * settings from function 0.
+	 */
+	if ((ahc->features & AHC_MULTI_FUNC) && PCI_FUNC(pdev->devfn) != 0)
+		ahc_linux_pci_inherit_flags(ahc);
+
+	pci_set_drvdata(pdev, ahc);
+	ahc_linux_register_host(ahc, &aic7xxx_driver_template);
+	return (0);
+}
+
+/******************************* PCI Routines *********************************/
+uint32_t
+ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
+{
+	switch (width) {
+	case 1:
+	{
+		uint8_t retval;
+
+		pci_read_config_byte(pci, reg, &retval);
+		return (retval);
+	}
+	case 2:
+	{
+		uint16_t retval;
+		pci_read_config_word(pci, reg, &retval);
+		return (retval);
+	}
+	case 4:
+	{
+		uint32_t retval;
+		pci_read_config_dword(pci, reg, &retval);
+		return (retval);
+	}
+	default:
+		panic("ahc_pci_read_config: Read size too big");
+		/* NOTREACHED */
+		return (0);
+	}
+}
+
+void
+ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+	switch (width) {
+	case 1:
+		pci_write_config_byte(pci, reg, value);
+		break;
+	case 2:
+		pci_write_config_word(pci, reg, value);
+		break;
+	case 4:
+		pci_write_config_dword(pci, reg, value);
+		break;
+	default:
+		panic("ahc_pci_write_config: Write size too big");
+		/* NOTREACHED */
+	}
+}
+
+
+static struct pci_driver aic7xxx_pci_driver = {
+	.name		= "aic7xxx",
+	.probe		= ahc_linux_pci_dev_probe,
+#ifdef CONFIG_PM
+	.suspend	= ahc_linux_pci_dev_suspend,
+	.resume		= ahc_linux_pci_dev_resume,
+#endif
+	.remove		= ahc_linux_pci_dev_remove,
+	.id_table	= ahc_linux_pci_id_table
+};
+
+int
+ahc_linux_pci_init(void)
+{
+	return pci_register_driver(&aic7xxx_pci_driver);
+}
+
+void
+ahc_linux_pci_exit(void)
+{
+	pci_unregister_driver(&aic7xxx_pci_driver);
+}
+
+static int
+ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc, resource_size_t *base)
+{
+	if (aic7xxx_allow_memio == 0)
+		return (ENOMEM);
+
+	*base = pci_resource_start(ahc->dev_softc, 0);
+	if (*base == 0)
+		return (ENOMEM);
+	if (!request_region(*base, 256, "aic7xxx"))
+		return (ENOMEM);
+	return (0);
+}
+
+static int
+ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
+				 resource_size_t *bus_addr,
+				 uint8_t __iomem **maddr)
+{
+	resource_size_t	start;
+	int	error;
+
+	error = 0;
+	start = pci_resource_start(ahc->dev_softc, 1);
+	if (start != 0) {
+		*bus_addr = start;
+		if (!request_mem_region(start, 0x1000, "aic7xxx"))
+			error = ENOMEM;
+		if (error == 0) {
+			*maddr = ioremap_nocache(start, 256);
+			if (*maddr == NULL) {
+				error = ENOMEM;
+				release_mem_region(start, 0x1000);
+			}
+		}
+	} else
+		error = ENOMEM;
+	return (error);
+}
+
+int
+ahc_pci_map_registers(struct ahc_softc *ahc)
+{
+	uint32_t command;
+	resource_size_t	base;
+	uint8_t	__iomem *maddr;
+	int	 error;
+
+	/*
+	 * If its allowed, we prefer memory mapped access.
+	 */
+	command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, 4);
+	command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
+	base = 0;
+	maddr = NULL;
+	error = ahc_linux_pci_reserve_mem_region(ahc, &base, &maddr);
+	if (error == 0) {
+		ahc->platform_data->mem_busaddr = base;
+		ahc->tag = BUS_SPACE_MEMIO;
+		ahc->bsh.maddr = maddr;
+		ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
+				     command | PCIM_CMD_MEMEN, 4);
+
+		/*
+		 * Do a quick test to see if memory mapped
+		 * I/O is functioning correctly.
+		 */
+		if (ahc_pci_test_register_access(ahc) != 0) {
+
+			printk("aic7xxx: PCI Device %d:%d:%d "
+			       "failed memory mapped test.  Using PIO.\n",
+			       ahc_get_pci_bus(ahc->dev_softc),
+			       ahc_get_pci_slot(ahc->dev_softc),
+			       ahc_get_pci_function(ahc->dev_softc));
+			iounmap(maddr);
+			release_mem_region(ahc->platform_data->mem_busaddr,
+					   0x1000);
+			ahc->bsh.maddr = NULL;
+			maddr = NULL;
+		} else
+			command |= PCIM_CMD_MEMEN;
+	} else {
+		printk("aic7xxx: PCI%d:%d:%d MEM region 0x%llx "
+		       "unavailable. Cannot memory map device.\n",
+		       ahc_get_pci_bus(ahc->dev_softc),
+		       ahc_get_pci_slot(ahc->dev_softc),
+		       ahc_get_pci_function(ahc->dev_softc),
+		       (unsigned long long)base);
+	}
+
+	/*
+	 * We always prefer memory mapped access.
+	 */
+	if (maddr == NULL) {
+
+		error = ahc_linux_pci_reserve_io_region(ahc, &base);
+		if (error == 0) {
+			ahc->tag = BUS_SPACE_PIO;
+			ahc->bsh.ioport = (u_long)base;
+			command |= PCIM_CMD_PORTEN;
+		} else {
+			printk("aic7xxx: PCI%d:%d:%d IO region 0x%llx[0..255] "
+			       "unavailable. Cannot map device.\n",
+			       ahc_get_pci_bus(ahc->dev_softc),
+			       ahc_get_pci_slot(ahc->dev_softc),
+			       ahc_get_pci_function(ahc->dev_softc),
+			       (unsigned long long)base);
+		}
+	}
+	ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
+	return (error);
+}
+
+int
+ahc_pci_map_int(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = request_irq(ahc->dev_softc->irq, ahc_linux_isr,
+			    IRQF_SHARED, "aic7xxx", ahc);
+	if (error == 0)
+		ahc->platform_data->irq = ahc->dev_softc->irq;
+	
+	return (-error);
+}
+
diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.c b/drivers/scsi/aic7xxx/aic7xxx_pci.c
new file mode 100644
index 0000000..22d5a94
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c
@@ -0,0 +1,2469 @@
+/*
+ * Product specific probe and attach routines for:
+ *      3940, 2940, aic7895, aic7890, aic7880,
+ *	aic7870, aic7860 and aic7850 SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_pci.c#79 $
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#include "aic7xxx_pci.h"
+
+static inline uint64_t
+ahc_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+	uint64_t id;
+
+	id = subvendor
+	   | (subdevice << 16)
+	   | ((uint64_t)vendor << 32)
+	   | ((uint64_t)device << 48);
+
+	return (id);
+}
+
+#define AHC_PCI_IOADDR	PCIR_MAPS	/* I/O Address */
+#define AHC_PCI_MEMADDR	(PCIR_MAPS + 4)	/* Mem I/O Address */
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define		DEVID_9005_TYPE_HBA		0x0	/* Standard Card */
+#define		DEVID_9005_TYPE_AAA		0x3	/* RAID Card */
+#define		DEVID_9005_TYPE_SISL		0x5	/* Container ROMB */
+#define		DEVID_9005_TYPE_MB		0xF	/* On Motherboard */
+
+#define DEVID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define		DEVID_9005_MAXRATE_U160		0x0
+#define		DEVID_9005_MAXRATE_ULTRA2	0x1
+#define		DEVID_9005_MAXRATE_ULTRA	0x2
+#define		DEVID_9005_MAXRATE_FAST		0x3
+
+#define DEVID_9005_MFUNC(id) (((id) & 0x40) >> 6)
+
+#define DEVID_9005_CLASS(id) (((id) & 0xFF00) >> 8)
+#define		DEVID_9005_CLASS_SPI		0x0	/* Parallel SCSI */
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define		SUBID_9005_TYPE_MB		0xF	/* On Motherboard */
+#define		SUBID_9005_TYPE_CARD		0x0	/* Standard Card */
+#define		SUBID_9005_TYPE_LCCARD		0x1	/* Low Cost Card */
+#define		SUBID_9005_TYPE_RAID		0x3	/* Combined with Raid */
+
+#define SUBID_9005_TYPE_KNOWN(id)			\
+	  ((((id) & 0xF) == SUBID_9005_TYPE_MB)		\
+	|| (((id) & 0xF) == SUBID_9005_TYPE_CARD)	\
+	|| (((id) & 0xF) == SUBID_9005_TYPE_LCCARD)	\
+	|| (((id) & 0xF) == SUBID_9005_TYPE_RAID))
+
+#define SUBID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define		SUBID_9005_MAXRATE_ULTRA2	0x0
+#define		SUBID_9005_MAXRATE_ULTRA	0x1
+#define		SUBID_9005_MAXRATE_U160		0x2
+#define		SUBID_9005_MAXRATE_RESERVED	0x3
+
+#define SUBID_9005_SEEPTYPE(id)						\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? ((id) & 0xC0) >> 6						\
+	 : ((id) & 0x300) >> 8)
+#define		SUBID_9005_SEEPTYPE_NONE	0x0
+#define		SUBID_9005_SEEPTYPE_1K		0x1
+#define		SUBID_9005_SEEPTYPE_2K_4K	0x2
+#define		SUBID_9005_SEEPTYPE_RESERVED	0x3
+#define SUBID_9005_AUTOTERM(id)						\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? (((id) & 0x400) >> 10) == 0					\
+	 : (((id) & 0x40) >> 6) == 0)
+
+#define SUBID_9005_NUMCHAN(id)						\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? ((id) & 0x300) >> 8						\
+	 : ((id) & 0xC00) >> 10)
+
+#define SUBID_9005_LEGACYCONN(id)					\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? 0								\
+	 : ((id) & 0x80) >> 7)
+
+#define SUBID_9005_MFUNCENB(id)						\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? ((id) & 0x800) >> 11						\
+	 : ((id) & 0x1000) >> 12)
+/*
+ * Informational only. Should use chip register to be
+ * certain, but may be use in identification strings.
+ */
+#define SUBID_9005_CARD_SCSIWIDTH_MASK	0x2000
+#define SUBID_9005_CARD_PCIWIDTH_MASK	0x4000
+#define SUBID_9005_CARD_SEDIFF_MASK	0x8000
+
+static ahc_device_setup_t ahc_aic785X_setup;
+static ahc_device_setup_t ahc_aic7860_setup;
+static ahc_device_setup_t ahc_apa1480_setup;
+static ahc_device_setup_t ahc_aic7870_setup;
+static ahc_device_setup_t ahc_aic7870h_setup;
+static ahc_device_setup_t ahc_aha394X_setup;
+static ahc_device_setup_t ahc_aha394Xh_setup;
+static ahc_device_setup_t ahc_aha494X_setup;
+static ahc_device_setup_t ahc_aha494Xh_setup;
+static ahc_device_setup_t ahc_aha398X_setup;
+static ahc_device_setup_t ahc_aic7880_setup;
+static ahc_device_setup_t ahc_aic7880h_setup;
+static ahc_device_setup_t ahc_aha2940Pro_setup;
+static ahc_device_setup_t ahc_aha394XU_setup;
+static ahc_device_setup_t ahc_aha394XUh_setup;
+static ahc_device_setup_t ahc_aha398XU_setup;
+static ahc_device_setup_t ahc_aic7890_setup;
+static ahc_device_setup_t ahc_aic7892_setup;
+static ahc_device_setup_t ahc_aic7895_setup;
+static ahc_device_setup_t ahc_aic7895h_setup;
+static ahc_device_setup_t ahc_aic7896_setup;
+static ahc_device_setup_t ahc_aic7899_setup;
+static ahc_device_setup_t ahc_aha29160C_setup;
+static ahc_device_setup_t ahc_raid_setup;
+static ahc_device_setup_t ahc_aha394XX_setup;
+static ahc_device_setup_t ahc_aha494XX_setup;
+static ahc_device_setup_t ahc_aha398XX_setup;
+
+static const struct ahc_pci_identity ahc_pci_ident_table[] = {
+	/* aic7850 based controllers */
+	{
+		ID_AHA_2902_04_10_15_20C_30C,
+		ID_ALL_MASK,
+		"Adaptec 2902/04/10/15/20C/30C SCSI adapter",
+		ahc_aic785X_setup
+	},
+	/* aic7860 based controllers */
+	{
+		ID_AHA_2930CU,
+		ID_ALL_MASK,
+		"Adaptec 2930CU SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AHA_1480A & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 1480A Ultra SCSI adapter",
+		ahc_apa1480_setup
+	},
+	{
+		ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940A Ultra SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940A/CN Ultra SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2930C Ultra SCSI adapter (VAR)",
+		ahc_aic7860_setup
+	},
+	/* aic7870 based controllers */
+	{
+		ID_AHA_2940,
+		ID_ALL_MASK,
+		"Adaptec 2940 SCSI adapter",
+		ahc_aic7870_setup
+	},
+	{
+		ID_AHA_3940,
+		ID_ALL_MASK,
+		"Adaptec 3940 SCSI adapter",
+		ahc_aha394X_setup
+	},
+	{
+		ID_AHA_398X,
+		ID_ALL_MASK,
+		"Adaptec 398X SCSI RAID adapter",
+		ahc_aha398X_setup
+	},
+	{
+		ID_AHA_2944,
+		ID_ALL_MASK,
+		"Adaptec 2944 SCSI adapter",
+		ahc_aic7870h_setup
+	},
+	{
+		ID_AHA_3944,
+		ID_ALL_MASK,
+		"Adaptec 3944 SCSI adapter",
+		ahc_aha394Xh_setup
+	},
+	{
+		ID_AHA_4944,
+		ID_ALL_MASK,
+		"Adaptec 4944 SCSI adapter",
+		ahc_aha494Xh_setup
+	},
+	/* aic7880 based controllers */
+	{
+		ID_AHA_2940U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940 Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	{
+		ID_AHA_3940U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 3940 Ultra SCSI adapter",
+		ahc_aha394XU_setup
+	},
+	{
+		ID_AHA_2944U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2944 Ultra SCSI adapter",
+		ahc_aic7880h_setup
+	},
+	{
+		ID_AHA_3944U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 3944 Ultra SCSI adapter",
+		ahc_aha394XUh_setup
+	},
+	{
+		ID_AHA_398XU & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 398X Ultra SCSI RAID adapter",
+		ahc_aha398XU_setup
+	},
+	{
+		/*
+		 * XXX Don't know the slot numbers
+		 * so we can't identify channels
+		 */
+		ID_AHA_4944U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 4944 Ultra SCSI adapter",
+		ahc_aic7880h_setup
+	},
+	{
+		ID_AHA_2930U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2930 Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	{
+		ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940 Pro Ultra SCSI adapter",
+		ahc_aha2940Pro_setup
+	},
+	{
+		ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940/CN Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	/* Ignore all SISL (AAC on MB) based controllers. */
+	{
+		ID_9005_SISL_ID,
+		ID_9005_SISL_MASK,
+		NULL,
+		NULL
+	},
+	/* aic7890 based controllers */
+	{
+		ID_AHA_2930U2,
+		ID_ALL_MASK,
+		"Adaptec 2930 Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AHA_2940U2B,
+		ID_ALL_MASK,
+		"Adaptec 2940B Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AHA_2940U2_OEM,
+		ID_ALL_MASK,
+		"Adaptec 2940 Ultra2 SCSI adapter (OEM)",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AHA_2940U2,
+		ID_ALL_MASK,
+		"Adaptec 2940 Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AHA_2950U2B,
+		ID_ALL_MASK,
+		"Adaptec 2950 Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AIC7890_ARO,
+		ID_ALL_MASK,
+		"Adaptec aic7890/91 Ultra2 SCSI adapter (ARO)",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AAA_131U2,
+		ID_ALL_MASK,
+		"Adaptec AAA-131 Ultra2 RAID adapter",
+		ahc_aic7890_setup
+	},
+	/* aic7892 based controllers */
+	{
+		ID_AHA_29160,
+		ID_ALL_MASK,
+		"Adaptec 29160 Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_29160_CPQ,
+		ID_ALL_MASK,
+		"Adaptec (Compaq OEM) 29160 Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_29160N,
+		ID_ALL_MASK,
+		"Adaptec 29160N Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_29160C,
+		ID_ALL_MASK,
+		"Adaptec 29160C Ultra160 SCSI adapter",
+		ahc_aha29160C_setup
+	},
+	{
+		ID_AHA_29160B,
+		ID_ALL_MASK,
+		"Adaptec 29160B Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_19160B,
+		ID_ALL_MASK,
+		"Adaptec 19160B Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AIC7892_ARO,
+		ID_ALL_MASK,
+		"Adaptec aic7892 Ultra160 SCSI adapter (ARO)",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_2915_30LP,
+		ID_ALL_MASK,
+		"Adaptec 2915/30LP Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	/* aic7895 based controllers */	
+	{
+		ID_AHA_2940U_DUAL,
+		ID_ALL_MASK,
+		"Adaptec 2940/DUAL Ultra SCSI adapter",
+		ahc_aic7895_setup
+	},
+	{
+		ID_AHA_3940AU,
+		ID_ALL_MASK,
+		"Adaptec 3940A Ultra SCSI adapter",
+		ahc_aic7895_setup
+	},
+	{
+		ID_AHA_3944AU,
+		ID_ALL_MASK,
+		"Adaptec 3944A Ultra SCSI adapter",
+		ahc_aic7895h_setup
+	},
+	{
+		ID_AIC7895_ARO,
+		ID_AIC7895_ARO_MASK,
+		"Adaptec aic7895 Ultra SCSI adapter (ARO)",
+		ahc_aic7895_setup
+	},
+	/* aic7896/97 based controllers */	
+	{
+		ID_AHA_3950U2B_0,
+		ID_ALL_MASK,
+		"Adaptec 3950B Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AHA_3950U2B_1,
+		ID_ALL_MASK,
+		"Adaptec 3950B Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AHA_3950U2D_0,
+		ID_ALL_MASK,
+		"Adaptec 3950D Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AHA_3950U2D_1,
+		ID_ALL_MASK,
+		"Adaptec 3950D Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AIC7896_ARO,
+		ID_ALL_MASK,
+		"Adaptec aic7896/97 Ultra2 SCSI adapter (ARO)",
+		ahc_aic7896_setup
+	},
+	/* aic7899 based controllers */	
+	{
+		ID_AHA_3960D,
+		ID_ALL_MASK,
+		"Adaptec 3960D Ultra160 SCSI adapter",
+		ahc_aic7899_setup
+	},
+	{
+		ID_AHA_3960D_CPQ,
+		ID_ALL_MASK,
+		"Adaptec (Compaq OEM) 3960D Ultra160 SCSI adapter",
+		ahc_aic7899_setup
+	},
+	{
+		ID_AIC7899_ARO,
+		ID_ALL_MASK,
+		"Adaptec aic7899 Ultra160 SCSI adapter (ARO)",
+		ahc_aic7899_setup
+	},
+	/* Generic chip probes for devices we don't know 'exactly' */
+	{
+		ID_AIC7850 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7850 SCSI adapter",
+		ahc_aic785X_setup
+	},
+	{
+		ID_AIC7855 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7855 SCSI adapter",
+		ahc_aic785X_setup
+	},
+	{
+		ID_AIC7859 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7859 SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AIC7860 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7860 Ultra SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AIC7870 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7870 SCSI adapter",
+		ahc_aic7870_setup
+	},
+	{
+		ID_AIC7880 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7880 Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	{
+		ID_AIC7890 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec aic7890/91 Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AIC7892 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec aic7892 Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AIC7895 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7895 Ultra SCSI adapter",
+		ahc_aic7895_setup
+	},
+	{
+		ID_AIC7896 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec aic7896/97 Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AIC7899 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec aic7899 Ultra160 SCSI adapter",
+		ahc_aic7899_setup
+	},
+	{
+		ID_AIC7810 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7810 RAID memory controller",
+		ahc_raid_setup
+	},
+	{
+		ID_AIC7815 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7815 RAID memory controller",
+		ahc_raid_setup
+	}
+};
+
+static const u_int ahc_num_pci_devs = ARRAY_SIZE(ahc_pci_ident_table);
+		
+#define AHC_394X_SLOT_CHANNEL_A	4
+#define AHC_394X_SLOT_CHANNEL_B	5
+
+#define AHC_398X_SLOT_CHANNEL_A	4
+#define AHC_398X_SLOT_CHANNEL_B	8
+#define AHC_398X_SLOT_CHANNEL_C	12
+
+#define AHC_494X_SLOT_CHANNEL_A	4
+#define AHC_494X_SLOT_CHANNEL_B	5
+#define AHC_494X_SLOT_CHANNEL_C	6
+#define AHC_494X_SLOT_CHANNEL_D	7
+
+#define	DEVCONFIG		0x40
+#define		PCIERRGENDIS	0x80000000ul
+#define		SCBSIZE32	0x00010000ul	/* aic789X only */
+#define		REXTVALID	0x00001000ul	/* ultra cards only */
+#define		MPORTMODE	0x00000400ul	/* aic7870+ only */
+#define		RAMPSM		0x00000200ul	/* aic7870+ only */
+#define		VOLSENSE	0x00000100ul
+#define		PCI64BIT	0x00000080ul	/* 64Bit PCI bus (Ultra2 Only)*/
+#define		SCBRAMSEL	0x00000080ul
+#define		MRDCEN		0x00000040ul
+#define		EXTSCBTIME	0x00000020ul	/* aic7870 only */
+#define		EXTSCBPEN	0x00000010ul	/* aic7870 only */
+#define		BERREN		0x00000008ul
+#define		DACEN		0x00000004ul
+#define		STPWLEVEL	0x00000002ul
+#define		DIFACTNEGEN	0x00000001ul	/* aic7870 only */
+
+#define	CSIZE_LATTIME		0x0c
+#define		CACHESIZE	0x0000003ful	/* only 5 bits */
+#define		LATTIME		0x0000ff00ul
+
+/* PCI STATUS definitions */
+#define	DPE	0x80
+#define SSE	0x40
+#define	RMA	0x20
+#define	RTA	0x10
+#define STA	0x08
+#define DPR	0x01
+
+static int ahc_9005_subdevinfo_valid(uint16_t vendor, uint16_t device,
+				     uint16_t subvendor, uint16_t subdevice);
+static int ahc_ext_scbram_present(struct ahc_softc *ahc);
+static void ahc_scbram_config(struct ahc_softc *ahc, int enable,
+				  int pcheck, int fast, int large);
+static void ahc_probe_ext_scbram(struct ahc_softc *ahc);
+static void check_extport(struct ahc_softc *ahc, u_int *sxfrctl1);
+static void ahc_parse_pci_eeprom(struct ahc_softc *ahc,
+				 struct seeprom_config *sc);
+static void configure_termination(struct ahc_softc *ahc,
+				  struct seeprom_descriptor *sd,
+				  u_int adapter_control,
+	 			  u_int *sxfrctl1);
+
+static void ahc_new_term_detect(struct ahc_softc *ahc,
+				int *enableSEC_low,
+				int *enableSEC_high,
+				int *enablePRI_low,
+				int *enablePRI_high,
+				int *eeprom_present);
+static void aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+				 int *internal68_present,
+				 int *externalcable_present,
+				 int *eeprom_present);
+static void aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+				 int *externalcable_present,
+				 int *eeprom_present);
+static void    write_brdctl(struct ahc_softc *ahc, uint8_t value);
+static uint8_t read_brdctl(struct ahc_softc *ahc);
+static void ahc_pci_intr(struct ahc_softc *ahc);
+static int  ahc_pci_chip_init(struct ahc_softc *ahc);
+
+static int
+ahc_9005_subdevinfo_valid(uint16_t device, uint16_t vendor,
+			  uint16_t subdevice, uint16_t subvendor)
+{
+	int result;
+
+	/* Default to invalid. */
+	result = 0;
+	if (vendor == 0x9005
+	 && subvendor == 0x9005
+         && subdevice != device
+         && SUBID_9005_TYPE_KNOWN(subdevice) != 0) {
+
+		switch (SUBID_9005_TYPE(subdevice)) {
+		case SUBID_9005_TYPE_MB:
+			break;
+		case SUBID_9005_TYPE_CARD:
+		case SUBID_9005_TYPE_LCCARD:
+			/*
+			 * Currently only trust Adaptec cards to
+			 * get the sub device info correct.
+			 */
+			if (DEVID_9005_TYPE(device) == DEVID_9005_TYPE_HBA)
+				result = 1;
+			break;
+		case SUBID_9005_TYPE_RAID:
+			break;
+		default:
+			break;
+		}
+	}
+	return (result);
+}
+
+const struct ahc_pci_identity *
+ahc_find_pci_device(ahc_dev_softc_t pci)
+{
+	uint64_t  full_id;
+	uint16_t  device;
+	uint16_t  vendor;
+	uint16_t  subdevice;
+	uint16_t  subvendor;
+	const struct ahc_pci_identity *entry;
+	u_int	  i;
+
+	vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+	device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+	subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+	subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+	full_id = ahc_compose_id(device, vendor, subdevice, subvendor);
+
+	/*
+	 * If the second function is not hooked up, ignore it.
+	 * Unfortunately, not all MB vendors implement the
+	 * subdevice ID as per the Adaptec spec, so do our best
+	 * to sanity check it prior to accepting the subdevice
+	 * ID as valid.
+	 */
+	if (ahc_get_pci_function(pci) > 0
+	 && ahc_9005_subdevinfo_valid(device, vendor, subdevice, subvendor)
+	 && SUBID_9005_MFUNCENB(subdevice) == 0)
+		return (NULL);
+
+	for (i = 0; i < ahc_num_pci_devs; i++) {
+		entry = &ahc_pci_ident_table[i];
+		if (entry->full_id == (full_id & entry->id_mask)) {
+			/* Honor exclusion entries. */
+			if (entry->name == NULL)
+				return (NULL);
+			return (entry);
+		}
+	}
+	return (NULL);
+}
+
+int
+ahc_pci_config(struct ahc_softc *ahc, const struct ahc_pci_identity *entry)
+{
+	u_int	 command;
+	u_int	 our_id;
+	u_int	 sxfrctl1;
+	u_int	 scsiseq;
+	u_int	 dscommand0;
+	uint32_t devconfig;
+	int	 error;
+	uint8_t	 sblkctl;
+
+	our_id = 0;
+	error = entry->setup(ahc);
+	if (error != 0)
+		return (error);
+	ahc->chip |= AHC_PCI;
+	ahc->description = entry->name;
+
+	pci_set_power_state(ahc->dev_softc, AHC_POWER_STATE_D0);
+
+	error = ahc_pci_map_registers(ahc);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Before we continue probing the card, ensure that
+	 * its interrupts are *disabled*.  We don't want
+	 * a misstep to hang the machine in an interrupt
+	 * storm.
+	 */
+	ahc_intr_enable(ahc, FALSE);
+
+	devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+
+	/*
+	 * If we need to support high memory, enable dual
+	 * address cycles.  This bit must be set to enable
+	 * high address bit generation even if we are on a
+	 * 64bit bus (PCI64BIT set in devconfig).
+	 */
+	if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+
+		if (bootverbose)
+			printk("%s: Enabling 39Bit Addressing\n",
+			       ahc_name(ahc));
+		devconfig |= DACEN;
+	}
+	
+	/* Ensure that pci error generation, a test feature, is disabled. */
+	devconfig |= PCIERRGENDIS;
+
+	ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+
+	/* Ensure busmastering is enabled */
+	command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/2);
+	command |= PCIM_CMD_BUSMASTEREN;
+
+	ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, /*bytes*/2);
+
+	/* On all PCI adapters, we allow SCB paging */
+	ahc->flags |= AHC_PAGESCBS;
+
+	error = ahc_softc_init(ahc);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Disable PCI parity error checking.  Users typically
+	 * do this to work around broken PCI chipsets that get
+	 * the parity timing wrong and thus generate lots of spurious
+	 * errors.  The chip only allows us to disable *all* parity
+	 * error reporting when doing this, so CIO bus, scb ram, and
+	 * scratch ram parity errors will be ignored too.
+	 */
+	if ((ahc->flags & AHC_DISABLE_PCI_PERR) != 0)
+		ahc->seqctl |= FAILDIS;
+
+	ahc->bus_intr = ahc_pci_intr;
+	ahc->bus_chip_init = ahc_pci_chip_init;
+
+	/* Remember how the card was setup in case there is no SEEPROM */
+	if ((ahc_inb(ahc, HCNTRL) & POWRDN) == 0) {
+		ahc_pause(ahc);
+		if ((ahc->features & AHC_ULTRA2) != 0)
+			our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+		else
+			our_id = ahc_inb(ahc, SCSIID) & OID;
+		sxfrctl1 = ahc_inb(ahc, SXFRCTL1) & STPWEN;
+		scsiseq = ahc_inb(ahc, SCSISEQ);
+	} else {
+		sxfrctl1 = STPWEN;
+		our_id = 7;
+		scsiseq = 0;
+	}
+
+	error = ahc_reset(ahc, /*reinit*/FALSE);
+	if (error != 0)
+		return (ENXIO);
+
+	if ((ahc->features & AHC_DT) != 0) {
+		u_int sfunct;
+
+		/* Perform ALT-Mode Setup */
+		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+		ahc_outb(ahc, OPTIONMODE,
+			 OPTIONMODE_DEFAULTS|AUTOACKEN|BUSFREEREV|EXPPHASEDIS);
+		ahc_outb(ahc, SFUNCT, sfunct);
+
+		/* Normal mode setup */
+		ahc_outb(ahc, CRCCONTROL1, CRCVALCHKEN|CRCENDCHKEN|CRCREQCHKEN
+					  |TARGCRCENDEN);
+	}
+
+	dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+	dscommand0 |= MPARCKEN|CACHETHEN;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+
+		/*
+		 * DPARCKEN doesn't work correctly on
+		 * some MBs so don't use it.
+		 */
+		dscommand0 &= ~DPARCKEN;
+	}
+
+	/*
+	 * Handle chips that must have cache line
+	 * streaming (dis/en)abled.
+	 */
+	if ((ahc->bugs & AHC_CACHETHEN_DIS_BUG) != 0)
+		dscommand0 |= CACHETHEN;
+
+	if ((ahc->bugs & AHC_CACHETHEN_BUG) != 0)
+		dscommand0 &= ~CACHETHEN;
+
+	ahc_outb(ahc, DSCOMMAND0, dscommand0);
+
+	ahc->pci_cachesize =
+	    ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME,
+				/*bytes*/1) & CACHESIZE;
+	ahc->pci_cachesize *= 4;
+
+	if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0
+	 && ahc->pci_cachesize == 4) {
+
+		ahc_pci_write_config(ahc->dev_softc, CSIZE_LATTIME,
+				     0, /*bytes*/1);
+		ahc->pci_cachesize = 0;
+	}
+
+	/*
+	 * We cannot perform ULTRA speeds without the presence
+	 * of the external precision resistor.
+	 */
+	if ((ahc->features & AHC_ULTRA) != 0) {
+		uint32_t devconfig;
+
+		devconfig = ahc_pci_read_config(ahc->dev_softc,
+						DEVCONFIG, /*bytes*/4);
+		if ((devconfig & REXTVALID) == 0)
+			ahc->features &= ~AHC_ULTRA;
+	}
+
+	/* See if we have a SEEPROM and perform auto-term */
+	check_extport(ahc, &sxfrctl1);
+
+	/*
+	 * Take the LED out of diagnostic mode
+	 */
+	sblkctl = ahc_inb(ahc, SBLKCTL);
+	ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		ahc_outb(ahc, DFF_THRSH, RD_DFTHRSH_MAX|WR_DFTHRSH_MAX);
+	} else {
+		ahc_outb(ahc, DSPCISTATUS, DFTHRSH_100);
+	}
+
+	if (ahc->flags & AHC_USEDEFAULTS) {
+		/*
+		 * PCI Adapter default setup
+		 * Should only be used if the adapter does not have
+		 * a SEEPROM.
+		 */
+		/* See if someone else set us up already */
+		if ((ahc->flags & AHC_NO_BIOS_INIT) == 0
+		 && scsiseq != 0) {
+			printk("%s: Using left over BIOS settings\n",
+				ahc_name(ahc));
+			ahc->flags &= ~AHC_USEDEFAULTS;
+			ahc->flags |= AHC_BIOS_ENABLED;
+		} else {
+			/*
+			 * Assume only one connector and always turn
+			 * on termination.
+			 */
+ 			our_id = 0x07;
+			sxfrctl1 = STPWEN;
+		}
+		ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI);
+
+		ahc->our_id = our_id;
+	}
+
+	/*
+	 * Take a look to see if we have external SRAM.
+	 * We currently do not attempt to use SRAM that is
+	 * shared among multiple controllers.
+	 */
+	ahc_probe_ext_scbram(ahc);
+
+	/*
+	 * Record our termination setting for the
+	 * generic initialization routine.
+	 */
+	if ((sxfrctl1 & STPWEN) != 0)
+		ahc->flags |= AHC_TERM_ENB_A;
+
+	/*
+	 * Save chip register configuration data for chip resets
+	 * that occur during runtime and resume events.
+	 */
+	ahc->bus_softc.pci_softc.devconfig =
+	    ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+	ahc->bus_softc.pci_softc.command =
+	    ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1);
+	ahc->bus_softc.pci_softc.csize_lattime =
+	    ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME, /*bytes*/1);
+	ahc->bus_softc.pci_softc.dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+	ahc->bus_softc.pci_softc.dspcistatus = ahc_inb(ahc, DSPCISTATUS);
+	if ((ahc->features & AHC_DT) != 0) {
+		u_int sfunct;
+
+		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+		ahc->bus_softc.pci_softc.optionmode = ahc_inb(ahc, OPTIONMODE);
+		ahc->bus_softc.pci_softc.targcrccnt = ahc_inw(ahc, TARGCRCCNT);
+		ahc_outb(ahc, SFUNCT, sfunct);
+		ahc->bus_softc.pci_softc.crccontrol1 =
+		    ahc_inb(ahc, CRCCONTROL1);
+	}
+	if ((ahc->features & AHC_MULTI_FUNC) != 0)
+		ahc->bus_softc.pci_softc.scbbaddr = ahc_inb(ahc, SCBBADDR);
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc->bus_softc.pci_softc.dff_thrsh = ahc_inb(ahc, DFF_THRSH);
+
+	/* Core initialization */
+	error = ahc_init(ahc);
+	if (error != 0)
+		return (error);
+	ahc->init_level++;
+
+	/*
+	 * Allow interrupts now that we are completely setup.
+	 */
+	return ahc_pci_map_int(ahc);
+}
+
+/*
+ * Test for the presence of external sram in an
+ * "unshared" configuration.
+ */
+static int
+ahc_ext_scbram_present(struct ahc_softc *ahc)
+{
+	u_int chip;
+	int ramps;
+	int single_user;
+	uint32_t devconfig;
+
+	chip = ahc->chip & AHC_CHIPID_MASK;
+	devconfig = ahc_pci_read_config(ahc->dev_softc,
+					DEVCONFIG, /*bytes*/4);
+	single_user = (devconfig & MPORTMODE) != 0;
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ramps = (ahc_inb(ahc, DSCOMMAND0) & RAMPS) != 0;
+	else if (chip == AHC_AIC7895 || chip == AHC_AIC7895C)
+		/*
+		 * External SCBRAM arbitration is flakey
+		 * on these chips.  Unfortunately this means
+		 * we don't use the extra SCB ram space on the
+		 * 3940AUW.
+		 */
+		ramps = 0;
+	else if (chip >= AHC_AIC7870)
+		ramps = (devconfig & RAMPSM) != 0;
+	else
+		ramps = 0;
+
+	if (ramps && single_user)
+		return (1);
+	return (0);
+}
+
+/*
+ * Enable external scbram.
+ */
+static void
+ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck,
+		  int fast, int large)
+{
+	uint32_t devconfig;
+
+	if (ahc->features & AHC_MULTI_FUNC) {
+		/*
+		 * Set the SCB Base addr (highest address bit)
+		 * depending on which channel we are.
+		 */
+		ahc_outb(ahc, SCBBADDR, ahc_get_pci_function(ahc->dev_softc));
+	}
+
+	ahc->flags &= ~AHC_LSCBS_ENABLED;
+	if (large)
+		ahc->flags |= AHC_LSCBS_ENABLED;
+	devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		u_int dscommand0;
+
+		dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+		if (enable)
+			dscommand0 &= ~INTSCBRAMSEL;
+		else
+			dscommand0 |= INTSCBRAMSEL;
+		if (large)
+			dscommand0 &= ~USCBSIZE32;
+		else
+			dscommand0 |= USCBSIZE32;
+		ahc_outb(ahc, DSCOMMAND0, dscommand0);
+	} else {
+		if (fast)
+			devconfig &= ~EXTSCBTIME;
+		else
+			devconfig |= EXTSCBTIME;
+		if (enable)
+			devconfig &= ~SCBRAMSEL;
+		else
+			devconfig |= SCBRAMSEL;
+		if (large)
+			devconfig &= ~SCBSIZE32;
+		else
+			devconfig |= SCBSIZE32;
+	}
+	if (pcheck)
+		devconfig |= EXTSCBPEN;
+	else
+		devconfig &= ~EXTSCBPEN;
+
+	ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+}
+
+/*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+static void
+ahc_probe_ext_scbram(struct ahc_softc *ahc)
+{
+	int num_scbs;
+	int test_num_scbs;
+	int enable;
+	int pcheck;
+	int fast;
+	int large;
+
+	enable = FALSE;
+	pcheck = FALSE;
+	fast = FALSE;
+	large = FALSE;
+	num_scbs = 0;
+	
+	if (ahc_ext_scbram_present(ahc) == 0)
+		goto done;
+
+	/*
+	 * Probe for the best parameters to use.
+	 */
+	ahc_scbram_config(ahc, /*enable*/TRUE, pcheck, fast, large);
+	num_scbs = ahc_probe_scbs(ahc);
+	if (num_scbs == 0) {
+		/* The SRAM wasn't really present. */
+		goto done;
+	}
+	enable = TRUE;
+
+	/*
+	 * Clear any outstanding parity error
+	 * and ensure that parity error reporting
+	 * is enabled.
+	 */
+	ahc_outb(ahc, SEQCTL, 0);
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+	ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+	/* Now see if we can do parity */
+	ahc_scbram_config(ahc, enable, /*pcheck*/TRUE, fast, large);
+	num_scbs = ahc_probe_scbs(ahc);
+	if ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+	 || (ahc_inb(ahc, ERROR) & MPARERR) == 0)
+		pcheck = TRUE;
+
+	/* Clear any resulting parity error */
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+	ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+	/* Now see if we can do fast timing */
+	ahc_scbram_config(ahc, enable, pcheck, /*fast*/TRUE, large);
+	test_num_scbs = ahc_probe_scbs(ahc);
+	if (test_num_scbs == num_scbs
+	 && ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+	  || (ahc_inb(ahc, ERROR) & MPARERR) == 0))
+		fast = TRUE;
+
+	/*
+	 * See if we can use large SCBs and still maintain
+	 * the same overall count of SCBs.
+	 */
+	if ((ahc->features & AHC_LARGE_SCBS) != 0) {
+		ahc_scbram_config(ahc, enable, pcheck, fast, /*large*/TRUE);
+		test_num_scbs = ahc_probe_scbs(ahc);
+		if (test_num_scbs >= num_scbs) {
+			large = TRUE;
+			num_scbs = test_num_scbs;
+	 		if (num_scbs >= 64) {
+				/*
+				 * We have enough space to move the
+				 * "busy targets table" into SCB space
+				 * and make it qualify all the way to the
+				 * lun level.
+				 */
+				ahc->flags |= AHC_SCB_BTT;
+			}
+		}
+	}
+done:
+	/*
+	 * Disable parity error reporting until we
+	 * can load instruction ram.
+	 */
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
+	/* Clear any latched parity error */
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+	ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+	if (bootverbose && enable) {
+		printk("%s: External SRAM, %s access%s, %dbytes/SCB\n",
+		       ahc_name(ahc), fast ? "fast" : "slow", 
+		       pcheck ? ", parity checking enabled" : "",
+		       large ? 64 : 32);
+	}
+	ahc_scbram_config(ahc, enable, pcheck, fast, large);
+}
+
+/*
+ * Perform some simple tests that should catch situations where
+ * our registers are invalidly mapped.
+ */
+int
+ahc_pci_test_register_access(struct ahc_softc *ahc)
+{
+	int	 error;
+	u_int	 status1;
+	uint32_t cmd;
+	uint8_t	 hcntrl;
+
+	error = EIO;
+
+	/*
+	 * Enable PCI error interrupt status, but suppress NMIs
+	 * generated by SERR raised due to target aborts.
+	 */
+	cmd = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/2);
+	ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
+			     cmd & ~PCIM_CMD_SERRESPEN, /*bytes*/2);
+
+	/*
+	 * First a simple test to see if any
+	 * registers can be read.  Reading
+	 * HCNTRL has no side effects and has
+	 * at least one bit that is guaranteed to
+	 * be zero so it is a good register to
+	 * use for this test.
+	 */
+	hcntrl = ahc_inb(ahc, HCNTRL);
+
+	if (hcntrl == 0xFF)
+		goto fail;
+
+	if ((hcntrl & CHIPRST) != 0) {
+		/*
+		 * The chip has not been initialized since
+		 * PCI/EISA/VLB bus reset.  Don't trust
+		 * "left over BIOS data".
+		 */
+		ahc->flags |= AHC_NO_BIOS_INIT;
+	}
+
+	/*
+	 * Next create a situation where write combining
+	 * or read prefetching could be initiated by the
+	 * CPU or host bridge.  Our device does not support
+	 * either, so look for data corruption and/or flagged
+	 * PCI errors.  First pause without causing another
+	 * chip reset.
+	 */
+	hcntrl &= ~CHIPRST;
+	ahc_outb(ahc, HCNTRL, hcntrl|PAUSE);
+	while (ahc_is_paused(ahc) == 0)
+		;
+
+	/* Clear any PCI errors that occurred before our driver attached. */
+	status1 = ahc_pci_read_config(ahc->dev_softc,
+				      PCIR_STATUS + 1, /*bytes*/1);
+	ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+			     status1, /*bytes*/1);
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+
+	ahc_outb(ahc, SEQCTL, PERRORDIS);
+	ahc_outb(ahc, SCBPTR, 0);
+	ahc_outl(ahc, SCB_BASE, 0x5aa555aa);
+	if (ahc_inl(ahc, SCB_BASE) != 0x5aa555aa)
+		goto fail;
+
+	status1 = ahc_pci_read_config(ahc->dev_softc,
+				      PCIR_STATUS + 1, /*bytes*/1);
+	if ((status1 & STA) != 0)
+		goto fail;
+
+	error = 0;
+
+fail:
+	/* Silently clear any latched errors. */
+	status1 = ahc_pci_read_config(ahc->dev_softc,
+				      PCIR_STATUS + 1, /*bytes*/1);
+	ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+			     status1, /*bytes*/1);
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
+	ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, cmd, /*bytes*/2);
+	return (error);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static void
+check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
+{
+	struct	seeprom_descriptor sd;
+	struct	seeprom_config *sc;
+	int	have_seeprom;
+	int	have_autoterm;
+
+	sd.sd_ahc = ahc;
+	sd.sd_control_offset = SEECTL;		
+	sd.sd_status_offset = SEECTL;		
+	sd.sd_dataout_offset = SEECTL;		
+	sc = ahc->seep_config;
+
+	/*
+	 * For some multi-channel devices, the c46 is simply too
+	 * small to work.  For the other controller types, we can
+	 * get our information from either SEEPROM type.  Set the
+	 * type to start our probe with accordingly.
+	 */
+	if (ahc->flags & AHC_LARGE_SEEPROM)
+		sd.sd_chip = C56_66;
+	else
+		sd.sd_chip = C46;
+
+	sd.sd_MS = SEEMS;
+	sd.sd_RDY = SEERDY;
+	sd.sd_CS = SEECS;
+	sd.sd_CK = SEECK;
+	sd.sd_DO = SEEDO;
+	sd.sd_DI = SEEDI;
+
+	have_seeprom = ahc_acquire_seeprom(ahc, &sd);
+	if (have_seeprom) {
+
+		if (bootverbose) 
+			printk("%s: Reading SEEPROM...", ahc_name(ahc));
+
+		for (;;) {
+			u_int start_addr;
+
+			start_addr = 32 * (ahc->channel - 'A');
+
+			have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)sc,
+							start_addr,
+							sizeof(*sc)/2);
+
+			if (have_seeprom)
+				have_seeprom = ahc_verify_cksum(sc);
+
+			if (have_seeprom != 0 || sd.sd_chip == C56_66) {
+				if (bootverbose) {
+					if (have_seeprom == 0)
+						printk ("checksum error\n");
+					else
+						printk ("done.\n");
+				}
+				break;
+			}
+			sd.sd_chip = C56_66;
+		}
+		ahc_release_seeprom(&sd);
+
+		/* Remember the SEEPROM type for later */
+		if (sd.sd_chip == C56_66)
+			ahc->flags |= AHC_LARGE_SEEPROM;
+	}
+
+	if (!have_seeprom) {
+		/*
+		 * Pull scratch ram settings and treat them as
+		 * if they are the contents of an seeprom if
+		 * the 'ADPT' signature is found in SCB2.
+		 * We manually compose the data as 16bit values
+		 * to avoid endian issues.
+		 */
+		ahc_outb(ahc, SCBPTR, 2);
+		if (ahc_inb(ahc, SCB_BASE) == 'A'
+		 && ahc_inb(ahc, SCB_BASE + 1) == 'D'
+		 && ahc_inb(ahc, SCB_BASE + 2) == 'P'
+		 && ahc_inb(ahc, SCB_BASE + 3) == 'T') {
+			uint16_t *sc_data;
+			int	  i;
+
+			sc_data = (uint16_t *)sc;
+			for (i = 0; i < 32; i++, sc_data++) {
+				int	j;
+
+				j = i * 2;
+				*sc_data = ahc_inb(ahc, SRAM_BASE + j)
+					 | ahc_inb(ahc, SRAM_BASE + j + 1) << 8;
+			}
+			have_seeprom = ahc_verify_cksum(sc);
+			if (have_seeprom)
+				ahc->flags |= AHC_SCB_CONFIG_USED;
+		}
+		/*
+		 * Clear any SCB parity errors in case this data and
+		 * its associated parity was not initialized by the BIOS
+		 */
+		ahc_outb(ahc, CLRINT, CLRPARERR);
+		ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+	}
+
+	if (!have_seeprom) {
+		if (bootverbose)
+			printk("%s: No SEEPROM available.\n", ahc_name(ahc));
+		ahc->flags |= AHC_USEDEFAULTS;
+		kfree(ahc->seep_config);
+		ahc->seep_config = NULL;
+		sc = NULL;
+	} else {
+		ahc_parse_pci_eeprom(ahc, sc);
+	}
+
+	/*
+	 * Cards that have the external logic necessary to talk to
+	 * a SEEPROM, are almost certain to have the remaining logic
+	 * necessary for auto-termination control.  This assumption
+	 * hasn't failed yet...
+	 */
+	have_autoterm = have_seeprom;
+
+	/*
+	 * Some low-cost chips have SEEPROM and auto-term control built
+	 * in, instead of using a GAL.  They can tell us directly
+	 * if the termination logic is enabled.
+	 */
+	if ((ahc->features & AHC_SPIOCAP) != 0) {
+		if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) == 0)
+			have_autoterm = FALSE;
+	}
+
+	if (have_autoterm) {
+		ahc->flags |= AHC_HAS_TERM_LOGIC;
+		ahc_acquire_seeprom(ahc, &sd);
+		configure_termination(ahc, &sd, sc->adapter_control, sxfrctl1);
+		ahc_release_seeprom(&sd);
+	} else if (have_seeprom) {
+		*sxfrctl1 &= ~STPWEN;
+		if ((sc->adapter_control & CFSTERM) != 0)
+			*sxfrctl1 |= STPWEN;
+		if (bootverbose)
+			printk("%s: Low byte termination %sabled\n",
+			       ahc_name(ahc),
+			       (*sxfrctl1 & STPWEN) ? "en" : "dis");
+	}
+}
+
+static void
+ahc_parse_pci_eeprom(struct ahc_softc *ahc, struct seeprom_config *sc)
+{
+	/*
+	 * Put the data we've collected down into SRAM
+	 * where ahc_init will find it.
+	 */
+	int	 i;
+	int	 max_targ = sc->max_targets & CFMAXTARG;
+	u_int	 scsi_conf;
+	uint16_t discenable;
+	uint16_t ultraenb;
+
+	discenable = 0;
+	ultraenb = 0;
+	if ((sc->adapter_control & CFULTRAEN) != 0) {
+		/*
+		 * Determine if this adapter has a "newstyle"
+		 * SEEPROM format.
+		 */
+		for (i = 0; i < max_targ; i++) {
+			if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0) {
+				ahc->flags |= AHC_NEWEEPROM_FMT;
+				break;
+			}
+		}
+	}
+
+	for (i = 0; i < max_targ; i++) {
+		u_int     scsirate;
+		uint16_t target_mask;
+
+		target_mask = 0x01 << i;
+		if (sc->device_flags[i] & CFDISC)
+			discenable |= target_mask;
+		if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) {
+			if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0)
+				ultraenb |= target_mask;
+		} else if ((sc->adapter_control & CFULTRAEN) != 0) {
+			ultraenb |= target_mask;
+		}
+		if ((sc->device_flags[i] & CFXFER) == 0x04
+		 && (ultraenb & target_mask) != 0) {
+			/* Treat 10MHz as a non-ultra speed */
+			sc->device_flags[i] &= ~CFXFER;
+		 	ultraenb &= ~target_mask;
+		}
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			u_int offset;
+
+			if (sc->device_flags[i] & CFSYNCH)
+				offset = MAX_OFFSET_ULTRA2;
+			else 
+				offset = 0;
+			ahc_outb(ahc, TARG_OFFSET + i, offset);
+
+			/*
+			 * The ultra enable bits contain the
+			 * high bit of the ultra2 sync rate
+			 * field.
+			 */
+			scsirate = (sc->device_flags[i] & CFXFER)
+				 | ((ultraenb & target_mask) ? 0x8 : 0x0);
+			if (sc->device_flags[i] & CFWIDEB)
+				scsirate |= WIDEXFER;
+		} else {
+			scsirate = (sc->device_flags[i] & CFXFER) << 4;
+			if (sc->device_flags[i] & CFSYNCH)
+				scsirate |= SOFS;
+			if (sc->device_flags[i] & CFWIDEB)
+				scsirate |= WIDEXFER;
+		}
+		ahc_outb(ahc, TARG_SCSIRATE + i, scsirate);
+	}
+	ahc->our_id = sc->brtime_id & CFSCSIID;
+
+	scsi_conf = (ahc->our_id & 0x7);
+	if (sc->adapter_control & CFSPARITY)
+		scsi_conf |= ENSPCHK;
+	if (sc->adapter_control & CFRESETB)
+		scsi_conf |= RESET_SCSI;
+
+	ahc->flags |= (sc->adapter_control & CFBOOTCHAN) >> CFBOOTCHANSHIFT;
+
+	if (sc->bios_control & CFEXTEND)
+		ahc->flags |= AHC_EXTENDED_TRANS_A;
+
+	if (sc->bios_control & CFBIOSEN)
+		ahc->flags |= AHC_BIOS_ENABLED;
+	if (ahc->features & AHC_ULTRA
+	 && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) {
+		/* Should we enable Ultra mode? */
+		if (!(sc->adapter_control & CFULTRAEN))
+			/* Treat us as a non-ultra card */
+			ultraenb = 0;
+	}
+
+	if (sc->signature == CFSIGNATURE
+	 || sc->signature == CFSIGNATURE2) {
+		uint32_t devconfig;
+
+		/* Honor the STPWLEVEL settings */
+		devconfig = ahc_pci_read_config(ahc->dev_softc,
+						DEVCONFIG, /*bytes*/4);
+		devconfig &= ~STPWLEVEL;
+		if ((sc->bios_control & CFSTPWLEVEL) != 0)
+			devconfig |= STPWLEVEL;
+		ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+				     devconfig, /*bytes*/4);
+	}
+	/* Set SCSICONF info */
+	ahc_outb(ahc, SCSICONF, scsi_conf);
+	ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+	ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+	ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff);
+	ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff);
+}
+
+static void
+configure_termination(struct ahc_softc *ahc,
+		      struct seeprom_descriptor *sd,
+		      u_int adapter_control,
+		      u_int *sxfrctl1)
+{
+	uint8_t brddat;
+	
+	brddat = 0;
+
+	/*
+	 * Update the settings in sxfrctl1 to match the
+	 * termination settings 
+	 */
+	*sxfrctl1 = 0;
+	
+	/*
+	 * SEECS must be on for the GALS to latch
+	 * the data properly.  Be sure to leave MS
+	 * on or we will release the seeprom.
+	 */
+	SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS);
+	if ((adapter_control & CFAUTOTERM) != 0
+	 || (ahc->features & AHC_NEW_TERMCTL) != 0) {
+		int internal50_present;
+		int internal68_present;
+		int externalcable_present;
+		int eeprom_present;
+		int enableSEC_low;
+		int enableSEC_high;
+		int enablePRI_low;
+		int enablePRI_high;
+		int sum;
+
+		enableSEC_low = 0;
+		enableSEC_high = 0;
+		enablePRI_low = 0;
+		enablePRI_high = 0;
+		if ((ahc->features & AHC_NEW_TERMCTL) != 0) {
+			ahc_new_term_detect(ahc, &enableSEC_low,
+					    &enableSEC_high,
+					    &enablePRI_low,
+					    &enablePRI_high,
+					    &eeprom_present);
+			if ((adapter_control & CFSEAUTOTERM) == 0) {
+				if (bootverbose)
+					printk("%s: Manual SE Termination\n",
+					       ahc_name(ahc));
+				enableSEC_low = (adapter_control & CFSELOWTERM);
+				enableSEC_high =
+				    (adapter_control & CFSEHIGHTERM);
+			}
+			if ((adapter_control & CFAUTOTERM) == 0) {
+				if (bootverbose)
+					printk("%s: Manual LVD Termination\n",
+					       ahc_name(ahc));
+				enablePRI_low = (adapter_control & CFSTERM);
+				enablePRI_high = (adapter_control & CFWSTERM);
+			}
+			/* Make the table calculations below happy */
+			internal50_present = 0;
+			internal68_present = 1;
+			externalcable_present = 1;
+		} else if ((ahc->features & AHC_SPIOCAP) != 0) {
+			aic785X_cable_detect(ahc, &internal50_present,
+					     &externalcable_present,
+					     &eeprom_present);
+			/* Can never support a wide connector. */
+			internal68_present = 0;
+		} else {
+			aic787X_cable_detect(ahc, &internal50_present,
+					     &internal68_present,
+					     &externalcable_present,
+					     &eeprom_present);
+		}
+
+		if ((ahc->features & AHC_WIDE) == 0)
+			internal68_present = 0;
+
+		if (bootverbose
+		 && (ahc->features & AHC_ULTRA2) == 0) {
+			printk("%s: internal 50 cable %s present",
+			       ahc_name(ahc),
+			       internal50_present ? "is":"not");
+
+			if ((ahc->features & AHC_WIDE) != 0)
+				printk(", internal 68 cable %s present",
+				       internal68_present ? "is":"not");
+			printk("\n%s: external cable %s present\n",
+			       ahc_name(ahc),
+			       externalcable_present ? "is":"not");
+		}
+		if (bootverbose)
+			printk("%s: BIOS eeprom %s present\n",
+			       ahc_name(ahc), eeprom_present ? "is" : "not");
+
+		if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) {
+			/*
+			 * The 50 pin connector is a separate bus,
+			 * so force it to always be terminated.
+			 * In the future, perform current sensing
+			 * to determine if we are in the middle of
+			 * a properly terminated bus.
+			 */
+			internal50_present = 0;
+		}
+
+		/*
+		 * Now set the termination based on what
+		 * we found.
+		 * Flash Enable = BRDDAT7
+		 * Secondary High Term Enable = BRDDAT6
+		 * Secondary Low Term Enable = BRDDAT5 (7890)
+		 * Primary High Term Enable = BRDDAT4 (7890)
+		 */
+		if ((ahc->features & AHC_ULTRA2) == 0
+		 && (internal50_present != 0)
+		 && (internal68_present != 0)
+		 && (externalcable_present != 0)) {
+			printk("%s: Illegal cable configuration!!. "
+			       "Only two connectors on the "
+			       "adapter may be used at a "
+			       "time!\n", ahc_name(ahc));
+
+			/*
+			 * Pretend there are no cables in the hope
+			 * that having all of the termination on
+			 * gives us a more stable bus.
+			 */
+		 	internal50_present = 0;
+			internal68_present = 0;
+			externalcable_present = 0;
+		}
+
+		if ((ahc->features & AHC_WIDE) != 0
+		 && ((externalcable_present == 0)
+		  || (internal68_present == 0)
+		  || (enableSEC_high != 0))) {
+			brddat |= BRDDAT6;
+			if (bootverbose) {
+				if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+					printk("%s: 68 pin termination "
+					       "Enabled\n", ahc_name(ahc));
+				else
+					printk("%s: %sHigh byte termination "
+					       "Enabled\n", ahc_name(ahc),
+					       enableSEC_high ? "Secondary "
+							      : "");
+			}
+		}
+
+		sum = internal50_present + internal68_present
+		    + externalcable_present;
+		if (sum < 2 || (enableSEC_low != 0)) {
+			if ((ahc->features & AHC_ULTRA2) != 0)
+				brddat |= BRDDAT5;
+			else
+				*sxfrctl1 |= STPWEN;
+			if (bootverbose) {
+				if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+					printk("%s: 50 pin termination "
+					       "Enabled\n", ahc_name(ahc));
+				else
+					printk("%s: %sLow byte termination "
+					       "Enabled\n", ahc_name(ahc),
+					       enableSEC_low ? "Secondary "
+							     : "");
+			}
+		}
+
+		if (enablePRI_low != 0) {
+			*sxfrctl1 |= STPWEN;
+			if (bootverbose)
+				printk("%s: Primary Low Byte termination "
+				       "Enabled\n", ahc_name(ahc));
+		}
+
+		/*
+		 * Setup STPWEN before setting up the rest of
+		 * the termination per the tech note on the U160 cards.
+		 */
+		ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+		if (enablePRI_high != 0) {
+			brddat |= BRDDAT4;
+			if (bootverbose)
+				printk("%s: Primary High Byte "
+				       "termination Enabled\n",
+				       ahc_name(ahc));
+		}
+		
+		write_brdctl(ahc, brddat);
+
+	} else {
+		if ((adapter_control & CFSTERM) != 0) {
+			*sxfrctl1 |= STPWEN;
+
+			if (bootverbose)
+				printk("%s: %sLow byte termination Enabled\n",
+				       ahc_name(ahc),
+				       (ahc->features & AHC_ULTRA2) ? "Primary "
+								    : "");
+		}
+
+		if ((adapter_control & CFWSTERM) != 0
+		 && (ahc->features & AHC_WIDE) != 0) {
+			brddat |= BRDDAT6;
+			if (bootverbose)
+				printk("%s: %sHigh byte termination Enabled\n",
+				       ahc_name(ahc),
+				       (ahc->features & AHC_ULTRA2)
+				     ? "Secondary " : "");
+		}
+
+		/*
+		 * Setup STPWEN before setting up the rest of
+		 * the termination per the tech note on the U160 cards.
+		 */
+		ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+		if ((ahc->features & AHC_WIDE) != 0)
+			write_brdctl(ahc, brddat);
+	}
+	SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */
+}
+
+static void
+ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low,
+		    int *enableSEC_high, int *enablePRI_low,
+		    int *enablePRI_high, int *eeprom_present)
+{
+	uint8_t brdctl;
+
+	/*
+	 * BRDDAT7 = Eeprom
+	 * BRDDAT6 = Enable Secondary High Byte termination
+	 * BRDDAT5 = Enable Secondary Low Byte termination
+	 * BRDDAT4 = Enable Primary high byte termination
+	 * BRDDAT3 = Enable Primary low byte termination
+	 */
+	brdctl = read_brdctl(ahc);
+	*eeprom_present = brdctl & BRDDAT7;
+	*enableSEC_high = (brdctl & BRDDAT6);
+	*enableSEC_low = (brdctl & BRDDAT5);
+	*enablePRI_high = (brdctl & BRDDAT4);
+	*enablePRI_low = (brdctl & BRDDAT3);
+}
+
+static void
+aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+		     int *internal68_present, int *externalcable_present,
+		     int *eeprom_present)
+{
+	uint8_t brdctl;
+
+	/*
+	 * First read the status of our cables.
+	 * Set the rom bank to 0 since the
+	 * bank setting serves as a multiplexor
+	 * for the cable detection logic.
+	 * BRDDAT5 controls the bank switch.
+	 */
+	write_brdctl(ahc, 0);
+
+	/*
+	 * Now read the state of the internal
+	 * connectors.  BRDDAT6 is INT50 and
+	 * BRDDAT7 is INT68.
+	 */
+	brdctl = read_brdctl(ahc);
+	*internal50_present = (brdctl & BRDDAT6) ? 0 : 1;
+	*internal68_present = (brdctl & BRDDAT7) ? 0 : 1;
+
+	/*
+	 * Set the rom bank to 1 and determine
+	 * the other signals.
+	 */
+	write_brdctl(ahc, BRDDAT5);
+
+	/*
+	 * Now read the state of the external
+	 * connectors.  BRDDAT6 is EXT68 and
+	 * BRDDAT7 is EPROMPS.
+	 */
+	brdctl = read_brdctl(ahc);
+	*externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+	*eeprom_present = (brdctl & BRDDAT7) ? 1 : 0;
+}
+
+static void
+aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+		     int *externalcable_present, int *eeprom_present)
+{
+	uint8_t brdctl;
+	uint8_t spiocap;
+
+	spiocap = ahc_inb(ahc, SPIOCAP);
+	spiocap &= ~SOFTCMDEN;
+	spiocap |= EXT_BRDCTL;
+	ahc_outb(ahc, SPIOCAP, spiocap);
+	ahc_outb(ahc, BRDCTL, BRDRW|BRDCS);
+	ahc_flush_device_writes(ahc);
+	ahc_delay(500);
+	ahc_outb(ahc, BRDCTL, 0);
+	ahc_flush_device_writes(ahc);
+	ahc_delay(500);
+	brdctl = ahc_inb(ahc, BRDCTL);
+	*internal50_present = (brdctl & BRDDAT5) ? 0 : 1;
+	*externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+	*eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) ? 1 : 0;
+}
+	
+int
+ahc_acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
+{
+	int wait;
+
+	if ((ahc->features & AHC_SPIOCAP) != 0
+	 && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0)
+		return (0);
+
+	/*
+	 * 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 chip reset, there
+	 * should be no contention.
+	 */
+	SEEPROM_OUTB(sd, sd->sd_MS);
+	wait = 1000;  /* 1 second timeout in msec */
+	while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) {
+		ahc_delay(1000);  /* delay 1 msec */
+	}
+	if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) {
+		SEEPROM_OUTB(sd, 0); 
+		return (0);
+	}
+	return(1);
+}
+
+void
+ahc_release_seeprom(struct seeprom_descriptor *sd)
+{
+	/* Release access to the memory port and the serial EEPROM. */
+	SEEPROM_OUTB(sd, 0);
+}
+
+static void
+write_brdctl(struct ahc_softc *ahc, uint8_t value)
+{
+	uint8_t brdctl;
+
+	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+		brdctl = BRDSTB;
+	 	if (ahc->channel == 'B')
+			brdctl |= BRDCS;
+	} else if ((ahc->features & AHC_ULTRA2) != 0) {
+		brdctl = 0;
+	} else {
+		brdctl = BRDSTB|BRDCS;
+	}
+	ahc_outb(ahc, BRDCTL, brdctl);
+	ahc_flush_device_writes(ahc);
+	brdctl |= value;
+	ahc_outb(ahc, BRDCTL, brdctl);
+	ahc_flush_device_writes(ahc);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		brdctl |= BRDSTB_ULTRA2;
+	else
+		brdctl &= ~BRDSTB;
+	ahc_outb(ahc, BRDCTL, brdctl);
+	ahc_flush_device_writes(ahc);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		brdctl = 0;
+	else
+		brdctl &= ~BRDCS;
+	ahc_outb(ahc, BRDCTL, brdctl);
+}
+
+static uint8_t
+read_brdctl(struct ahc_softc *ahc)
+{
+	uint8_t brdctl;
+	uint8_t value;
+
+	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+		brdctl = BRDRW;
+	 	if (ahc->channel == 'B')
+			brdctl |= BRDCS;
+	} else if ((ahc->features & AHC_ULTRA2) != 0) {
+		brdctl = BRDRW_ULTRA2;
+	} else {
+		brdctl = BRDRW|BRDCS;
+	}
+	ahc_outb(ahc, BRDCTL, brdctl);
+	ahc_flush_device_writes(ahc);
+	value = ahc_inb(ahc, BRDCTL);
+	ahc_outb(ahc, BRDCTL, 0);
+	return (value);
+}
+
+static void
+ahc_pci_intr(struct ahc_softc *ahc)
+{
+	u_int error;
+	u_int status1;
+
+	error = ahc_inb(ahc, ERROR);
+	if ((error & PCIERRSTAT) == 0)
+		return;
+
+	status1 = ahc_pci_read_config(ahc->dev_softc,
+				      PCIR_STATUS + 1, /*bytes*/1);
+
+	printk("%s: PCI error Interrupt at seqaddr = 0x%x\n",
+	      ahc_name(ahc),
+	      ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+
+	if (status1 & DPE) {
+		ahc->pci_target_perr_count++;
+		printk("%s: Data Parity Error Detected during address "
+		       "or write data phase\n", ahc_name(ahc));
+	}
+	if (status1 & SSE) {
+		printk("%s: Signal System Error Detected\n", ahc_name(ahc));
+	}
+	if (status1 & RMA) {
+		printk("%s: Received a Master Abort\n", ahc_name(ahc));
+	}
+	if (status1 & RTA) {
+		printk("%s: Received a Target Abort\n", ahc_name(ahc));
+	}
+	if (status1 & STA) {
+		printk("%s: Signaled a Target Abort\n", ahc_name(ahc));
+	}
+	if (status1 & DPR) {
+		printk("%s: Data Parity Error has been reported via PERR#\n",
+		       ahc_name(ahc));
+	}
+
+	/* Clear latched errors. */
+	ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+			     status1, /*bytes*/1);
+
+	if ((status1 & (DPE|SSE|RMA|RTA|STA|DPR)) == 0) {
+		printk("%s: Latched PCIERR interrupt with "
+		       "no status bits set\n", ahc_name(ahc)); 
+	} else {
+		ahc_outb(ahc, CLRINT, CLRPARERR);
+	}
+
+	if (ahc->pci_target_perr_count > AHC_PCI_TARGET_PERR_THRESH) {
+		printk(
+"%s: WARNING WARNING WARNING WARNING\n"
+"%s: Too many PCI parity errors observed as a target.\n"
+"%s: Some device on this bus is generating bad parity.\n"
+"%s: This is an error *observed by*, not *generated by*, this controller.\n"
+"%s: PCI parity error checking has been disabled.\n"
+"%s: WARNING WARNING WARNING WARNING\n",
+		       ahc_name(ahc), ahc_name(ahc), ahc_name(ahc),
+		       ahc_name(ahc), ahc_name(ahc), ahc_name(ahc));
+		ahc->seqctl |= FAILDIS;
+		ahc_outb(ahc, SEQCTL, ahc->seqctl);
+	}
+	ahc_unpause(ahc);
+}
+
+static int
+ahc_pci_chip_init(struct ahc_softc *ahc)
+{
+	ahc_outb(ahc, DSCOMMAND0, ahc->bus_softc.pci_softc.dscommand0);
+	ahc_outb(ahc, DSPCISTATUS, ahc->bus_softc.pci_softc.dspcistatus);
+	if ((ahc->features & AHC_DT) != 0) {
+		u_int sfunct;
+
+		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+		ahc_outb(ahc, OPTIONMODE, ahc->bus_softc.pci_softc.optionmode);
+		ahc_outw(ahc, TARGCRCCNT, ahc->bus_softc.pci_softc.targcrccnt);
+		ahc_outb(ahc, SFUNCT, sfunct);
+		ahc_outb(ahc, CRCCONTROL1,
+			 ahc->bus_softc.pci_softc.crccontrol1);
+	}
+	if ((ahc->features & AHC_MULTI_FUNC) != 0)
+		ahc_outb(ahc, SCBBADDR, ahc->bus_softc.pci_softc.scbbaddr);
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, DFF_THRSH, ahc->bus_softc.pci_softc.dff_thrsh);
+
+	return (ahc_chip_init(ahc));
+}
+
+#ifdef CONFIG_PM
+void
+ahc_pci_resume(struct ahc_softc *ahc)
+{
+	/*
+	 * We assume that the OS has restored our register
+	 * mappings, etc.  Just update the config space registers
+	 * that the OS doesn't know about and rely on our chip
+	 * reset handler to handle the rest.
+	 */
+	ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+			     ahc->bus_softc.pci_softc.devconfig, /*bytes*/4);
+	ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
+			     ahc->bus_softc.pci_softc.command, /*bytes*/1);
+	ahc_pci_write_config(ahc->dev_softc, CSIZE_LATTIME,
+			     ahc->bus_softc.pci_softc.csize_lattime, /*bytes*/1);
+	if ((ahc->flags & AHC_HAS_TERM_LOGIC) != 0) {
+		struct	seeprom_descriptor sd;
+		u_int	sxfrctl1;
+
+		sd.sd_ahc = ahc;
+		sd.sd_control_offset = SEECTL;		
+		sd.sd_status_offset = SEECTL;		
+		sd.sd_dataout_offset = SEECTL;		
+
+		ahc_acquire_seeprom(ahc, &sd);
+		configure_termination(ahc, &sd,
+				      ahc->seep_config->adapter_control,
+				      &sxfrctl1);
+		ahc_release_seeprom(&sd);
+	}
+}
+#endif
+
+static int
+ahc_aic785X_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+	uint8_t rev;
+
+	pci = ahc->dev_softc;
+	ahc->channel = 'A';
+	ahc->chip = AHC_AIC7850;
+	ahc->features = AHC_AIC7850_FE;
+	ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev >= 1)
+		ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+	ahc->instruction_ram_size = 512;
+	return (0);
+}
+
+static int
+ahc_aic7860_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+	uint8_t rev;
+
+	pci = ahc->dev_softc;
+	ahc->channel = 'A';
+	ahc->chip = AHC_AIC7860;
+	ahc->features = AHC_AIC7860_FE;
+	ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev >= 1)
+		ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+	ahc->instruction_ram_size = 512;
+	return (0);
+}
+
+static int
+ahc_apa1480_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7860_setup(ahc);
+	if (error != 0)
+		return (error);
+	ahc->features |= AHC_REMOVABLE;
+	return (0);
+}
+
+static int
+ahc_aic7870_setup(struct ahc_softc *ahc)
+{
+
+	ahc->channel = 'A';
+	ahc->chip = AHC_AIC7870;
+	ahc->features = AHC_AIC7870_FE;
+	ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+	ahc->instruction_ram_size = 512;
+	return (0);
+}
+
+static int
+ahc_aic7870h_setup(struct ahc_softc *ahc)
+{
+	int error = ahc_aic7870_setup(ahc);
+
+	ahc->features |= AHC_HVD;
+
+	return error;
+}
+
+static int
+ahc_aha394X_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7870_setup(ahc);
+	if (error == 0)
+		error = ahc_aha394XX_setup(ahc);
+	return (error);
+}
+
+static int
+ahc_aha394Xh_setup(struct ahc_softc *ahc)
+{
+	int error = ahc_aha394X_setup(ahc);
+
+	ahc->features |= AHC_HVD;
+
+	return error;
+}
+
+static int
+ahc_aha398X_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7870_setup(ahc);
+	if (error == 0)
+		error = ahc_aha398XX_setup(ahc);
+	return (error);
+}
+
+static int
+ahc_aha494X_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7870_setup(ahc);
+	if (error == 0)
+		error = ahc_aha494XX_setup(ahc);
+	return (error);
+}
+
+static int
+ahc_aha494Xh_setup(struct ahc_softc *ahc)
+{
+	int error = ahc_aha494X_setup(ahc);
+
+	ahc->features |= AHC_HVD;
+
+	return error;
+}
+
+static int
+ahc_aic7880_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+	uint8_t rev;
+
+	pci = ahc->dev_softc;
+	ahc->channel = 'A';
+	ahc->chip = AHC_AIC7880;
+	ahc->features = AHC_AIC7880_FE;
+	ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev >= 1) {
+		ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+	} else {
+		ahc->bugs |= AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+	}
+	ahc->instruction_ram_size = 512;
+	return (0);
+}
+
+static int
+ahc_aic7880h_setup(struct ahc_softc *ahc)
+{
+	int error = ahc_aic7880_setup(ahc);
+
+	ahc->features |= AHC_HVD;
+
+	return error;
+}
+
+
+static int
+ahc_aha2940Pro_setup(struct ahc_softc *ahc)
+{
+
+	ahc->flags |= AHC_INT50_SPEEDFLEX;
+	return (ahc_aic7880_setup(ahc));
+}
+
+static int
+ahc_aha394XU_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7880_setup(ahc);
+	if (error == 0)
+		error = ahc_aha394XX_setup(ahc);
+	return (error);
+}
+
+static int
+ahc_aha394XUh_setup(struct ahc_softc *ahc)
+{
+	int error = ahc_aha394XU_setup(ahc);
+
+	ahc->features |= AHC_HVD;
+
+	return error;
+}
+
+static int
+ahc_aha398XU_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7880_setup(ahc);
+	if (error == 0)
+		error = ahc_aha398XX_setup(ahc);
+	return (error);
+}
+
+static int
+ahc_aic7890_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+	uint8_t rev;
+
+	pci = ahc->dev_softc;
+	ahc->channel = 'A';
+	ahc->chip = AHC_AIC7890;
+	ahc->features = AHC_AIC7890_FE;
+	ahc->flags |= AHC_NEWEEPROM_FMT;
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev == 0)
+		ahc->bugs |= AHC_AUTOFLUSH_BUG|AHC_CACHETHEN_BUG;
+	ahc->instruction_ram_size = 768;
+	return (0);
+}
+
+static int
+ahc_aic7892_setup(struct ahc_softc *ahc)
+{
+
+	ahc->channel = 'A';
+	ahc->chip = AHC_AIC7892;
+	ahc->features = AHC_AIC7892_FE;
+	ahc->flags |= AHC_NEWEEPROM_FMT;
+	ahc->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+	ahc->instruction_ram_size = 1024;
+	return (0);
+}
+
+static int
+ahc_aic7895_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+	uint8_t rev;
+
+	pci = ahc->dev_softc;
+	ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+	/*
+	 * The 'C' revision of the aic7895 has a few additional features.
+	 */
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev >= 4) {
+		ahc->chip = AHC_AIC7895C;
+		ahc->features = AHC_AIC7895C_FE;
+	} else  {
+		u_int command;
+
+		ahc->chip = AHC_AIC7895;
+		ahc->features = AHC_AIC7895_FE;
+
+		/*
+		 * The BIOS disables the use of MWI transactions
+		 * since it does not have the MWI bug work around
+		 * we have.  Disabling MWI reduces performance, so
+		 * turn it on again.
+		 */
+		command = ahc_pci_read_config(pci, PCIR_COMMAND, /*bytes*/1);
+		command |= PCIM_CMD_MWRICEN;
+		ahc_pci_write_config(pci, PCIR_COMMAND, command, /*bytes*/1);
+		ahc->bugs |= AHC_PCI_MWI_BUG;
+	}
+	/*
+	 * XXX Does CACHETHEN really not work???  What about PCI retry?
+	 * on C level chips.  Need to test, but for now, play it safe.
+	 */
+	ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_PCI_2_1_RETRY_BUG
+		  |  AHC_CACHETHEN_BUG;
+
+#if 0
+	uint32_t devconfig;
+
+	/*
+	 * Cachesize must also be zero due to stray DAC
+	 * problem when sitting behind some bridges.
+	 */
+	ahc_pci_write_config(pci, CSIZE_LATTIME, 0, /*bytes*/1);
+	devconfig = ahc_pci_read_config(pci, DEVCONFIG, /*bytes*/1);
+	devconfig |= MRDCEN;
+	ahc_pci_write_config(pci, DEVCONFIG, devconfig, /*bytes*/1);
+#endif
+	ahc->flags |= AHC_NEWEEPROM_FMT;
+	ahc->instruction_ram_size = 512;
+	return (0);
+}
+
+static int
+ahc_aic7895h_setup(struct ahc_softc *ahc)
+{
+	int error = ahc_aic7895_setup(ahc);
+
+	ahc->features |= AHC_HVD;
+
+	return error;
+}
+
+static int
+ahc_aic7896_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+
+	pci = ahc->dev_softc;
+	ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+	ahc->chip = AHC_AIC7896;
+	ahc->features = AHC_AIC7896_FE;
+	ahc->flags |= AHC_NEWEEPROM_FMT;
+	ahc->bugs |= AHC_CACHETHEN_DIS_BUG;
+	ahc->instruction_ram_size = 768;
+	return (0);
+}
+
+static int
+ahc_aic7899_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+
+	pci = ahc->dev_softc;
+	ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+	ahc->chip = AHC_AIC7899;
+	ahc->features = AHC_AIC7899_FE;
+	ahc->flags |= AHC_NEWEEPROM_FMT;
+	ahc->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+	ahc->instruction_ram_size = 1024;
+	return (0);
+}
+
+static int
+ahc_aha29160C_setup(struct ahc_softc *ahc)
+{
+	int error;
+
+	error = ahc_aic7899_setup(ahc);
+	if (error != 0)
+		return (error);
+	ahc->features |= AHC_REMOVABLE;
+	return (0);
+}
+
+static int
+ahc_raid_setup(struct ahc_softc *ahc)
+{
+	printk("RAID functionality unsupported\n");
+	return (ENXIO);
+}
+
+static int
+ahc_aha394XX_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+
+	pci = ahc->dev_softc;
+	switch (ahc_get_pci_slot(pci)) {
+	case AHC_394X_SLOT_CHANNEL_A:
+		ahc->channel = 'A';
+		break;
+	case AHC_394X_SLOT_CHANNEL_B:
+		ahc->channel = 'B';
+		break;
+	default:
+		printk("adapter at unexpected slot %d\n"
+		       "unable to map to a channel\n",
+		       ahc_get_pci_slot(pci));
+		ahc->channel = 'A';
+	}
+	return (0);
+}
+
+static int
+ahc_aha398XX_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+
+	pci = ahc->dev_softc;
+	switch (ahc_get_pci_slot(pci)) {
+	case AHC_398X_SLOT_CHANNEL_A:
+		ahc->channel = 'A';
+		break;
+	case AHC_398X_SLOT_CHANNEL_B:
+		ahc->channel = 'B';
+		break;
+	case AHC_398X_SLOT_CHANNEL_C:
+		ahc->channel = 'C';
+		break;
+	default:
+		printk("adapter at unexpected slot %d\n"
+		       "unable to map to a channel\n",
+		       ahc_get_pci_slot(pci));
+		ahc->channel = 'A';
+		break;
+	}
+	ahc->flags |= AHC_LARGE_SEEPROM;
+	return (0);
+}
+
+static int
+ahc_aha494XX_setup(struct ahc_softc *ahc)
+{
+	ahc_dev_softc_t pci;
+
+	pci = ahc->dev_softc;
+	switch (ahc_get_pci_slot(pci)) {
+	case AHC_494X_SLOT_CHANNEL_A:
+		ahc->channel = 'A';
+		break;
+	case AHC_494X_SLOT_CHANNEL_B:
+		ahc->channel = 'B';
+		break;
+	case AHC_494X_SLOT_CHANNEL_C:
+		ahc->channel = 'C';
+		break;
+	case AHC_494X_SLOT_CHANNEL_D:
+		ahc->channel = 'D';
+		break;
+	default:
+		printk("adapter at unexpected slot %d\n"
+		       "unable to map to a channel\n",
+		       ahc_get_pci_slot(pci));
+		ahc->channel = 'A';
+	}
+	ahc->flags |= AHC_LARGE_SEEPROM;
+	return (0);
+}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.h b/drivers/scsi/aic7xxx/aic7xxx_pci.h
new file mode 100644
index 0000000..263f85d
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_pci.h
@@ -0,0 +1,125 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id$
+ *
+ */
+#ifndef _AIC7XXX_PCI_H_
+#define _AIC7XXX_PCI_H_
+
+#define ID_ALL_MASK			0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK		0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK		0xFFF0FFFF00000000ull
+#define ID_9005_SISL_MASK		0x000FFFFF00000000ull
+#define ID_9005_SISL_ID			0x0005900500000000ull
+#define ID_AIC7850			0x5078900400000000ull
+#define ID_AHA_2902_04_10_15_20C_30C	0x5078900478509004ull
+#define ID_AIC7855			0x5578900400000000ull
+#define ID_AIC7859			0x3860900400000000ull
+#define ID_AHA_2930CU			0x3860900438699004ull
+#define ID_AIC7860			0x6078900400000000ull
+#define ID_AIC7860C			0x6078900478609004ull
+#define ID_AHA_1480A			0x6075900400000000ull
+#define ID_AHA_2940AU_0			0x6178900400000000ull
+#define ID_AHA_2940AU_1			0x6178900478619004ull
+#define ID_AHA_2940AU_CN		0x2178900478219004ull
+#define ID_AHA_2930C_VAR		0x6038900438689004ull
+
+#define ID_AIC7870			0x7078900400000000ull
+#define ID_AHA_2940			0x7178900400000000ull
+#define ID_AHA_3940			0x7278900400000000ull
+#define ID_AHA_398X			0x7378900400000000ull
+#define ID_AHA_2944			0x7478900400000000ull
+#define ID_AHA_3944			0x7578900400000000ull
+#define ID_AHA_4944			0x7678900400000000ull
+
+#define ID_AIC7880			0x8078900400000000ull
+#define ID_AIC7880_B			0x8078900478809004ull
+#define ID_AHA_2940U			0x8178900400000000ull
+#define ID_AHA_3940U			0x8278900400000000ull
+#define ID_AHA_2944U			0x8478900400000000ull
+#define ID_AHA_3944U			0x8578900400000000ull
+#define ID_AHA_398XU			0x8378900400000000ull
+#define ID_AHA_4944U			0x8678900400000000ull
+#define ID_AHA_2940UB			0x8178900478819004ull
+#define ID_AHA_2930U			0x8878900478889004ull
+#define ID_AHA_2940U_PRO		0x8778900478879004ull
+#define ID_AHA_2940U_CN			0x0078900478009004ull
+
+#define ID_AIC7895			0x7895900478959004ull
+#define ID_AIC7895_ARO			0x7890900478939004ull
+#define ID_AIC7895_ARO_MASK		0xFFF0FFFFFFFFFFFFull
+#define ID_AHA_2940U_DUAL		0x7895900478919004ull
+#define ID_AHA_3940AU			0x7895900478929004ull
+#define ID_AHA_3944AU			0x7895900478949004ull
+
+#define ID_AIC7890			0x001F9005000F9005ull
+#define ID_AIC7890_ARO			0x00139005000F9005ull
+#define ID_AAA_131U2			0x0013900500039005ull
+#define ID_AHA_2930U2			0x0011900501819005ull
+#define ID_AHA_2940U2B			0x00109005A1009005ull
+#define ID_AHA_2940U2_OEM		0x0010900521809005ull
+#define ID_AHA_2940U2			0x00109005A1809005ull
+#define ID_AHA_2950U2B			0x00109005E1009005ull
+
+#define ID_AIC7892			0x008F9005FFFF9005ull
+#define ID_AIC7892_ARO			0x00839005FFFF9005ull
+#define ID_AHA_29160			0x00809005E2A09005ull
+#define ID_AHA_29160_CPQ		0x00809005E2A00E11ull
+#define ID_AHA_29160N			0x0080900562A09005ull
+#define ID_AHA_29160C			0x0080900562209005ull
+#define ID_AHA_29160B			0x00809005E2209005ull
+#define ID_AHA_19160B			0x0081900562A19005ull
+#define ID_AHA_2915_30LP		0x0082900502109005ull
+
+#define ID_AIC7896			0x005F9005FFFF9005ull
+#define ID_AIC7896_ARO			0x00539005FFFF9005ull
+#define ID_AHA_3950U2B_0		0x00509005FFFF9005ull
+#define ID_AHA_3950U2B_1		0x00509005F5009005ull
+#define ID_AHA_3950U2D_0		0x00519005FFFF9005ull
+#define ID_AHA_3950U2D_1		0x00519005B5009005ull
+
+#define ID_AIC7899			0x00CF9005FFFF9005ull
+#define ID_AIC7899_ARO			0x00C39005FFFF9005ull
+#define ID_AHA_3960D			0x00C09005F6209005ull
+#define ID_AHA_3960D_CPQ		0x00C09005F6200E11ull
+
+#define ID_AIC7810			0x1078900400000000ull
+#define ID_AIC7815			0x7815900400000000ull
+
+#endif /* _AIC7XXX_PCI_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_proc.c b/drivers/scsi/aic7xxx/aic7xxx_proc.c
new file mode 100644
index 0000000..1845960
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#29 $
+ */
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+static void	ahc_dump_target_state(struct ahc_softc *ahc,
+				      struct seq_file *m,
+				      u_int our_id, char channel,
+				      u_int target_id, u_int target_offset);
+static void	ahc_dump_device_state(struct seq_file *m,
+				      struct scsi_device *dev);
+
+/*
+ * Table of syncrates that don't follow the "divisible by 4"
+ * rule. This table will be expanded in future SCSI specs.
+ */
+static const struct {
+	u_int period_factor;
+	u_int period;	/* in 100ths of ns */
+} scsi_syncrates[] = {
+	{ 0x08, 625 },	/* FAST-160 */
+	{ 0x09, 1250 },	/* FAST-80 */
+	{ 0x0a, 2500 },	/* FAST-40 40MHz */
+	{ 0x0b, 3030 },	/* FAST-40 33MHz */
+	{ 0x0c, 5000 }	/* FAST-20 */
+};
+
+/*
+ * Return the frequency in kHz corresponding to the given
+ * sync period factor.
+ */
+static u_int
+ahc_calc_syncsrate(u_int period_factor)
+{
+	int i;
+
+	/* See if the period is in the "exception" table */
+	for (i = 0; i < ARRAY_SIZE(scsi_syncrates); i++) {
+
+		if (period_factor == scsi_syncrates[i].period_factor) {
+			/* Period in kHz */
+			return (100000000 / scsi_syncrates[i].period);
+		}
+	}
+
+	/*
+	 * Wasn't in the table, so use the standard
+	 * 4 times conversion.
+	 */
+	return (10000000 / (period_factor * 4 * 10));
+}
+
+static void
+ahc_format_transinfo(struct seq_file *m, struct ahc_transinfo *tinfo)
+{
+	u_int speed;
+	u_int freq;
+	u_int mb;
+
+        speed = 3300;
+        freq = 0;
+	if (tinfo->offset != 0) {
+		freq = ahc_calc_syncsrate(tinfo->period);
+		speed = freq;
+	}
+	speed *= (0x01 << tinfo->width);
+        mb = speed / 1000;
+        if (mb > 0)
+		seq_printf(m, "%d.%03dMB/s transfers", mb, speed % 1000);
+        else
+		seq_printf(m, "%dKB/s transfers", speed);
+
+	if (freq != 0) {
+		seq_printf(m, " (%d.%03dMHz%s, offset %d",
+			 freq / 1000, freq % 1000,
+			 (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+			 ? " DT" : "", tinfo->offset);
+	}
+
+	if (tinfo->width > 0) {
+		if (freq != 0) {
+			seq_puts(m, ", ");
+		} else {
+			seq_puts(m, " (");
+		}
+		seq_printf(m, "%dbit)", 8 * (0x01 << tinfo->width));
+	} else if (freq != 0) {
+		seq_putc(m, ')');
+	}
+	seq_putc(m, '\n');
+}
+
+static void
+ahc_dump_target_state(struct ahc_softc *ahc, struct seq_file *m,
+		      u_int our_id, char channel, u_int target_id,
+		      u_int target_offset)
+{
+	struct	scsi_target *starget;
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	int	lun;
+
+	tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+				    target_id, &tstate);
+	if ((ahc->features & AHC_TWIN) != 0)
+		seq_printf(m, "Channel %c ", channel);
+	seq_printf(m, "Target %d Negotiation Settings\n", target_id);
+	seq_puts(m, "\tUser: ");
+	ahc_format_transinfo(m, &tinfo->user);
+	starget = ahc->platform_data->starget[target_offset];
+	if (!starget)
+		return;
+
+	seq_puts(m, "\tGoal: ");
+	ahc_format_transinfo(m, &tinfo->goal);
+	seq_puts(m, "\tCurr: ");
+	ahc_format_transinfo(m, &tinfo->curr);
+
+	for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+		struct scsi_device *sdev;
+
+		sdev = scsi_device_lookup_by_target(starget, lun);
+
+		if (sdev == NULL)
+			continue;
+
+		ahc_dump_device_state(m, sdev);
+	}
+}
+
+static void
+ahc_dump_device_state(struct seq_file *m, struct scsi_device *sdev)
+{
+	struct ahc_linux_device *dev = scsi_transport_device_data(sdev);
+
+	seq_printf(m, "\tChannel %c Target %d Lun %d Settings\n",
+		  sdev->sdev_target->channel + 'A',
+		   sdev->sdev_target->id, (u8)sdev->lun);
+
+	seq_printf(m, "\t\tCommands Queued %ld\n", dev->commands_issued);
+	seq_printf(m, "\t\tCommands Active %d\n", dev->active);
+	seq_printf(m, "\t\tCommand Openings %d\n", dev->openings);
+	seq_printf(m, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+	seq_printf(m, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+int
+ahc_proc_write_seeprom(struct Scsi_Host *shost, char *buffer, int length)
+{
+	struct	ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata;
+	struct seeprom_descriptor sd;
+	int have_seeprom;
+	u_long s;
+	int paused;
+	int written;
+
+	/* Default to failure. */
+	written = -EINVAL;
+	ahc_lock(ahc, &s);
+	paused = ahc_is_paused(ahc);
+	if (!paused)
+		ahc_pause(ahc);
+
+	if (length != sizeof(struct seeprom_config)) {
+		printk("ahc_proc_write_seeprom: incorrect buffer size\n");
+		goto done;
+	}
+
+	have_seeprom = ahc_verify_cksum((struct seeprom_config*)buffer);
+	if (have_seeprom == 0) {
+		printk("ahc_proc_write_seeprom: cksum verification failed\n");
+		goto done;
+	}
+
+	sd.sd_ahc = ahc;
+#if AHC_PCI_CONFIG > 0
+	if ((ahc->chip & AHC_PCI) != 0) {
+		sd.sd_control_offset = SEECTL;
+		sd.sd_status_offset = SEECTL;
+		sd.sd_dataout_offset = SEECTL;
+		if (ahc->flags & AHC_LARGE_SEEPROM)
+			sd.sd_chip = C56_66;
+		else
+			sd.sd_chip = C46;
+		sd.sd_MS = SEEMS;
+		sd.sd_RDY = SEERDY;
+		sd.sd_CS = SEECS;
+		sd.sd_CK = SEECK;
+		sd.sd_DO = SEEDO;
+		sd.sd_DI = SEEDI;
+		have_seeprom = ahc_acquire_seeprom(ahc, &sd);
+	} else
+#endif
+	if ((ahc->chip & AHC_VL) != 0) {
+		sd.sd_control_offset = SEECTL_2840;
+		sd.sd_status_offset = STATUS_2840;
+		sd.sd_dataout_offset = STATUS_2840;		
+		sd.sd_chip = C46;
+		sd.sd_MS = 0;
+		sd.sd_RDY = EEPROM_TF;
+		sd.sd_CS = CS_2840;
+		sd.sd_CK = CK_2840;
+		sd.sd_DO = DO_2840;
+		sd.sd_DI = DI_2840;
+		have_seeprom = TRUE;
+	} else {
+		printk("ahc_proc_write_seeprom: unsupported adapter type\n");
+		goto done;
+	}
+
+	if (!have_seeprom) {
+		printk("ahc_proc_write_seeprom: No Serial EEPROM\n");
+		goto done;
+	} else {
+		u_int start_addr;
+
+		if (ahc->seep_config == NULL) {
+			ahc->seep_config = kmalloc(sizeof(*ahc->seep_config), GFP_ATOMIC);
+			if (ahc->seep_config == NULL) {
+				printk("aic7xxx: Unable to allocate serial "
+				       "eeprom buffer.  Write failing\n");
+				goto done;
+			}
+		}
+		printk("aic7xxx: Writing Serial EEPROM\n");
+		start_addr = 32 * (ahc->channel - 'A');
+		ahc_write_seeprom(&sd, (u_int16_t *)buffer, start_addr,
+				  sizeof(struct seeprom_config)/2);
+		ahc_read_seeprom(&sd, (uint16_t *)ahc->seep_config,
+				 start_addr, sizeof(struct seeprom_config)/2);
+#if AHC_PCI_CONFIG > 0
+		if ((ahc->chip & AHC_VL) == 0)
+			ahc_release_seeprom(&sd);
+#endif
+		written = length;
+	}
+
+done:
+	if (!paused)
+		ahc_unpause(ahc);
+	ahc_unlock(ahc, &s);
+	return (written);
+}
+
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+ahc_linux_show_info(struct seq_file *m, struct Scsi_Host *shost)
+{
+	struct	ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata;
+	char	ahc_info[256];
+	u_int	max_targ;
+	u_int	i;
+
+	seq_printf(m, "Adaptec AIC7xxx driver version: %s\n",
+		  AIC7XXX_DRIVER_VERSION);
+	seq_printf(m, "%s\n", ahc->description);
+	ahc_controller_info(ahc, ahc_info);
+	seq_printf(m, "%s\n", ahc_info);
+	seq_printf(m, "Allocated SCBs: %d, SG List Length: %d\n\n",
+		  ahc->scb_data->numscbs, AHC_NSEG);
+
+
+	if (ahc->seep_config == NULL)
+		seq_puts(m, "No Serial EEPROM\n");
+	else {
+		seq_puts(m, "Serial EEPROM:\n");
+		for (i = 0; i < sizeof(*ahc->seep_config)/2; i++) {
+			if (((i % 8) == 0) && (i != 0)) {
+				seq_putc(m, '\n');
+			}
+			seq_printf(m, "0x%.4x ",
+				  ((uint16_t*)ahc->seep_config)[i]);
+		}
+		seq_putc(m, '\n');
+	}
+	seq_putc(m, '\n');
+
+	max_targ = 16;
+	if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+		max_targ = 8;
+
+	for (i = 0; i < max_targ; i++) {
+		u_int our_id;
+		u_int target_id;
+		char channel;
+
+		channel = 'A';
+		our_id = ahc->our_id;
+		target_id = i;
+		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+			channel = 'B';
+			our_id = ahc->our_id_b;
+			target_id = i % 8;
+		}
+
+		ahc_dump_target_state(ahc, m, our_id,
+				      channel, target_id, i);
+	}
+	return 0;
+}
diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
new file mode 100644
index 0000000..e821082
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped
@@ -0,0 +1,912 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *		 from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#58 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#40 $
+ */
+typedef int (ahc_reg_print_t)(u_int, u_int *, u_int);
+typedef struct ahc_reg_parse_entry {
+	char	*name;
+	uint8_t	 value;
+	uint8_t	 mask;
+} ahc_reg_parse_entry_t;
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiseq_print;
+#else
+#define ahc_scsiseq_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCSISEQ", 0x00, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sxfrctl0_print;
+#else
+#define ahc_sxfrctl0_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SXFRCTL0", 0x01, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsisigi_print;
+#else
+#define ahc_scsisigi_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCSISIGI", 0x03, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsirate_print;
+#else
+#define ahc_scsirate_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCSIRATE", 0x04, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat0_print;
+#else
+#define ahc_sstat0_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SSTAT0", 0x0b, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat1_print;
+#else
+#define ahc_sstat1_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SSTAT1", 0x0c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat2_print;
+#else
+#define ahc_sstat2_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SSTAT2", 0x0d, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sstat3_print;
+#else
+#define ahc_sstat3_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SSTAT3", 0x0e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_simode0_print;
+#else
+#define ahc_simode0_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SIMODE0", 0x10, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_simode1_print;
+#else
+#define ahc_simode1_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SIMODE1", 0x11, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsibusl_print;
+#else
+#define ahc_scsibusl_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCSIBUSL", 0x12, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sblkctl_print;
+#else
+#define ahc_sblkctl_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SBLKCTL", 0x1f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seq_flags_print;
+#else
+#define ahc_seq_flags_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SEQ_FLAGS", 0x3c, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_lastphase_print;
+#else
+#define ahc_lastphase_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "LASTPHASE", 0x3f, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_seqctl_print;
+#else
+#define ahc_seqctl_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SEQCTL", 0x60, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_sram_base_print;
+#else
+#define ahc_sram_base_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SRAM_BASE", 0x70, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_error_print;
+#else
+#define ahc_error_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "ERROR", 0x92, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfcntrl_print;
+#else
+#define ahc_dfcntrl_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "DFCNTRL", 0x93, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_dfstatus_print;
+#else
+#define ahc_dfstatus_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "DFSTATUS", 0x94, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scsiphase_print;
+#else
+#define ahc_scsiphase_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCSIPHASE", 0x9e, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_base_print;
+#else
+#define ahc_scb_base_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCB_BASE", 0xa0, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_control_print;
+#else
+#define ahc_scb_control_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCB_CONTROL", 0xb8, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_scsiid_print;
+#else
+#define ahc_scb_scsiid_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCB_SCSIID", 0xb9, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_lun_print;
+#else
+#define ahc_scb_lun_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCB_LUN", 0xba, regvalue, cur_col, wrap)
+#endif
+
+#if AIC_DEBUG_REGISTERS
+ahc_reg_print_t ahc_scb_tag_print;
+#else
+#define ahc_scb_tag_print(regvalue, cur_col, wrap) \
+    ahc_print_register(NULL, 0, "SCB_TAG", 0xbb, regvalue, cur_col, wrap)
+#endif
+
+
+#define	SCSISEQ         		0x00
+#define		TEMODE          	0x80
+#define		SCSIRSTO        	0x01
+
+#define	SXFRCTL0        		0x01
+#define		DFON            	0x80
+#define		DFPEXP          	0x40
+#define		FAST20          	0x20
+#define		CLRSTCNT        	0x10
+#define		SPIOEN          	0x08
+#define		SCAMEN          	0x04
+#define		CLRCHN          	0x02
+
+#define	SXFRCTL1        		0x02
+#define		STIMESEL        	0x18
+#define		BITBUCKET       	0x80
+#define		SWRAPEN         	0x40
+#define		ENSTIMER        	0x04
+#define		ACTNEGEN        	0x02
+#define		STPWEN          	0x01
+
+#define	SCSISIGO        		0x03
+#define		CDO             	0x80
+#define		IOO             	0x40
+#define		MSGO            	0x20
+#define		ATNO            	0x10
+#define		SELO            	0x08
+#define		BSYO            	0x04
+#define		REQO            	0x02
+#define		ACKO            	0x01
+
+#define	SCSISIGI        		0x03
+#define		P_DATAIN_DT     	0x60
+#define		P_DATAOUT_DT    	0x20
+#define		ATNI            	0x10
+#define		SELI            	0x08
+#define		BSYI            	0x04
+#define		REQI            	0x02
+#define		ACKI            	0x01
+
+#define	SCSIRATE        		0x04
+#define		SXFR            	0x70
+#define		SOFS            	0x0f
+#define		SXFR_ULTRA2     	0x0f
+#define		WIDEXFER        	0x80
+#define		ENABLE_CRC      	0x40
+#define		SINGLE_EDGE     	0x10
+
+#define	SCSIID          		0x05
+#define	SCSIOFFSET      		0x05
+#define		SOFS_ULTRA2     	0x7f
+
+#define	SCSIDATL        		0x06
+
+#define	SCSIDATH        		0x07
+
+#define	STCNT           		0x08
+
+#define	OPTIONMODE      		0x08
+#define		OPTIONMODE_DEFAULTS	0x03
+#define		AUTORATEEN      	0x80
+#define		AUTOACKEN       	0x40
+#define		ATNMGMNTEN      	0x20
+#define		BUSFREEREV      	0x10
+#define		EXPPHASEDIS     	0x08
+#define		SCSIDATL_IMGEN  	0x04
+#define		AUTO_MSGOUT_DE  	0x02
+#define		DIS_MSGIN_DUALEDGE	0x01
+
+#define	TARGCRCCNT      		0x0a
+
+#define	CLRSINT0        		0x0b
+#define		CLRSELDO        	0x40
+#define		CLRSELDI        	0x20
+#define		CLRSELINGO      	0x10
+#define		CLRIOERR        	0x08
+#define		CLRSWRAP        	0x08
+#define		CLRSPIORDY      	0x02
+
+#define	SSTAT0          		0x0b
+#define		TARGET          	0x80
+#define		SELDO           	0x40
+#define		SELDI           	0x20
+#define		SELINGO         	0x10
+#define		SWRAP           	0x08
+#define		IOERR           	0x08
+#define		SDONE           	0x04
+#define		SPIORDY         	0x02
+#define		DMADONE         	0x01
+
+#define	CLRSINT1        		0x0c
+#define		CLRSELTIMEO     	0x80
+#define		CLRATNO         	0x40
+#define		CLRSCSIRSTI     	0x20
+#define		CLRBUSFREE      	0x08
+#define		CLRSCSIPERR     	0x04
+#define		CLRPHASECHG     	0x02
+#define		CLRREQINIT      	0x01
+
+#define	SSTAT1          		0x0c
+#define		SELTO           	0x80
+#define		ATNTARG         	0x40
+#define		SCSIRSTI        	0x20
+#define		PHASEMIS        	0x10
+#define		BUSFREE         	0x08
+#define		SCSIPERR        	0x04
+#define		PHASECHG        	0x02
+#define		REQINIT         	0x01
+
+#define	SSTAT2          		0x0d
+#define		SFCNT           	0x1f
+#define		OVERRUN         	0x80
+#define		SHVALID         	0x40
+#define		EXP_ACTIVE      	0x10
+#define		CRCVALERR       	0x08
+#define		CRCENDERR       	0x04
+#define		CRCREQERR       	0x02
+#define		DUAL_EDGE_ERR   	0x01
+
+#define	SSTAT3          		0x0e
+#define		SCSICNT         	0xf0
+#define		U2OFFCNT        	0x7f
+#define		OFFCNT          	0x0f
+
+#define	SCSIID_ULTRA2   		0x0f
+
+#define	SIMODE0         		0x10
+#define		ENSELDO         	0x40
+#define		ENSELDI         	0x20
+#define		ENSELINGO       	0x10
+#define		ENIOERR         	0x08
+#define		ENSWRAP         	0x08
+#define		ENSDONE         	0x04
+#define		ENSPIORDY       	0x02
+#define		ENDMADONE       	0x01
+
+#define	SIMODE1         		0x11
+#define		ENSELTIMO       	0x80
+#define		ENATNTARG       	0x40
+#define		ENSCSIRST       	0x20
+#define		ENPHASEMIS      	0x10
+#define		ENBUSFREE       	0x08
+#define		ENSCSIPERR      	0x04
+#define		ENPHASECHG      	0x02
+#define		ENREQINIT       	0x01
+
+#define	SCSIBUSL        		0x12
+
+#define	SCSIBUSH        		0x13
+
+#define	SXFRCTL2        		0x13
+#define		ASYNC_SETUP     	0x07
+#define		AUTORSTDIS      	0x10
+#define		CMDDMAEN        	0x08
+
+#define	SHADDR          		0x14
+
+#define	SELTIMER        		0x18
+#define	TARGIDIN        		0x18
+#define		STAGE6          	0x20
+#define		STAGE5          	0x10
+#define		STAGE4          	0x08
+#define		STAGE3          	0x04
+#define		STAGE2          	0x02
+#define		STAGE1          	0x01
+
+#define	SELID           		0x19
+#define		SELID_MASK      	0xf0
+#define		ONEBIT          	0x08
+
+#define	SCAMCTL         		0x1a
+#define		SCAMLVL         	0x03
+#define		ENSCAMSELO      	0x80
+#define		CLRSCAMSELID    	0x40
+#define		ALTSTIM         	0x20
+#define		DFLTTID         	0x10
+
+#define	TARGID          		0x1b
+
+#define	SPIOCAP         		0x1b
+#define		SOFT1           	0x80
+#define		SOFT0           	0x40
+#define		SOFTCMDEN       	0x20
+#define		EXT_BRDCTL      	0x10
+#define		SEEPROM         	0x08
+#define		EEPROM          	0x04
+#define		ROM             	0x02
+#define		SSPIOCPS        	0x01
+
+#define	BRDCTL          		0x1d
+#define		BRDDAT7         	0x80
+#define		BRDDAT6         	0x40
+#define		BRDDAT5         	0x20
+#define		BRDDAT4         	0x10
+#define		BRDSTB          	0x10
+#define		BRDDAT3         	0x08
+#define		BRDCS           	0x08
+#define		BRDDAT2         	0x04
+#define		BRDRW           	0x04
+#define		BRDRW_ULTRA2    	0x02
+#define		BRDCTL1         	0x02
+#define		BRDCTL0         	0x01
+#define		BRDSTB_ULTRA2   	0x01
+
+#define	SEECTL          		0x1e
+#define		EXTARBACK       	0x80
+#define		EXTARBREQ       	0x40
+#define		SEEMS           	0x20
+#define		SEERDY          	0x10
+#define		SEECS           	0x08
+#define		SEECK           	0x04
+#define		SEEDO           	0x02
+#define		SEEDI           	0x01
+
+#define	SBLKCTL         		0x1f
+#define		DIAGLEDEN       	0x80
+#define		DIAGLEDON       	0x40
+#define		AUTOFLUSHDIS    	0x20
+#define		ENAB40          	0x08
+#define		SELBUSB         	0x08
+#define		ENAB20          	0x04
+#define		SELWIDE         	0x02
+#define		XCVR            	0x01
+
+#define	BUSY_TARGETS    		0x20
+#define	TARG_SCSIRATE   		0x20
+
+#define	ULTRA_ENB       		0x30
+#define	CMDSIZE_TABLE   		0x30
+
+#define	DISC_DSB        		0x32
+
+#define	CMDSIZE_TABLE_TAIL		0x34
+
+#define	MWI_RESIDUAL    		0x38
+
+#define	NEXT_QUEUED_SCB 		0x39
+
+#define	MSG_OUT         		0x3a
+
+#define	DMAPARAMS       		0x3b
+#define		PRELOADEN       	0x80
+#define		WIDEODD         	0x40
+#define		SCSIEN          	0x20
+#define		SDMAEN          	0x10
+#define		SDMAENACK       	0x10
+#define		HDMAEN          	0x08
+#define		HDMAENACK       	0x08
+#define		DIRECTION       	0x04
+#define		FIFOFLUSH       	0x02
+#define		FIFORESET       	0x01
+
+#define	SEQ_FLAGS       		0x3c
+#define		NOT_IDENTIFIED  	0x80
+#define		NO_CDB_SENT     	0x40
+#define		TARGET_CMD_IS_TAGGED	0x40
+#define		DPHASE          	0x20
+#define		TARG_CMD_PENDING	0x10
+#define		CMDPHASE_PENDING	0x08
+#define		DPHASE_PENDING  	0x04
+#define		SPHASE_PENDING  	0x02
+#define		NO_DISCONNECT   	0x01
+
+#define	SAVED_SCSIID    		0x3d
+
+#define	SAVED_LUN       		0x3e
+
+#define	LASTPHASE       		0x3f
+#define		P_MESGIN        	0xe0
+#define		PHASE_MASK      	0xe0
+#define		P_STATUS        	0xc0
+#define		P_MESGOUT       	0xa0
+#define		P_COMMAND       	0x80
+#define		P_DATAIN        	0x40
+#define		P_BUSFREE       	0x01
+#define		P_DATAOUT       	0x00
+#define		CDI             	0x80
+#define		IOI             	0x40
+#define		MSGI            	0x20
+
+#define	WAITING_SCBH    		0x40
+
+#define	DISCONNECTED_SCBH		0x41
+
+#define	FREE_SCBH       		0x42
+
+#define	COMPLETE_SCBH   		0x43
+
+#define	HSCB_ADDR       		0x44
+
+#define	SHARED_DATA_ADDR		0x48
+
+#define	KERNEL_QINPOS   		0x4c
+
+#define	QINPOS          		0x4d
+
+#define	QOUTPOS         		0x4e
+
+#define	KERNEL_TQINPOS  		0x4f
+
+#define	TQINPOS         		0x50
+
+#define	ARG_1           		0x51
+#define	RETURN_1        		0x51
+#define		SEND_MSG        	0x80
+#define		SEND_SENSE      	0x40
+#define		SEND_REJ        	0x20
+#define		MSGOUT_PHASEMIS 	0x10
+#define		EXIT_MSG_LOOP   	0x08
+#define		CONT_MSG_LOOP   	0x04
+#define		CONT_TARG_SESSION	0x02
+
+#define	ARG_2           		0x52
+#define	RETURN_2        		0x52
+
+#define	LAST_MSG        		0x53
+#define	TARG_IMMEDIATE_SCB		0x53
+
+#define	SCSISEQ_TEMPLATE		0x54
+#define		ENSELO          	0x40
+#define		ENSELI          	0x20
+#define		ENRSELI         	0x10
+#define		ENAUTOATNO      	0x08
+#define		ENAUTOATNI      	0x04
+#define		ENAUTOATNP      	0x02
+
+#define	HA_274_BIOSGLOBAL		0x56
+#define	INITIATOR_TAG   		0x56
+#define		HA_274_EXTENDED_TRANS	0x01
+
+#define	SEQ_FLAGS2      		0x57
+#define		TARGET_MSG_PENDING	0x02
+#define		SCB_DMA         	0x01
+
+#define	SCSICONF        		0x5a
+#define		HWSCSIID        	0x0f
+#define		HSCSIID         	0x07
+#define		TERM_ENB        	0x80
+#define		RESET_SCSI      	0x40
+#define		ENSPCHK         	0x20
+
+#define	INTDEF          		0x5c
+#define		VECTOR          	0x0f
+#define		EDGE_TRIG       	0x80
+
+#define	HOSTCONF        		0x5d
+
+#define	HA_274_BIOSCTRL 		0x5f
+#define		BIOSDISABLED    	0x30
+#define		BIOSMODE        	0x30
+#define		CHANNEL_B_PRIMARY	0x08
+
+#define	SEQCTL          		0x60
+#define		PERRORDIS       	0x80
+#define		PAUSEDIS        	0x40
+#define		FAILDIS         	0x20
+#define		FASTMODE        	0x10
+#define		BRKADRINTEN     	0x08
+#define		STEP            	0x04
+#define		SEQRESET        	0x02
+#define		LOADRAM         	0x01
+
+#define	SEQRAM          		0x61
+
+#define	SEQADDR0        		0x62
+
+#define	SEQADDR1        		0x63
+#define		SEQADDR1_MASK   	0x01
+
+#define	ACCUM           		0x64
+
+#define	SINDEX          		0x65
+
+#define	DINDEX          		0x66
+
+#define	ALLONES         		0x69
+
+#define	ALLZEROS        		0x6a
+
+#define	NONE            		0x6a
+
+#define	FLAGS           		0x6b
+#define		ZERO            	0x02
+#define		CARRY           	0x01
+
+#define	SINDIR          		0x6c
+
+#define	DINDIR          		0x6d
+
+#define	FUNCTION1       		0x6e
+
+#define	STACK           		0x6f
+
+#define	TARG_OFFSET     		0x70
+
+#define	SRAM_BASE       		0x70
+
+#define	BCTL            		0x84
+#define		ACE             	0x08
+#define		ENABLE          	0x01
+
+#define	DSCOMMAND0      		0x84
+#define		CACHETHEN       	0x80
+#define		DPARCKEN        	0x40
+#define		MPARCKEN        	0x20
+#define		EXTREQLCK       	0x10
+#define		INTSCBRAMSEL    	0x08
+#define		RAMPS           	0x04
+#define		USCBSIZE32      	0x02
+#define		CIOPARCKEN      	0x01
+
+#define	BUSTIME         		0x85
+#define		BOFF            	0xf0
+#define		BON             	0x0f
+
+#define	DSCOMMAND1      		0x85
+#define		DSLATT          	0xfc
+#define		HADDLDSEL1      	0x02
+#define		HADDLDSEL0      	0x01
+
+#define	BUSSPD          		0x86
+#define		DFTHRSH         	0xc0
+#define		DFTHRSH_75      	0x80
+#define		STBOFF          	0x38
+#define		STBON           	0x07
+
+#define	HS_MAILBOX      		0x86
+#define		HOST_MAILBOX    	0xf0
+#define		HOST_TQINPOS    	0x80
+#define		SEQ_MAILBOX     	0x0f
+
+#define	DSPCISTATUS     		0x86
+#define		DFTHRSH_100     	0xc0
+
+#define	HCNTRL          		0x87
+#define		POWRDN          	0x40
+#define		SWINT           	0x10
+#define		IRQMS           	0x08
+#define		PAUSE           	0x04
+#define		INTEN           	0x02
+#define		CHIPRST         	0x01
+#define		CHIPRSTACK      	0x01
+
+#define	HADDR           		0x88
+
+#define	HCNT            		0x8c
+
+#define	SCBPTR          		0x90
+
+#define	INTSTAT         		0x91
+#define		SEQINT_MASK     	0xf1
+#define		OUT_OF_RANGE    	0xe1
+#define		NO_FREE_SCB     	0xd1
+#define		SCB_MISMATCH    	0xc1
+#define		MISSED_BUSFREE  	0xb1
+#define		MKMSG_FAILED    	0xa1
+#define		DATA_OVERRUN    	0x91
+#define		PERR_DETECTED   	0x81
+#define		BAD_STATUS      	0x71
+#define		HOST_MSG_LOOP   	0x61
+#define		PDATA_REINIT    	0x51
+#define		IGN_WIDE_RES    	0x41
+#define		NO_MATCH        	0x31
+#define		PROTO_VIOLATION 	0x21
+#define		SEND_REJECT     	0x11
+#define		INT_PEND        	0x0f
+#define		BAD_PHASE       	0x01
+#define		BRKADRINT       	0x08
+#define		SCSIINT         	0x04
+#define		CMDCMPLT        	0x02
+#define		SEQINT          	0x01
+
+#define	CLRINT          		0x92
+#define		CLRPARERR       	0x10
+#define		CLRBRKADRINT    	0x08
+#define		CLRSCSIINT      	0x04
+#define		CLRCMDINT       	0x02
+#define		CLRSEQINT       	0x01
+
+#define	ERROR           		0x92
+#define		CIOPARERR       	0x80
+#define		PCIERRSTAT      	0x40
+#define		MPARERR         	0x20
+#define		DPARERR         	0x10
+#define		SQPARERR        	0x08
+#define		ILLOPCODE       	0x04
+#define		ILLSADDR        	0x02
+#define		ILLHADDR        	0x01
+
+#define	DFCNTRL         		0x93
+
+#define	DFSTATUS        		0x94
+#define		PRELOAD_AVAIL   	0x80
+#define		DFCACHETH       	0x40
+#define		FIFOQWDEMP      	0x20
+#define		MREQPEND        	0x10
+#define		HDONE           	0x08
+#define		DFTHRESH        	0x04
+#define		FIFOFULL        	0x02
+#define		FIFOEMP         	0x01
+
+#define	DFWADDR         		0x95
+
+#define	DFRADDR         		0x97
+
+#define	DFDAT           		0x99
+
+#define	SCBCNT          		0x9a
+#define		SCBCNT_MASK     	0x1f
+#define		SCBAUTO         	0x80
+
+#define	QINFIFO         		0x9b
+
+#define	QINCNT          		0x9c
+
+#define	QOUTFIFO        		0x9d
+
+#define	CRCCONTROL1     		0x9d
+#define		CRCONSEEN       	0x80
+#define		CRCVALCHKEN     	0x40
+#define		CRCENDCHKEN     	0x20
+#define		CRCREQCHKEN     	0x10
+#define		TARGCRCENDEN    	0x08
+#define		TARGCRCCNTEN    	0x04
+
+#define	QOUTCNT         		0x9e
+
+#define	SCSIPHASE       		0x9e
+#define		DATA_PHASE_MASK 	0x03
+#define		STATUS_PHASE    	0x20
+#define		COMMAND_PHASE   	0x10
+#define		MSG_IN_PHASE    	0x08
+#define		MSG_OUT_PHASE   	0x04
+#define		DATA_IN_PHASE   	0x02
+#define		DATA_OUT_PHASE  	0x01
+
+#define	SFUNCT          		0x9f
+#define		ALT_MODE        	0x80
+
+#define	SCB_BASE        		0xa0
+
+#define	SCB_CDB_PTR     		0xa0
+#define	SCB_RESIDUAL_DATACNT		0xa0
+#define	SCB_CDB_STORE   		0xa0
+
+#define	SCB_RESIDUAL_SGPTR		0xa4
+
+#define	SCB_SCSI_STATUS 		0xa8
+
+#define	SCB_TARGET_PHASES		0xa9
+
+#define	SCB_TARGET_DATA_DIR		0xaa
+
+#define	SCB_TARGET_ITAG 		0xab
+
+#define	SCB_DATAPTR     		0xac
+
+#define	SCB_DATACNT     		0xb0
+#define		SG_HIGH_ADDR_BITS	0x7f
+#define		SG_LAST_SEG     	0x80
+
+#define	SCB_SGPTR       		0xb4
+#define		SG_RESID_VALID  	0x04
+#define		SG_FULL_RESID   	0x02
+#define		SG_LIST_NULL    	0x01
+
+#define	SCB_CONTROL     		0xb8
+#define		SCB_TAG_TYPE    	0x03
+#define		STATUS_RCVD     	0x80
+#define		TARGET_SCB      	0x80
+#define		DISCENB         	0x40
+#define		TAG_ENB         	0x20
+#define		MK_MESSAGE      	0x10
+#define		ULTRAENB        	0x08
+#define		DISCONNECTED    	0x04
+
+#define	SCB_SCSIID      		0xb9
+#define		TID             	0xf0
+#define		TWIN_TID        	0x70
+#define		OID             	0x0f
+#define		TWIN_CHNLB      	0x80
+
+#define	SCB_LUN         		0xba
+#define		LID             	0x3f
+#define		SCB_XFERLEN_ODD 	0x80
+
+#define	SCB_TAG         		0xbb
+
+#define	SCB_CDB_LEN     		0xbc
+
+#define	SCB_SCSIRATE    		0xbd
+
+#define	SCB_SCSIOFFSET  		0xbe
+
+#define	SCB_NEXT        		0xbf
+
+#define	SCB_64_SPARE    		0xc0
+
+#define	SEECTL_2840     		0xc0
+#define		CS_2840         	0x04
+#define		CK_2840         	0x02
+#define		DO_2840         	0x01
+
+#define	STATUS_2840     		0xc1
+#define		BIOS_SEL        	0x60
+#define		ADSEL           	0x1e
+#define		EEPROM_TF       	0x80
+#define		DI_2840         	0x01
+
+#define	SCB_64_BTT      		0xd0
+
+#define	CCHADDR         		0xe0
+
+#define	CCHCNT          		0xe8
+
+#define	CCSGRAM         		0xe9
+
+#define	CCSGADDR        		0xea
+
+#define	CCSGCTL         		0xeb
+#define		CCSGDONE        	0x80
+#define		CCSGEN          	0x08
+#define		SG_FETCH_NEEDED 	0x02
+#define		CCSGRESET       	0x01
+
+#define	CCSCBRAM        		0xec
+
+#define	CCSCBADDR       		0xed
+
+#define	CCSCBCTL        		0xee
+#define		CCSCBDONE       	0x80
+#define		ARRDONE         	0x40
+#define		CCARREN         	0x10
+#define		CCSCBEN         	0x08
+#define		CCSCBDIR        	0x04
+#define		CCSCBRESET      	0x01
+
+#define	CCSCBCNT        		0xef
+
+#define	SCBBADDR        		0xf0
+
+#define	CCSCBPTR        		0xf1
+
+#define	HNSCB_QOFF      		0xf4
+
+#define	SNSCB_QOFF      		0xf6
+
+#define	SDSCB_QOFF      		0xf8
+
+#define	QOFF_CTLSTA     		0xfa
+#define		SCB_QSIZE       	0x07
+#define		SCB_QSIZE_256   	0x06
+#define		SCB_AVAIL       	0x40
+#define		SNSCB_ROLLOVER  	0x20
+#define		SDSCB_ROLLOVER  	0x10
+
+#define	DFF_THRSH       		0xfb
+#define		WR_DFTHRSH      	0x70
+#define		WR_DFTHRSH_MAX  	0x70
+#define		WR_DFTHRSH_90   	0x60
+#define		WR_DFTHRSH_85   	0x50
+#define		WR_DFTHRSH_75   	0x40
+#define		WR_DFTHRSH_63   	0x30
+#define		WR_DFTHRSH_50   	0x20
+#define		WR_DFTHRSH_25   	0x10
+#define		RD_DFTHRSH      	0x07
+#define		RD_DFTHRSH_MAX  	0x07
+#define		RD_DFTHRSH_90   	0x06
+#define		RD_DFTHRSH_85   	0x05
+#define		RD_DFTHRSH_75   	0x04
+#define		RD_DFTHRSH_63   	0x03
+#define		RD_DFTHRSH_50   	0x02
+#define		RD_DFTHRSH_25   	0x01
+#define		RD_DFTHRSH_MIN  	0x00
+#define		WR_DFTHRSH_MIN  	0x00
+
+#define	SG_CACHE_SHADOW 		0xfc
+#define		SG_ADDR_MASK    	0xf8
+#define		LAST_SEG        	0x02
+#define		LAST_SEG_DONE   	0x01
+
+#define	SG_CACHE_PRE    		0xfc
+
+
+#define	MAX_OFFSET_ULTRA2	0x7f
+#define	MAX_OFFSET_16BIT	0x08
+#define	BUS_8_BIT	0x00
+#define	TARGET_CMD_CMPLT	0xfe
+#define	STATUS_QUEUE_FULL	0x28
+#define	STATUS_BUSY	0x08
+#define	MAX_OFFSET_8BIT	0x0f
+#define	BUS_32_BIT	0x02
+#define	CCSGADDR_MAX	0x80
+#define	TID_SHIFT	0x04
+#define	SCB_DOWNLOAD_SIZE_64	0x30
+#define	HOST_MAILBOX_SHIFT	0x04
+#define	CMD_GROUP_CODE_SHIFT	0x05
+#define	CCSGRAM_MAXSEGS	0x10
+#define	SCB_LIST_NULL	0xff
+#define	SG_SIZEOF	0x08
+#define	SCB_DOWNLOAD_SIZE	0x20
+#define	SEQ_MAILBOX_SHIFT	0x00
+#define	TARGET_DATA_IN	0x01
+#define	HOST_MSG	0xff
+#define	MAX_OFFSET	0x7f
+#define	BUS_16_BIT	0x01
+#define	SCB_UPLOAD_SIZE	0x20
+#define	STACK_SIZE	0x04
+
+
+/* Downloaded Constant Definitions */
+#define	INVERTED_CACHESIZE_MASK	0x03
+#define	SG_PREFETCH_ADDR_MASK	0x06
+#define	SG_PREFETCH_ALIGN_MASK	0x05
+#define	QOUTFIFO_OFFSET	0x00
+#define	SG_PREFETCH_CNT	0x04
+#define	CACHESIZE_MASK	0x02
+#define	QINFIFO_OFFSET	0x01
+#define	DOWNLOAD_CONST_COUNT	0x07
+
+
+/* Exported Labels */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped
new file mode 100644
index 0000000..9f9b880
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped
@@ -0,0 +1,413 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *		 from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#58 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#40 $
+ */
+
+#include "aic7xxx_osm.h"
+
+static const ahc_reg_parse_entry_t SCSISEQ_parse_table[] = {
+	{ "SCSIRSTO",		0x01, 0x01 },
+	{ "ENAUTOATNP",		0x02, 0x02 },
+	{ "ENAUTOATNI",		0x04, 0x04 },
+	{ "ENAUTOATNO",		0x08, 0x08 },
+	{ "ENRSELI",		0x10, 0x10 },
+	{ "ENSELI",		0x20, 0x20 },
+	{ "ENSELO",		0x40, 0x40 },
+	{ "TEMODE",		0x80, 0x80 }
+};
+
+int
+ahc_scsiseq_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SCSISEQ_parse_table, 8, "SCSISEQ",
+	    0x00, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SXFRCTL0_parse_table[] = {
+	{ "CLRCHN",		0x02, 0x02 },
+	{ "SCAMEN",		0x04, 0x04 },
+	{ "SPIOEN",		0x08, 0x08 },
+	{ "CLRSTCNT",		0x10, 0x10 },
+	{ "FAST20",		0x20, 0x20 },
+	{ "DFPEXP",		0x40, 0x40 },
+	{ "DFON",		0x80, 0x80 }
+};
+
+int
+ahc_sxfrctl0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SXFRCTL0_parse_table, 7, "SXFRCTL0",
+	    0x01, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SCSISIGI_parse_table[] = {
+	{ "ACKI",		0x01, 0x01 },
+	{ "REQI",		0x02, 0x02 },
+	{ "BSYI",		0x04, 0x04 },
+	{ "SELI",		0x08, 0x08 },
+	{ "ATNI",		0x10, 0x10 },
+	{ "MSGI",		0x20, 0x20 },
+	{ "IOI",		0x40, 0x40 },
+	{ "CDI",		0x80, 0x80 },
+	{ "P_DATAOUT",		0x00, 0x00 },
+	{ "P_DATAOUT_DT",	0x20, 0x20 },
+	{ "P_DATAIN",		0x40, 0x40 },
+	{ "P_DATAIN_DT",	0x60, 0x60 },
+	{ "P_COMMAND",		0x80, 0x80 },
+	{ "P_MESGOUT",		0xa0, 0xa0 },
+	{ "P_STATUS",		0xc0, 0xc0 },
+	{ "PHASE_MASK",		0xe0, 0xe0 },
+	{ "P_MESGIN",		0xe0, 0xe0 }
+};
+
+int
+ahc_scsisigi_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SCSISIGI_parse_table, 17, "SCSISIGI",
+	    0x03, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SCSIRATE_parse_table[] = {
+	{ "SINGLE_EDGE",	0x10, 0x10 },
+	{ "ENABLE_CRC",		0x40, 0x40 },
+	{ "WIDEXFER",		0x80, 0x80 },
+	{ "SXFR_ULTRA2",	0x0f, 0x0f },
+	{ "SOFS",		0x0f, 0x0f },
+	{ "SXFR",		0x70, 0x70 }
+};
+
+int
+ahc_scsirate_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SCSIRATE_parse_table, 6, "SCSIRATE",
+	    0x04, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SSTAT0_parse_table[] = {
+	{ "DMADONE",		0x01, 0x01 },
+	{ "SPIORDY",		0x02, 0x02 },
+	{ "SDONE",		0x04, 0x04 },
+	{ "SWRAP",		0x08, 0x08 },
+	{ "IOERR",		0x08, 0x08 },
+	{ "SELINGO",		0x10, 0x10 },
+	{ "SELDI",		0x20, 0x20 },
+	{ "SELDO",		0x40, 0x40 },
+	{ "TARGET",		0x80, 0x80 }
+};
+
+int
+ahc_sstat0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SSTAT0_parse_table, 9, "SSTAT0",
+	    0x0b, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SSTAT1_parse_table[] = {
+	{ "REQINIT",		0x01, 0x01 },
+	{ "PHASECHG",		0x02, 0x02 },
+	{ "SCSIPERR",		0x04, 0x04 },
+	{ "BUSFREE",		0x08, 0x08 },
+	{ "PHASEMIS",		0x10, 0x10 },
+	{ "SCSIRSTI",		0x20, 0x20 },
+	{ "ATNTARG",		0x40, 0x40 },
+	{ "SELTO",		0x80, 0x80 }
+};
+
+int
+ahc_sstat1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SSTAT1_parse_table, 8, "SSTAT1",
+	    0x0c, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SSTAT2_parse_table[] = {
+	{ "DUAL_EDGE_ERR",	0x01, 0x01 },
+	{ "CRCREQERR",		0x02, 0x02 },
+	{ "CRCENDERR",		0x04, 0x04 },
+	{ "CRCVALERR",		0x08, 0x08 },
+	{ "EXP_ACTIVE",		0x10, 0x10 },
+	{ "SHVALID",		0x40, 0x40 },
+	{ "OVERRUN",		0x80, 0x80 },
+	{ "SFCNT",		0x1f, 0x1f }
+};
+
+int
+ahc_sstat2_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SSTAT2_parse_table, 8, "SSTAT2",
+	    0x0d, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SSTAT3_parse_table[] = {
+	{ "OFFCNT",		0x0f, 0x0f },
+	{ "U2OFFCNT",		0x7f, 0x7f },
+	{ "SCSICNT",		0xf0, 0xf0 }
+};
+
+int
+ahc_sstat3_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SSTAT3_parse_table, 3, "SSTAT3",
+	    0x0e, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SIMODE0_parse_table[] = {
+	{ "ENDMADONE",		0x01, 0x01 },
+	{ "ENSPIORDY",		0x02, 0x02 },
+	{ "ENSDONE",		0x04, 0x04 },
+	{ "ENSWRAP",		0x08, 0x08 },
+	{ "ENIOERR",		0x08, 0x08 },
+	{ "ENSELINGO",		0x10, 0x10 },
+	{ "ENSELDI",		0x20, 0x20 },
+	{ "ENSELDO",		0x40, 0x40 }
+};
+
+int
+ahc_simode0_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SIMODE0_parse_table, 8, "SIMODE0",
+	    0x10, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SIMODE1_parse_table[] = {
+	{ "ENREQINIT",		0x01, 0x01 },
+	{ "ENPHASECHG",		0x02, 0x02 },
+	{ "ENSCSIPERR",		0x04, 0x04 },
+	{ "ENBUSFREE",		0x08, 0x08 },
+	{ "ENPHASEMIS",		0x10, 0x10 },
+	{ "ENSCSIRST",		0x20, 0x20 },
+	{ "ENATNTARG",		0x40, 0x40 },
+	{ "ENSELTIMO",		0x80, 0x80 }
+};
+
+int
+ahc_simode1_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SIMODE1_parse_table, 8, "SIMODE1",
+	    0x11, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scsibusl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(NULL, 0, "SCSIBUSL",
+	    0x12, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SBLKCTL_parse_table[] = {
+	{ "XCVR",		0x01, 0x01 },
+	{ "SELWIDE",		0x02, 0x02 },
+	{ "ENAB20",		0x04, 0x04 },
+	{ "SELBUSB",		0x08, 0x08 },
+	{ "ENAB40",		0x08, 0x08 },
+	{ "AUTOFLUSHDIS",	0x20, 0x20 },
+	{ "DIAGLEDON",		0x40, 0x40 },
+	{ "DIAGLEDEN",		0x80, 0x80 }
+};
+
+int
+ahc_sblkctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SBLKCTL_parse_table, 8, "SBLKCTL",
+	    0x1f, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SEQ_FLAGS_parse_table[] = {
+	{ "NO_DISCONNECT",	0x01, 0x01 },
+	{ "SPHASE_PENDING",	0x02, 0x02 },
+	{ "DPHASE_PENDING",	0x04, 0x04 },
+	{ "CMDPHASE_PENDING",	0x08, 0x08 },
+	{ "TARG_CMD_PENDING",	0x10, 0x10 },
+	{ "DPHASE",		0x20, 0x20 },
+	{ "NO_CDB_SENT",	0x40, 0x40 },
+	{ "TARGET_CMD_IS_TAGGED",0x40, 0x40 },
+	{ "NOT_IDENTIFIED",	0x80, 0x80 }
+};
+
+int
+ahc_seq_flags_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SEQ_FLAGS_parse_table, 9, "SEQ_FLAGS",
+	    0x3c, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t LASTPHASE_parse_table[] = {
+	{ "MSGI",		0x20, 0x20 },
+	{ "IOI",		0x40, 0x40 },
+	{ "CDI",		0x80, 0x80 },
+	{ "P_DATAOUT",		0x00, 0x00 },
+	{ "P_BUSFREE",		0x01, 0x01 },
+	{ "P_DATAIN",		0x40, 0x40 },
+	{ "P_COMMAND",		0x80, 0x80 },
+	{ "P_MESGOUT",		0xa0, 0xa0 },
+	{ "P_STATUS",		0xc0, 0xc0 },
+	{ "PHASE_MASK",		0xe0, 0xe0 },
+	{ "P_MESGIN",		0xe0, 0xe0 }
+};
+
+int
+ahc_lastphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(LASTPHASE_parse_table, 11, "LASTPHASE",
+	    0x3f, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SEQCTL_parse_table[] = {
+	{ "LOADRAM",		0x01, 0x01 },
+	{ "SEQRESET",		0x02, 0x02 },
+	{ "STEP",		0x04, 0x04 },
+	{ "BRKADRINTEN",	0x08, 0x08 },
+	{ "FASTMODE",		0x10, 0x10 },
+	{ "FAILDIS",		0x20, 0x20 },
+	{ "PAUSEDIS",		0x40, 0x40 },
+	{ "PERRORDIS",		0x80, 0x80 }
+};
+
+int
+ahc_seqctl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SEQCTL_parse_table, 8, "SEQCTL",
+	    0x60, regvalue, cur_col, wrap));
+}
+
+int
+ahc_sram_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(NULL, 0, "SRAM_BASE",
+	    0x70, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t ERROR_parse_table[] = {
+	{ "ILLHADDR",		0x01, 0x01 },
+	{ "ILLSADDR",		0x02, 0x02 },
+	{ "ILLOPCODE",		0x04, 0x04 },
+	{ "SQPARERR",		0x08, 0x08 },
+	{ "DPARERR",		0x10, 0x10 },
+	{ "MPARERR",		0x20, 0x20 },
+	{ "PCIERRSTAT",		0x40, 0x40 },
+	{ "CIOPARERR",		0x80, 0x80 }
+};
+
+int
+ahc_error_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(ERROR_parse_table, 8, "ERROR",
+	    0x92, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t DFCNTRL_parse_table[] = {
+	{ "FIFORESET",		0x01, 0x01 },
+	{ "FIFOFLUSH",		0x02, 0x02 },
+	{ "DIRECTION",		0x04, 0x04 },
+	{ "HDMAEN",		0x08, 0x08 },
+	{ "HDMAENACK",		0x08, 0x08 },
+	{ "SDMAEN",		0x10, 0x10 },
+	{ "SDMAENACK",		0x10, 0x10 },
+	{ "SCSIEN",		0x20, 0x20 },
+	{ "WIDEODD",		0x40, 0x40 },
+	{ "PRELOADEN",		0x80, 0x80 }
+};
+
+int
+ahc_dfcntrl_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(DFCNTRL_parse_table, 10, "DFCNTRL",
+	    0x93, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t DFSTATUS_parse_table[] = {
+	{ "FIFOEMP",		0x01, 0x01 },
+	{ "FIFOFULL",		0x02, 0x02 },
+	{ "DFTHRESH",		0x04, 0x04 },
+	{ "HDONE",		0x08, 0x08 },
+	{ "MREQPEND",		0x10, 0x10 },
+	{ "FIFOQWDEMP",		0x20, 0x20 },
+	{ "DFCACHETH",		0x40, 0x40 },
+	{ "PRELOAD_AVAIL",	0x80, 0x80 }
+};
+
+int
+ahc_dfstatus_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(DFSTATUS_parse_table, 8, "DFSTATUS",
+	    0x94, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SCSIPHASE_parse_table[] = {
+	{ "DATA_OUT_PHASE",	0x01, 0x01 },
+	{ "DATA_IN_PHASE",	0x02, 0x02 },
+	{ "MSG_OUT_PHASE",	0x04, 0x04 },
+	{ "MSG_IN_PHASE",	0x08, 0x08 },
+	{ "COMMAND_PHASE",	0x10, 0x10 },
+	{ "STATUS_PHASE",	0x20, 0x20 },
+	{ "DATA_PHASE_MASK",	0x03, 0x03 }
+};
+
+int
+ahc_scsiphase_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SCSIPHASE_parse_table, 7, "SCSIPHASE",
+	    0x9e, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_base_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(NULL, 0, "SCB_BASE",
+	    0xa0, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SCB_CONTROL_parse_table[] = {
+	{ "DISCONNECTED",	0x04, 0x04 },
+	{ "ULTRAENB",		0x08, 0x08 },
+	{ "MK_MESSAGE",		0x10, 0x10 },
+	{ "TAG_ENB",		0x20, 0x20 },
+	{ "DISCENB",		0x40, 0x40 },
+	{ "TARGET_SCB",		0x80, 0x80 },
+	{ "STATUS_RCVD",	0x80, 0x80 },
+	{ "SCB_TAG_TYPE",	0x03, 0x03 }
+};
+
+int
+ahc_scb_control_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SCB_CONTROL_parse_table, 8, "SCB_CONTROL",
+	    0xb8, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SCB_SCSIID_parse_table[] = {
+	{ "TWIN_CHNLB",		0x80, 0x80 },
+	{ "OID",		0x0f, 0x0f },
+	{ "TWIN_TID",		0x70, 0x70 },
+	{ "TID",		0xf0, 0xf0 }
+};
+
+int
+ahc_scb_scsiid_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SCB_SCSIID_parse_table, 4, "SCB_SCSIID",
+	    0xb9, regvalue, cur_col, wrap));
+}
+
+static const ahc_reg_parse_entry_t SCB_LUN_parse_table[] = {
+	{ "SCB_XFERLEN_ODD",	0x80, 0x80 },
+	{ "LID",		0x3f, 0x3f }
+};
+
+int
+ahc_scb_lun_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(SCB_LUN_parse_table, 2, "SCB_LUN",
+	    0xba, regvalue, cur_col, wrap));
+}
+
+int
+ahc_scb_tag_print(u_int regvalue, u_int *cur_col, u_int wrap)
+{
+	return (ahc_print_register(NULL, 0, "SCB_TAG",
+	    0xbb, regvalue, cur_col, wrap));
+}
+
diff --git a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
new file mode 100644
index 0000000..07e93fb
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
@@ -0,0 +1,1308 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *		 from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#58 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#40 $
+ */
+static const uint8_t seqprog[] = {
+	0xb2, 0x00, 0x00, 0x08,
+	0xf7, 0x11, 0x22, 0x08,
+	0x00, 0x65, 0xee, 0x59,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x6a, 0x24, 0x08,
+	0x40, 0x00, 0x40, 0x68,
+	0x08, 0x1f, 0x3e, 0x10,
+	0x40, 0x00, 0x40, 0x68,
+	0xff, 0x40, 0x3c, 0x60,
+	0x08, 0x1f, 0x3e, 0x10,
+	0x60, 0x0b, 0x42, 0x68,
+	0x40, 0xfa, 0x12, 0x78,
+	0x01, 0x4d, 0xc8, 0x30,
+	0x00, 0x4c, 0x12, 0x70,
+	0x01, 0x39, 0xa2, 0x30,
+	0x00, 0x6a, 0xc2, 0x5e,
+	0x01, 0x51, 0x20, 0x31,
+	0x01, 0x57, 0xae, 0x00,
+	0x0d, 0x6a, 0x76, 0x00,
+	0x00, 0x51, 0x14, 0x5e,
+	0x01, 0x51, 0xc8, 0x30,
+	0x00, 0x39, 0xc8, 0x60,
+	0x00, 0xbb, 0x30, 0x70,
+	0xc1, 0x6a, 0xda, 0x5e,
+	0x01, 0xbf, 0x72, 0x30,
+	0x01, 0x40, 0x7e, 0x31,
+	0x01, 0x90, 0x80, 0x30,
+	0x01, 0xf6, 0xd4, 0x30,
+	0x01, 0x4d, 0x9a, 0x18,
+	0xfe, 0x57, 0xae, 0x08,
+	0x01, 0x40, 0x20, 0x31,
+	0x00, 0x65, 0xcc, 0x58,
+	0x60, 0x0b, 0x40, 0x78,
+	0x08, 0x6a, 0x18, 0x00,
+	0x08, 0x11, 0x22, 0x00,
+	0x60, 0x0b, 0x00, 0x78,
+	0x40, 0x0b, 0xfa, 0x68,
+	0x80, 0x0b, 0xb6, 0x78,
+	0x20, 0x6a, 0x16, 0x00,
+	0xa4, 0x6a, 0x06, 0x00,
+	0x08, 0x6a, 0x78, 0x00,
+	0x01, 0x50, 0xc8, 0x30,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x48, 0x6a, 0xfe, 0x5d,
+	0x01, 0x6a, 0xdc, 0x01,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x48, 0x6a, 0xfe, 0x5d,
+	0x01, 0x6a, 0x26, 0x01,
+	0xf0, 0x19, 0x7a, 0x08,
+	0x0f, 0x18, 0xc8, 0x08,
+	0x0f, 0x0f, 0xc8, 0x08,
+	0x0f, 0x05, 0xc8, 0x08,
+	0x00, 0x3d, 0x7a, 0x00,
+	0x08, 0x1f, 0x6e, 0x78,
+	0x80, 0x3d, 0x7a, 0x00,
+	0x01, 0x3d, 0xd8, 0x31,
+	0x01, 0x3d, 0x32, 0x31,
+	0x10, 0x03, 0x4e, 0x79,
+	0x00, 0x65, 0xf2, 0x58,
+	0x80, 0x66, 0xae, 0x78,
+	0x01, 0x66, 0xd8, 0x31,
+	0x01, 0x66, 0x32, 0x31,
+	0x3f, 0x66, 0x7c, 0x08,
+	0x40, 0x66, 0x82, 0x68,
+	0x01, 0x3c, 0x78, 0x00,
+	0x10, 0x03, 0x9e, 0x78,
+	0x00, 0x65, 0xf2, 0x58,
+	0xe0, 0x66, 0xc8, 0x18,
+	0x00, 0x65, 0xaa, 0x50,
+	0xdd, 0x66, 0xc8, 0x18,
+	0x00, 0x65, 0xaa, 0x48,
+	0x01, 0x66, 0xd8, 0x31,
+	0x01, 0x66, 0x32, 0x31,
+	0x10, 0x03, 0x4e, 0x79,
+	0x00, 0x65, 0xf2, 0x58,
+	0x01, 0x66, 0xd8, 0x31,
+	0x01, 0x66, 0x32, 0x31,
+	0x01, 0x66, 0xac, 0x30,
+	0x40, 0x3c, 0x78, 0x00,
+	0xff, 0x6a, 0xd8, 0x01,
+	0xff, 0x6a, 0x32, 0x01,
+	0x10, 0x3c, 0x78, 0x00,
+	0x02, 0x57, 0x40, 0x69,
+	0x10, 0x03, 0x3e, 0x69,
+	0x00, 0x65, 0x20, 0x41,
+	0x02, 0x57, 0xae, 0x00,
+	0x00, 0x65, 0x9e, 0x40,
+	0x61, 0x6a, 0xda, 0x5e,
+	0x08, 0x51, 0x20, 0x71,
+	0x02, 0x0b, 0xb2, 0x78,
+	0x00, 0x65, 0xae, 0x40,
+	0x1a, 0x01, 0x02, 0x00,
+	0xf0, 0x19, 0x7a, 0x08,
+	0x0f, 0x0f, 0xc8, 0x08,
+	0x0f, 0x05, 0xc8, 0x08,
+	0x00, 0x3d, 0x7a, 0x00,
+	0x08, 0x1f, 0xc4, 0x78,
+	0x80, 0x3d, 0x7a, 0x00,
+	0x20, 0x6a, 0x16, 0x00,
+	0x00, 0x65, 0xcc, 0x41,
+	0x00, 0x65, 0xb4, 0x5e,
+	0x00, 0x65, 0x12, 0x40,
+	0x20, 0x11, 0xd2, 0x68,
+	0x20, 0x6a, 0x18, 0x00,
+	0x20, 0x11, 0x22, 0x00,
+	0xf7, 0x1f, 0xca, 0x08,
+	0x80, 0xb9, 0xd8, 0x78,
+	0x08, 0x65, 0xca, 0x00,
+	0x01, 0x65, 0x3e, 0x30,
+	0x01, 0xb9, 0x1e, 0x30,
+	0x7f, 0xb9, 0x0a, 0x08,
+	0x01, 0xb9, 0x0a, 0x30,
+	0x01, 0x54, 0xca, 0x30,
+	0x80, 0xb8, 0xe6, 0x78,
+	0x80, 0x65, 0xca, 0x00,
+	0x01, 0x65, 0x00, 0x34,
+	0x01, 0x54, 0x00, 0x34,
+	0x08, 0xb8, 0xee, 0x78,
+	0x20, 0x01, 0x02, 0x00,
+	0x02, 0xbd, 0x08, 0x34,
+	0x01, 0xbd, 0x08, 0x34,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0xf4, 0x78,
+	0xf7, 0x01, 0x02, 0x08,
+	0x01, 0x06, 0xcc, 0x34,
+	0xb2, 0x00, 0x00, 0x08,
+	0x01, 0x40, 0x20, 0x31,
+	0x01, 0xbf, 0x80, 0x30,
+	0x01, 0xb9, 0x7a, 0x30,
+	0x3f, 0xba, 0x7c, 0x08,
+	0x00, 0x65, 0xea, 0x58,
+	0x80, 0x0b, 0xc4, 0x79,
+	0x12, 0x01, 0x02, 0x00,
+	0x01, 0xab, 0xac, 0x30,
+	0xe4, 0x6a, 0x70, 0x5d,
+	0x40, 0x6a, 0x16, 0x00,
+	0x80, 0x3e, 0x86, 0x5d,
+	0x20, 0xb8, 0x18, 0x79,
+	0x20, 0x6a, 0x86, 0x5d,
+	0x00, 0xab, 0x86, 0x5d,
+	0x01, 0xa9, 0x78, 0x30,
+	0x10, 0xb8, 0x20, 0x79,
+	0xe4, 0x6a, 0x70, 0x5d,
+	0x00, 0x65, 0xae, 0x40,
+	0x10, 0x03, 0x3c, 0x69,
+	0x08, 0x3c, 0x5a, 0x69,
+	0x04, 0x3c, 0x92, 0x69,
+	0x02, 0x3c, 0x98, 0x69,
+	0x01, 0x3c, 0x44, 0x79,
+	0xff, 0x6a, 0xa6, 0x00,
+	0x00, 0x65, 0xa4, 0x59,
+	0x00, 0x6a, 0xc2, 0x5e,
+	0xff, 0x53, 0x30, 0x71,
+	0x0d, 0x6a, 0x76, 0x00,
+	0x00, 0x53, 0x14, 0x5e,
+	0x00, 0x65, 0xea, 0x58,
+	0x12, 0x01, 0x02, 0x00,
+	0x00, 0x65, 0x18, 0x41,
+	0xa4, 0x6a, 0x06, 0x00,
+	0x00, 0x65, 0xf2, 0x58,
+	0xfd, 0x57, 0xae, 0x08,
+	0x00, 0x65, 0xae, 0x40,
+	0xe4, 0x6a, 0x70, 0x5d,
+	0x20, 0x3c, 0x4a, 0x79,
+	0x02, 0x6a, 0x86, 0x5d,
+	0x04, 0x6a, 0x86, 0x5d,
+	0x01, 0x03, 0x4c, 0x69,
+	0xf7, 0x11, 0x22, 0x08,
+	0xff, 0x6a, 0x24, 0x08,
+	0xff, 0x6a, 0x06, 0x08,
+	0x01, 0x6a, 0x7e, 0x00,
+	0x00, 0x65, 0xa4, 0x59,
+	0x00, 0x65, 0x04, 0x40,
+	0x80, 0x86, 0xc8, 0x08,
+	0x01, 0x4f, 0xc8, 0x30,
+	0x00, 0x50, 0x6c, 0x61,
+	0xc4, 0x6a, 0x70, 0x5d,
+	0x40, 0x3c, 0x68, 0x79,
+	0x28, 0x6a, 0x86, 0x5d,
+	0x00, 0x65, 0x4c, 0x41,
+	0x08, 0x6a, 0x86, 0x5d,
+	0x00, 0x65, 0x4c, 0x41,
+	0x84, 0x6a, 0x70, 0x5d,
+	0x00, 0x65, 0xf2, 0x58,
+	0x01, 0x66, 0xc8, 0x30,
+	0x01, 0x64, 0xd8, 0x31,
+	0x01, 0x64, 0x32, 0x31,
+	0x5b, 0x64, 0xc8, 0x28,
+	0x30, 0x64, 0xca, 0x18,
+	0x01, 0x6c, 0xc8, 0x30,
+	0xff, 0x64, 0x8e, 0x79,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0x80, 0x79,
+	0x01, 0x64, 0x86, 0x61,
+	0xf7, 0x01, 0x02, 0x08,
+	0x01, 0x06, 0xd8, 0x31,
+	0x01, 0x06, 0x32, 0x31,
+	0xff, 0x64, 0xc8, 0x18,
+	0xff, 0x64, 0x80, 0x69,
+	0xf7, 0x3c, 0x78, 0x08,
+	0x00, 0x65, 0x20, 0x41,
+	0x40, 0xaa, 0x7e, 0x10,
+	0x04, 0xaa, 0x70, 0x5d,
+	0x00, 0x65, 0x58, 0x42,
+	0xc4, 0x6a, 0x70, 0x5d,
+	0xc0, 0x6a, 0x7e, 0x00,
+	0x00, 0xa8, 0x86, 0x5d,
+	0xe4, 0x6a, 0x06, 0x00,
+	0x00, 0x6a, 0x86, 0x5d,
+	0x00, 0x65, 0x4c, 0x41,
+	0x10, 0x3c, 0xa8, 0x69,
+	0x00, 0xbb, 0x8c, 0x44,
+	0x18, 0x6a, 0xda, 0x01,
+	0x01, 0x69, 0xd8, 0x31,
+	0x1c, 0x6a, 0xd0, 0x01,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0xb0, 0x79,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x01, 0x93, 0x26, 0x01,
+	0x03, 0x6a, 0x2a, 0x01,
+	0x01, 0x69, 0x32, 0x31,
+	0x1c, 0x6a, 0xe2, 0x5d,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0xaa, 0x5e,
+	0x01, 0x50, 0xa0, 0x18,
+	0x02, 0x6a, 0x22, 0x05,
+	0x1a, 0x01, 0x02, 0x00,
+	0x80, 0x6a, 0x74, 0x00,
+	0x40, 0x6a, 0x78, 0x00,
+	0x40, 0x6a, 0x16, 0x00,
+	0x00, 0x65, 0xda, 0x5d,
+	0x01, 0x3f, 0xc8, 0x30,
+	0xbf, 0x64, 0x58, 0x7a,
+	0x80, 0x64, 0xa0, 0x73,
+	0xa0, 0x64, 0x02, 0x74,
+	0xc0, 0x64, 0xf6, 0x73,
+	0xe0, 0x64, 0x32, 0x74,
+	0x01, 0x6a, 0xda, 0x5e,
+	0x00, 0x65, 0xcc, 0x41,
+	0xf7, 0x11, 0x22, 0x08,
+	0x01, 0x06, 0xd4, 0x30,
+	0xff, 0x6a, 0x24, 0x08,
+	0xf7, 0x01, 0x02, 0x08,
+	0xc0, 0x6a, 0x78, 0x00,
+	0x09, 0x0c, 0xe8, 0x79,
+	0x08, 0x0c, 0x04, 0x68,
+	0xb1, 0x6a, 0xda, 0x5e,
+	0xff, 0x6a, 0x26, 0x09,
+	0x12, 0x01, 0x02, 0x00,
+	0x02, 0x6a, 0x08, 0x30,
+	0xff, 0x6a, 0x08, 0x08,
+	0xdf, 0x01, 0x02, 0x08,
+	0x01, 0x6a, 0x7e, 0x00,
+	0xc0, 0x6a, 0x78, 0x04,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x08, 0xa4, 0x48, 0x19,
+	0x00, 0xa5, 0x4a, 0x21,
+	0x00, 0xa6, 0x4c, 0x21,
+	0x00, 0xa7, 0x4e, 0x25,
+	0x08, 0xeb, 0xde, 0x7e,
+	0x80, 0xeb, 0x08, 0x7a,
+	0xff, 0x6a, 0xd6, 0x09,
+	0x08, 0xeb, 0x0c, 0x6a,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x80, 0xa3, 0xde, 0x6e,
+	0x88, 0xeb, 0x22, 0x72,
+	0x08, 0xeb, 0xde, 0x6e,
+	0x04, 0xea, 0x26, 0xe2,
+	0x08, 0xee, 0xde, 0x6e,
+	0x04, 0x6a, 0xd0, 0x81,
+	0x05, 0xa4, 0xc0, 0x89,
+	0x03, 0xa5, 0xc2, 0x31,
+	0x09, 0x6a, 0xd6, 0x05,
+	0x00, 0x65, 0x0a, 0x5a,
+	0x06, 0xa4, 0xd4, 0x89,
+	0x80, 0x94, 0xde, 0x7e,
+	0x07, 0xe9, 0x10, 0x31,
+	0x01, 0xe9, 0x46, 0x31,
+	0x00, 0xa3, 0xbc, 0x5e,
+	0x00, 0x65, 0xfc, 0x59,
+	0x01, 0xa4, 0xca, 0x30,
+	0x80, 0xa3, 0x36, 0x7a,
+	0x02, 0x65, 0xca, 0x00,
+	0x01, 0x65, 0xf8, 0x31,
+	0x80, 0x93, 0x26, 0x01,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x01, 0x8c, 0xc8, 0x30,
+	0x00, 0x88, 0xc8, 0x18,
+	0x02, 0x64, 0xc8, 0x88,
+	0xff, 0x64, 0xde, 0x7e,
+	0xff, 0x8d, 0x4c, 0x6a,
+	0xff, 0x8e, 0x4c, 0x6a,
+	0x03, 0x8c, 0xd4, 0x98,
+	0x00, 0x65, 0xde, 0x56,
+	0x01, 0x64, 0x70, 0x30,
+	0xff, 0x64, 0xc8, 0x10,
+	0x01, 0x64, 0xc8, 0x18,
+	0x00, 0x8c, 0x18, 0x19,
+	0xff, 0x8d, 0x1a, 0x21,
+	0xff, 0x8e, 0x1c, 0x25,
+	0xc0, 0x3c, 0x5c, 0x7a,
+	0x21, 0x6a, 0xda, 0x5e,
+	0xa8, 0x6a, 0x76, 0x00,
+	0x79, 0x6a, 0x76, 0x00,
+	0x40, 0x3f, 0x64, 0x6a,
+	0x04, 0x3b, 0x76, 0x00,
+	0x04, 0x6a, 0xd4, 0x81,
+	0x20, 0x3c, 0x6c, 0x7a,
+	0x51, 0x6a, 0xda, 0x5e,
+	0x00, 0x65, 0x84, 0x42,
+	0x20, 0x3c, 0x78, 0x00,
+	0x00, 0xb3, 0xbc, 0x5e,
+	0x07, 0xac, 0x10, 0x31,
+	0x05, 0xb3, 0x46, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xac, 0x6a, 0xf0, 0x5d,
+	0xa3, 0x6a, 0xcc, 0x00,
+	0xb3, 0x6a, 0xf4, 0x5d,
+	0x00, 0x65, 0x3c, 0x5a,
+	0xfd, 0xa4, 0x48, 0x09,
+	0x03, 0x8c, 0x10, 0x30,
+	0x00, 0x65, 0xe8, 0x5d,
+	0x01, 0xa4, 0x96, 0x7a,
+	0x04, 0x3b, 0x76, 0x08,
+	0x01, 0x3b, 0x26, 0x31,
+	0x80, 0x02, 0x04, 0x00,
+	0x10, 0x0c, 0x8c, 0x7a,
+	0x03, 0x9e, 0x8e, 0x6a,
+	0x7f, 0x02, 0x04, 0x08,
+	0x91, 0x6a, 0xda, 0x5e,
+	0x00, 0x65, 0xcc, 0x41,
+	0x01, 0xa4, 0xca, 0x30,
+	0x80, 0xa3, 0x9c, 0x7a,
+	0x02, 0x65, 0xca, 0x00,
+	0x01, 0x65, 0xf8, 0x31,
+	0x01, 0x3b, 0x26, 0x31,
+	0x00, 0x65, 0x10, 0x5a,
+	0x01, 0xfc, 0xaa, 0x6a,
+	0x80, 0x0b, 0xa0, 0x6a,
+	0x10, 0x0c, 0xa0, 0x7a,
+	0x20, 0x93, 0xa0, 0x6a,
+	0x02, 0x93, 0x26, 0x01,
+	0x02, 0xfc, 0xb4, 0x7a,
+	0x40, 0x0d, 0xc8, 0x6a,
+	0x01, 0xa4, 0x48, 0x01,
+	0x00, 0x65, 0xc8, 0x42,
+	0x40, 0x0d, 0xba, 0x6a,
+	0x00, 0x65, 0x10, 0x5a,
+	0x00, 0x65, 0xac, 0x42,
+	0x80, 0xfc, 0xc4, 0x7a,
+	0x80, 0xa4, 0xc4, 0x6a,
+	0xff, 0xa5, 0x4a, 0x19,
+	0xff, 0xa6, 0x4c, 0x21,
+	0xff, 0xa7, 0x4e, 0x21,
+	0xf8, 0xfc, 0x48, 0x09,
+	0x7f, 0xa3, 0x46, 0x09,
+	0x04, 0x3b, 0xe4, 0x6a,
+	0x02, 0x93, 0x26, 0x01,
+	0x01, 0x94, 0xca, 0x7a,
+	0x01, 0x94, 0xca, 0x7a,
+	0x01, 0x94, 0xca, 0x7a,
+	0x01, 0x94, 0xca, 0x7a,
+	0x01, 0x94, 0xca, 0x7a,
+	0x01, 0xa4, 0xe2, 0x7a,
+	0x01, 0xfc, 0xd8, 0x7a,
+	0x01, 0x94, 0xe4, 0x6a,
+	0x01, 0x94, 0xe4, 0x6a,
+	0x01, 0x94, 0xe4, 0x6a,
+	0x00, 0x65, 0x84, 0x42,
+	0x01, 0x94, 0xe2, 0x7a,
+	0x10, 0x94, 0xe4, 0x6a,
+	0xd7, 0x93, 0x26, 0x09,
+	0x28, 0x93, 0xe8, 0x6a,
+	0x01, 0x85, 0x0a, 0x01,
+	0x02, 0xfc, 0xf0, 0x6a,
+	0x01, 0x14, 0x46, 0x31,
+	0xff, 0x6a, 0x10, 0x09,
+	0xfe, 0x85, 0x0a, 0x09,
+	0xff, 0x38, 0xfe, 0x6a,
+	0x80, 0xa3, 0xfe, 0x7a,
+	0x80, 0x0b, 0xfc, 0x7a,
+	0x04, 0x3b, 0xfe, 0x7a,
+	0xbf, 0x3b, 0x76, 0x08,
+	0x01, 0x3b, 0x26, 0x31,
+	0x00, 0x65, 0x10, 0x5a,
+	0x01, 0x0b, 0x0c, 0x6b,
+	0x10, 0x0c, 0x00, 0x7b,
+	0x04, 0x93, 0x0a, 0x6b,
+	0x01, 0x94, 0x08, 0x7b,
+	0x10, 0x94, 0x0a, 0x6b,
+	0xc7, 0x93, 0x26, 0x09,
+	0x01, 0x99, 0xd4, 0x30,
+	0x38, 0x93, 0x0e, 0x6b,
+	0xff, 0x08, 0x5c, 0x6b,
+	0xff, 0x09, 0x5c, 0x6b,
+	0xff, 0x0a, 0x5c, 0x6b,
+	0xff, 0x38, 0x2a, 0x7b,
+	0x04, 0x14, 0x10, 0x31,
+	0x01, 0x38, 0x18, 0x31,
+	0x02, 0x6a, 0x1a, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x14, 0x6a, 0xf6, 0x5d,
+	0x00, 0x38, 0xe2, 0x5d,
+	0xff, 0x6a, 0x70, 0x08,
+	0x00, 0x65, 0x56, 0x43,
+	0x80, 0xa3, 0x30, 0x7b,
+	0x01, 0xa4, 0x48, 0x01,
+	0x00, 0x65, 0x5c, 0x43,
+	0x08, 0xeb, 0x36, 0x7b,
+	0x00, 0x65, 0x10, 0x5a,
+	0x08, 0xeb, 0x32, 0x6b,
+	0x07, 0xe9, 0x10, 0x31,
+	0x01, 0xe9, 0xca, 0x30,
+	0x01, 0x65, 0x46, 0x31,
+	0x00, 0x6a, 0xbc, 0x5e,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xa4, 0x6a, 0xf6, 0x5d,
+	0x08, 0x6a, 0xe2, 0x5d,
+	0x0d, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0xaa, 0x5e,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x00, 0x65, 0x8c, 0x5e,
+	0x01, 0x99, 0x46, 0x31,
+	0x00, 0xa3, 0xbc, 0x5e,
+	0x01, 0x88, 0x10, 0x31,
+	0x00, 0x65, 0x3c, 0x5a,
+	0x00, 0x65, 0xfc, 0x59,
+	0x03, 0x8c, 0x10, 0x30,
+	0x00, 0x65, 0xe8, 0x5d,
+	0x80, 0x0b, 0x84, 0x6a,
+	0x80, 0x0b, 0x64, 0x6b,
+	0x01, 0x0c, 0x5e, 0x7b,
+	0x10, 0x0c, 0x84, 0x7a,
+	0x03, 0x9e, 0x84, 0x6a,
+	0x00, 0x65, 0x06, 0x5a,
+	0x00, 0x6a, 0xbc, 0x5e,
+	0x01, 0xa4, 0x84, 0x6b,
+	0xff, 0x38, 0x7a, 0x7b,
+	0x01, 0x38, 0xc8, 0x30,
+	0x00, 0x08, 0x40, 0x19,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x00, 0x09, 0x42, 0x21,
+	0x00, 0x0a, 0x44, 0x21,
+	0xff, 0x6a, 0x70, 0x08,
+	0x00, 0x65, 0x7c, 0x43,
+	0x03, 0x08, 0x40, 0x31,
+	0x03, 0x08, 0x40, 0x31,
+	0x01, 0x08, 0x40, 0x31,
+	0x01, 0x09, 0x42, 0x31,
+	0x01, 0x0a, 0x44, 0x31,
+	0xfd, 0xb4, 0x68, 0x09,
+	0x12, 0x01, 0x02, 0x00,
+	0x12, 0x01, 0x02, 0x00,
+	0x04, 0x3c, 0xcc, 0x79,
+	0xfb, 0x3c, 0x78, 0x08,
+	0x04, 0x93, 0x20, 0x79,
+	0x01, 0x0c, 0x90, 0x6b,
+	0x80, 0xba, 0x20, 0x79,
+	0x80, 0x04, 0x20, 0x79,
+	0xe4, 0x6a, 0x70, 0x5d,
+	0x23, 0x6a, 0x86, 0x5d,
+	0x01, 0x6a, 0x86, 0x5d,
+	0x00, 0x65, 0x20, 0x41,
+	0x00, 0x65, 0xcc, 0x41,
+	0x80, 0x3c, 0xa4, 0x7b,
+	0x21, 0x6a, 0xda, 0x5e,
+	0x01, 0xbc, 0x18, 0x31,
+	0x02, 0x6a, 0x1a, 0x31,
+	0x02, 0x6a, 0xf8, 0x01,
+	0x01, 0xbc, 0x10, 0x30,
+	0x02, 0x6a, 0x12, 0x30,
+	0x01, 0xbc, 0x10, 0x30,
+	0xff, 0x6a, 0x12, 0x08,
+	0xff, 0x6a, 0x14, 0x08,
+	0xf3, 0xbc, 0xd4, 0x18,
+	0xa0, 0x6a, 0xca, 0x53,
+	0x04, 0xa0, 0x10, 0x31,
+	0xac, 0x6a, 0x26, 0x01,
+	0x04, 0xa0, 0x10, 0x31,
+	0x03, 0x08, 0x18, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xa0, 0x6a, 0xf6, 0x5d,
+	0x00, 0xbc, 0xe2, 0x5d,
+	0x3d, 0x6a, 0x26, 0x01,
+	0x00, 0x65, 0xe2, 0x43,
+	0xff, 0x6a, 0x10, 0x09,
+	0xa4, 0x6a, 0x26, 0x01,
+	0x0c, 0xa0, 0x32, 0x31,
+	0x05, 0x6a, 0x26, 0x01,
+	0x35, 0x6a, 0x26, 0x01,
+	0x0c, 0xa0, 0x32, 0x31,
+	0x36, 0x6a, 0x26, 0x01,
+	0x02, 0x93, 0x26, 0x01,
+	0x35, 0x6a, 0x26, 0x01,
+	0x00, 0x65, 0x9e, 0x5e,
+	0x00, 0x65, 0x9e, 0x5e,
+	0x02, 0x93, 0x26, 0x01,
+	0xbf, 0x3c, 0x78, 0x08,
+	0x04, 0x0b, 0xe8, 0x6b,
+	0x10, 0x0c, 0xe4, 0x7b,
+	0x01, 0x03, 0xe8, 0x6b,
+	0x20, 0x93, 0xea, 0x6b,
+	0x04, 0x0b, 0xf0, 0x6b,
+	0x40, 0x3c, 0x78, 0x00,
+	0xc7, 0x93, 0x26, 0x09,
+	0x38, 0x93, 0xf2, 0x6b,
+	0x00, 0x65, 0xcc, 0x41,
+	0x80, 0x3c, 0x58, 0x6c,
+	0x01, 0x06, 0x50, 0x31,
+	0x80, 0xb8, 0x70, 0x01,
+	0x00, 0x65, 0xcc, 0x41,
+	0x10, 0x3f, 0x06, 0x00,
+	0x10, 0x6a, 0x06, 0x00,
+	0x01, 0x3a, 0xca, 0x30,
+	0x80, 0x65, 0x1e, 0x64,
+	0x10, 0xb8, 0x42, 0x6c,
+	0xc0, 0x3e, 0xca, 0x00,
+	0x40, 0xb8, 0x0e, 0x6c,
+	0xbf, 0x65, 0xca, 0x08,
+	0x20, 0xb8, 0x22, 0x7c,
+	0x01, 0x65, 0x0c, 0x30,
+	0x00, 0x65, 0xda, 0x5d,
+	0xa0, 0x3f, 0x2a, 0x64,
+	0x23, 0xb8, 0x0c, 0x08,
+	0x00, 0x65, 0xda, 0x5d,
+	0xa0, 0x3f, 0x2a, 0x64,
+	0x00, 0xbb, 0x22, 0x44,
+	0xff, 0x65, 0x22, 0x64,
+	0x00, 0x65, 0x42, 0x44,
+	0x40, 0x6a, 0x18, 0x00,
+	0x01, 0x65, 0x0c, 0x30,
+	0x00, 0x65, 0xda, 0x5d,
+	0xa0, 0x3f, 0xfe, 0x73,
+	0x40, 0x6a, 0x18, 0x00,
+	0x01, 0x3a, 0xa6, 0x30,
+	0x08, 0x6a, 0x74, 0x00,
+	0x00, 0x65, 0xcc, 0x41,
+	0x64, 0x6a, 0x6a, 0x5d,
+	0x80, 0x64, 0xda, 0x6c,
+	0x04, 0x64, 0x9c, 0x74,
+	0x02, 0x64, 0xac, 0x74,
+	0x00, 0x6a, 0x62, 0x74,
+	0x03, 0x64, 0xca, 0x74,
+	0x23, 0x64, 0x4a, 0x74,
+	0x08, 0x64, 0x5e, 0x74,
+	0x61, 0x6a, 0xda, 0x5e,
+	0x00, 0x65, 0xda, 0x5d,
+	0x08, 0x51, 0xce, 0x71,
+	0x00, 0x65, 0x42, 0x44,
+	0x80, 0x04, 0x5c, 0x7c,
+	0x51, 0x6a, 0x60, 0x5d,
+	0x01, 0x51, 0x5c, 0x64,
+	0x01, 0xa4, 0x54, 0x7c,
+	0x80, 0xba, 0x5e, 0x6c,
+	0x41, 0x6a, 0xda, 0x5e,
+	0x00, 0x65, 0x5e, 0x44,
+	0x21, 0x6a, 0xda, 0x5e,
+	0x00, 0x65, 0x5e, 0x44,
+	0x07, 0x6a, 0x56, 0x5d,
+	0x01, 0x06, 0xd4, 0x30,
+	0x00, 0x65, 0xcc, 0x41,
+	0x80, 0xb8, 0x58, 0x7c,
+	0xc0, 0x3c, 0x6c, 0x7c,
+	0x80, 0x3c, 0x58, 0x6c,
+	0xff, 0xa8, 0x6c, 0x6c,
+	0x40, 0x3c, 0x58, 0x6c,
+	0x10, 0xb8, 0x70, 0x7c,
+	0xa1, 0x6a, 0xda, 0x5e,
+	0x01, 0xb4, 0x76, 0x6c,
+	0x02, 0xb4, 0x78, 0x6c,
+	0x01, 0xa4, 0x78, 0x7c,
+	0xff, 0xa8, 0x88, 0x7c,
+	0x04, 0xb4, 0x68, 0x01,
+	0x01, 0x6a, 0x76, 0x00,
+	0x00, 0xbb, 0x14, 0x5e,
+	0xff, 0xa8, 0x88, 0x7c,
+	0x71, 0x6a, 0xda, 0x5e,
+	0x40, 0x51, 0x88, 0x64,
+	0x00, 0x65, 0xb4, 0x5e,
+	0x00, 0x65, 0xde, 0x41,
+	0x00, 0xbb, 0x8c, 0x5c,
+	0x00, 0x65, 0xde, 0x41,
+	0x00, 0x65, 0xb4, 0x5e,
+	0x01, 0x65, 0xa2, 0x30,
+	0x01, 0xf8, 0xc8, 0x30,
+	0x01, 0x4e, 0xc8, 0x30,
+	0x00, 0x6a, 0xb8, 0xdd,
+	0x00, 0x51, 0xca, 0x5d,
+	0x01, 0x4e, 0x9c, 0x18,
+	0x02, 0x6a, 0x22, 0x05,
+	0xc0, 0x3c, 0x58, 0x6c,
+	0x04, 0xb8, 0x70, 0x01,
+	0x00, 0x65, 0xd6, 0x5e,
+	0x20, 0xb8, 0xde, 0x69,
+	0x01, 0xbb, 0xa2, 0x30,
+	0x3f, 0xba, 0x7c, 0x08,
+	0x00, 0xb9, 0xd0, 0x5c,
+	0x00, 0x65, 0xde, 0x41,
+	0x01, 0x06, 0xd4, 0x30,
+	0x20, 0x3c, 0xcc, 0x79,
+	0x20, 0x3c, 0x5e, 0x7c,
+	0x01, 0xa4, 0xba, 0x7c,
+	0x01, 0xb4, 0x68, 0x01,
+	0x00, 0x65, 0xcc, 0x41,
+	0x00, 0x65, 0x5e, 0x44,
+	0x04, 0x14, 0x58, 0x31,
+	0x01, 0x06, 0xd4, 0x30,
+	0x08, 0xa0, 0x60, 0x31,
+	0xac, 0x6a, 0xcc, 0x00,
+	0x14, 0x6a, 0xf6, 0x5d,
+	0x01, 0x06, 0xd4, 0x30,
+	0xa0, 0x6a, 0xee, 0x5d,
+	0x00, 0x65, 0xcc, 0x41,
+	0xdf, 0x3c, 0x78, 0x08,
+	0x12, 0x01, 0x02, 0x00,
+	0x00, 0x65, 0x5e, 0x44,
+	0x4c, 0x65, 0xcc, 0x28,
+	0x01, 0x3e, 0x20, 0x31,
+	0xd0, 0x66, 0xcc, 0x18,
+	0x20, 0x66, 0xcc, 0x18,
+	0x01, 0x51, 0xda, 0x34,
+	0x4c, 0x3d, 0xca, 0x28,
+	0x3f, 0x64, 0x7c, 0x08,
+	0xd0, 0x65, 0xca, 0x18,
+	0x01, 0x3e, 0x20, 0x31,
+	0x30, 0x65, 0xd4, 0x18,
+	0x00, 0x65, 0xe8, 0x4c,
+	0xe1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x20, 0x65, 0xd4, 0x18,
+	0x00, 0x65, 0xf0, 0x54,
+	0xe1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x20, 0x65, 0xca, 0x18,
+	0xe0, 0x65, 0xd4, 0x18,
+	0x00, 0x65, 0xfa, 0x4c,
+	0xe1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xd0, 0x65, 0xd4, 0x18,
+	0x00, 0x65, 0x02, 0x55,
+	0xe1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x01, 0x6c, 0xa2, 0x30,
+	0xff, 0x51, 0x14, 0x75,
+	0x00, 0x51, 0x90, 0x5d,
+	0x01, 0x51, 0x20, 0x31,
+	0x00, 0x65, 0x36, 0x45,
+	0x3f, 0xba, 0xc8, 0x08,
+	0x00, 0x3e, 0x36, 0x75,
+	0x00, 0x65, 0xb2, 0x5e,
+	0x80, 0x3c, 0x78, 0x00,
+	0x01, 0x06, 0xd4, 0x30,
+	0x00, 0x65, 0xda, 0x5d,
+	0x01, 0x3c, 0x78, 0x00,
+	0xe0, 0x3f, 0x52, 0x65,
+	0x02, 0x3c, 0x78, 0x00,
+	0x20, 0x12, 0x52, 0x65,
+	0x51, 0x6a, 0x60, 0x5d,
+	0x00, 0x51, 0x90, 0x5d,
+	0x51, 0x6a, 0x60, 0x5d,
+	0x01, 0x51, 0x20, 0x31,
+	0x04, 0x3c, 0x78, 0x00,
+	0x01, 0xb9, 0xc8, 0x30,
+	0x00, 0x3d, 0x50, 0x65,
+	0x08, 0x3c, 0x78, 0x00,
+	0x3f, 0xba, 0xc8, 0x08,
+	0x00, 0x3e, 0x50, 0x65,
+	0x10, 0x3c, 0x78, 0x00,
+	0x04, 0xb8, 0x50, 0x7d,
+	0xfb, 0xb8, 0x70, 0x09,
+	0x20, 0xb8, 0x46, 0x6d,
+	0x01, 0x90, 0xc8, 0x30,
+	0xff, 0x6a, 0xa2, 0x00,
+	0x00, 0x3d, 0xd0, 0x5c,
+	0x01, 0x64, 0x20, 0x31,
+	0xff, 0x6a, 0x78, 0x08,
+	0x00, 0x65, 0xea, 0x58,
+	0x10, 0xb8, 0x5e, 0x7c,
+	0xff, 0x6a, 0x56, 0x5d,
+	0x00, 0x65, 0x5e, 0x44,
+	0x00, 0x65, 0xb2, 0x5e,
+	0x31, 0x6a, 0xda, 0x5e,
+	0x00, 0x65, 0x5e, 0x44,
+	0x10, 0x3f, 0x06, 0x00,
+	0x10, 0x6a, 0x06, 0x00,
+	0x01, 0x65, 0x74, 0x34,
+	0x81, 0x6a, 0xda, 0x5e,
+	0x00, 0x65, 0x62, 0x45,
+	0x01, 0x06, 0xd4, 0x30,
+	0x01, 0x0c, 0x62, 0x7d,
+	0x04, 0x0c, 0x5c, 0x6d,
+	0xe0, 0x03, 0x7e, 0x08,
+	0xe0, 0x3f, 0xcc, 0x61,
+	0x01, 0x65, 0xcc, 0x30,
+	0x01, 0x12, 0xda, 0x34,
+	0x01, 0x06, 0xd4, 0x34,
+	0x01, 0x03, 0x70, 0x6d,
+	0x40, 0x03, 0xcc, 0x08,
+	0x01, 0x65, 0x06, 0x30,
+	0x40, 0x65, 0xc8, 0x08,
+	0x00, 0x66, 0x7e, 0x75,
+	0x40, 0x65, 0x7e, 0x7d,
+	0x00, 0x65, 0x7e, 0x5d,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0x88, 0x7d,
+	0x01, 0x65, 0x0c, 0x30,
+	0x02, 0x0b, 0x8c, 0x7d,
+	0xf7, 0x01, 0x02, 0x0c,
+	0x01, 0x65, 0xc8, 0x30,
+	0xff, 0x41, 0xb0, 0x75,
+	0x01, 0x41, 0x20, 0x31,
+	0xff, 0x6a, 0xa4, 0x00,
+	0x00, 0x65, 0xa0, 0x45,
+	0xff, 0xbf, 0xb0, 0x75,
+	0x01, 0x90, 0xa4, 0x30,
+	0x01, 0xbf, 0x20, 0x31,
+	0x00, 0xbb, 0x9a, 0x65,
+	0xff, 0x52, 0xae, 0x75,
+	0x01, 0xbf, 0xcc, 0x30,
+	0x01, 0x90, 0xca, 0x30,
+	0x01, 0x52, 0x20, 0x31,
+	0x01, 0x66, 0x7e, 0x31,
+	0x01, 0x65, 0x20, 0x35,
+	0x01, 0xbf, 0x82, 0x34,
+	0x01, 0x64, 0xa2, 0x30,
+	0x00, 0x6a, 0xc2, 0x5e,
+	0x0d, 0x6a, 0x76, 0x00,
+	0x00, 0x51, 0x14, 0x46,
+	0x01, 0x65, 0xa4, 0x30,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x48, 0x6a, 0x08, 0x5e,
+	0x01, 0x6a, 0xd0, 0x01,
+	0x01, 0x6a, 0xdc, 0x05,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x48, 0x6a, 0x08, 0x5e,
+	0x01, 0x6a, 0xe2, 0x5d,
+	0x01, 0x6a, 0x26, 0x05,
+	0x01, 0x65, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0xce, 0x7d,
+	0xff, 0x6a, 0xdc, 0x0d,
+	0x01, 0x65, 0x32, 0x31,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0xaa, 0x46,
+	0x81, 0x6a, 0xda, 0x5e,
+	0x01, 0x0c, 0xda, 0x7d,
+	0x04, 0x0c, 0xd8, 0x6d,
+	0xe0, 0x03, 0x06, 0x08,
+	0xe0, 0x03, 0x7e, 0x0c,
+	0x01, 0x65, 0x18, 0x31,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x0d,
+	0x01, 0x8c, 0x10, 0x30,
+	0x01, 0x8d, 0x12, 0x30,
+	0x01, 0x8e, 0x14, 0x34,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x34,
+	0x3d, 0x64, 0xa4, 0x28,
+	0x55, 0x64, 0xc8, 0x28,
+	0x00, 0x65, 0x08, 0x46,
+	0x2e, 0x64, 0xa4, 0x28,
+	0x66, 0x64, 0xc8, 0x28,
+	0x00, 0x6c, 0xda, 0x18,
+	0x01, 0x52, 0xc8, 0x30,
+	0x00, 0x6c, 0xda, 0x20,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x00, 0x6c, 0xda, 0x20,
+	0x00, 0x6c, 0xda, 0x24,
+	0x01, 0x65, 0xc8, 0x30,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x44, 0x6a, 0x04, 0x5e,
+	0x01, 0x90, 0xe2, 0x31,
+	0x04, 0x3b, 0x28, 0x7e,
+	0x30, 0x6a, 0xd0, 0x01,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x1d, 0x6a, 0xdc, 0x01,
+	0xdc, 0xee, 0x24, 0x66,
+	0x00, 0x65, 0x40, 0x46,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x01, 0x6a, 0xdc, 0x01,
+	0x20, 0xa0, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0x30, 0x7e,
+	0x11, 0x6a, 0xdc, 0x01,
+	0x50, 0xee, 0x34, 0x66,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x09, 0x6a, 0xdc, 0x01,
+	0x88, 0xee, 0x3a, 0x66,
+	0x19, 0x6a, 0xdc, 0x01,
+	0xd8, 0xee, 0x3e, 0x66,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x18, 0xee, 0x42, 0x6e,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x44, 0x6a, 0x04, 0x5e,
+	0x20, 0x6a, 0xe2, 0x5d,
+	0x01, 0x3b, 0x26, 0x31,
+	0x04, 0x3b, 0x5c, 0x6e,
+	0xa0, 0x6a, 0xca, 0x00,
+	0x20, 0x65, 0xc8, 0x18,
+	0x00, 0x65, 0x9a, 0x5e,
+	0x00, 0x65, 0x54, 0x66,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0xaa, 0x46,
+	0xa0, 0x6a, 0xcc, 0x00,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x20, 0x94, 0x60, 0x6e,
+	0x10, 0x94, 0x62, 0x6e,
+	0x08, 0x94, 0x7c, 0x6e,
+	0x08, 0x94, 0x7c, 0x6e,
+	0x08, 0x94, 0x7c, 0x6e,
+	0xff, 0x8c, 0xc8, 0x10,
+	0xc1, 0x64, 0xc8, 0x18,
+	0xf8, 0x64, 0xc8, 0x08,
+	0x01, 0x99, 0xda, 0x30,
+	0x00, 0x66, 0x70, 0x66,
+	0xc0, 0x66, 0xac, 0x76,
+	0x60, 0x66, 0xc8, 0x18,
+	0x3d, 0x64, 0xc8, 0x28,
+	0x00, 0x65, 0x60, 0x46,
+	0xf7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0x7e, 0x6e,
+	0x00, 0x62, 0xc4, 0x18,
+	0x00, 0x65, 0xaa, 0x5e,
+	0x00, 0x65, 0x8a, 0x5e,
+	0x00, 0x65, 0x8a, 0x5e,
+	0x00, 0x65, 0x8a, 0x5e,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x34,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x35,
+	0x08, 0x94, 0xaa, 0x7e,
+	0xf7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0xae, 0x6e,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x04, 0xb8, 0xd6, 0x6e,
+	0x01, 0x42, 0x7e, 0x31,
+	0xff, 0x6a, 0x76, 0x01,
+	0x01, 0x90, 0x84, 0x34,
+	0xff, 0x6a, 0x76, 0x05,
+	0x01, 0x85, 0x0a, 0x01,
+	0x7f, 0x65, 0x10, 0x09,
+	0xfe, 0x85, 0x0a, 0x0d,
+	0xff, 0x42, 0xd2, 0x66,
+	0xff, 0x41, 0xca, 0x66,
+	0xd1, 0x6a, 0xda, 0x5e,
+	0xff, 0x6a, 0xca, 0x04,
+	0x01, 0x41, 0x20, 0x31,
+	0x01, 0xbf, 0x82, 0x30,
+	0x01, 0x6a, 0x76, 0x00,
+	0x00, 0xbb, 0x14, 0x46,
+	0x01, 0x42, 0x20, 0x31,
+	0x01, 0xbf, 0x84, 0x34,
+	0x01, 0x41, 0x7e, 0x31,
+	0x01, 0x90, 0x82, 0x34,
+	0x01, 0x65, 0x22, 0x31,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xff, 0x6a, 0xd4, 0x0c
+};
+
+typedef int ahc_patch_func_t (struct ahc_softc *ahc);
+static ahc_patch_func_t ahc_patch23_func;
+
+static int
+ahc_patch23_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch22_func;
+
+static int
+ahc_patch22_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_CMD_CHAN) == 0);
+}
+
+static ahc_patch_func_t ahc_patch21_func;
+
+static int
+ahc_patch21_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_QUEUE_REGS) == 0);
+}
+
+static ahc_patch_func_t ahc_patch20_func;
+
+static int
+ahc_patch20_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_WIDE) != 0);
+}
+
+static ahc_patch_func_t ahc_patch19_func;
+
+static int
+ahc_patch19_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_SCB_BTT) != 0);
+}
+
+static ahc_patch_func_t ahc_patch18_func;
+
+static int
+ahc_patch18_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch17_func;
+
+static int
+ahc_patch17_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_TMODE_WIDEODD_BUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch16_func;
+
+static int
+ahc_patch16_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch15_func;
+
+static int
+ahc_patch15_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_ULTRA2) == 0);
+}
+
+static ahc_patch_func_t ahc_patch14_func;
+
+static int
+ahc_patch14_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0);
+}
+
+static ahc_patch_func_t ahc_patch13_func;
+
+static int
+ahc_patch13_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_39BIT_ADDRESSING) != 0);
+}
+
+static ahc_patch_func_t ahc_patch12_func;
+
+static int
+ahc_patch12_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_HS_MAILBOX) != 0);
+}
+
+static ahc_patch_func_t ahc_patch11_func;
+
+static int
+ahc_patch11_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_ULTRA) != 0);
+}
+
+static ahc_patch_func_t ahc_patch10_func;
+
+static int
+ahc_patch10_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_MULTI_TID) != 0);
+}
+
+static ahc_patch_func_t ahc_patch9_func;
+
+static int
+ahc_patch9_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_CMD_CHAN) != 0);
+}
+
+static ahc_patch_func_t ahc_patch8_func;
+
+static int
+ahc_patch8_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_INITIATORROLE) != 0);
+}
+
+static ahc_patch_func_t ahc_patch7_func;
+
+static int
+ahc_patch7_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_TARGETROLE) != 0);
+}
+
+static ahc_patch_func_t ahc_patch6_func;
+
+static int
+ahc_patch6_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_DT) == 0);
+}
+
+static ahc_patch_func_t ahc_patch5_func;
+
+static int
+ahc_patch5_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0);
+}
+
+static ahc_patch_func_t ahc_patch4_func;
+
+static int
+ahc_patch4_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_PAGESCBS) != 0);
+}
+
+static ahc_patch_func_t ahc_patch3_func;
+
+static int
+ahc_patch3_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_QUEUE_REGS) != 0);
+}
+
+static ahc_patch_func_t ahc_patch2_func;
+
+static int
+ahc_patch2_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_TWIN) != 0);
+}
+
+static ahc_patch_func_t ahc_patch1_func;
+
+static int
+ahc_patch1_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_ULTRA2) != 0);
+}
+
+static ahc_patch_func_t ahc_patch0_func;
+
+static int
+ahc_patch0_func(struct ahc_softc *ahc)
+{
+	return (0);
+}
+
+static const struct patch {
+	ahc_patch_func_t		*patch_func;
+	uint32_t		 begin		:10,
+				 skip_instr	:10,
+				 skip_patch	:12;
+} patches[] = {
+	{ ahc_patch1_func, 4, 1, 1 },
+	{ ahc_patch2_func, 6, 2, 1 },
+	{ ahc_patch2_func, 9, 1, 1 },
+	{ ahc_patch3_func, 11, 1, 2 },
+	{ ahc_patch0_func, 12, 2, 1 },
+	{ ahc_patch4_func, 15, 1, 2 },
+	{ ahc_patch0_func, 16, 1, 1 },
+	{ ahc_patch5_func, 22, 2, 1 },
+	{ ahc_patch3_func, 27, 1, 2 },
+	{ ahc_patch0_func, 28, 1, 1 },
+	{ ahc_patch6_func, 34, 1, 1 },
+	{ ahc_patch7_func, 37, 54, 19 },
+	{ ahc_patch8_func, 37, 1, 1 },
+	{ ahc_patch9_func, 42, 3, 2 },
+	{ ahc_patch0_func, 45, 3, 1 },
+	{ ahc_patch10_func, 49, 1, 2 },
+	{ ahc_patch0_func, 50, 2, 3 },
+	{ ahc_patch1_func, 50, 1, 2 },
+	{ ahc_patch0_func, 51, 1, 1 },
+	{ ahc_patch2_func, 53, 2, 1 },
+	{ ahc_patch9_func, 55, 1, 2 },
+	{ ahc_patch0_func, 56, 1, 1 },
+	{ ahc_patch9_func, 60, 1, 2 },
+	{ ahc_patch0_func, 61, 1, 1 },
+	{ ahc_patch9_func, 71, 1, 2 },
+	{ ahc_patch0_func, 72, 1, 1 },
+	{ ahc_patch9_func, 75, 1, 2 },
+	{ ahc_patch0_func, 76, 1, 1 },
+	{ ahc_patch9_func, 79, 1, 2 },
+	{ ahc_patch0_func, 80, 1, 1 },
+	{ ahc_patch8_func, 91, 9, 4 },
+	{ ahc_patch1_func, 93, 1, 2 },
+	{ ahc_patch0_func, 94, 1, 1 },
+	{ ahc_patch2_func, 96, 2, 1 },
+	{ ahc_patch2_func, 105, 4, 1 },
+	{ ahc_patch1_func, 109, 1, 2 },
+	{ ahc_patch0_func, 110, 2, 3 },
+	{ ahc_patch2_func, 110, 1, 2 },
+	{ ahc_patch0_func, 111, 1, 1 },
+	{ ahc_patch7_func, 112, 4, 2 },
+	{ ahc_patch0_func, 116, 1, 1 },
+	{ ahc_patch11_func, 117, 2, 1 },
+	{ ahc_patch1_func, 119, 1, 2 },
+	{ ahc_patch0_func, 120, 1, 1 },
+	{ ahc_patch7_func, 121, 4, 1 },
+	{ ahc_patch7_func, 131, 95, 11 },
+	{ ahc_patch4_func, 151, 1, 1 },
+	{ ahc_patch1_func, 168, 1, 1 },
+	{ ahc_patch12_func, 173, 1, 2 },
+	{ ahc_patch0_func, 174, 1, 1 },
+	{ ahc_patch9_func, 185, 1, 2 },
+	{ ahc_patch0_func, 186, 1, 1 },
+	{ ahc_patch9_func, 195, 1, 2 },
+	{ ahc_patch0_func, 196, 1, 1 },
+	{ ahc_patch9_func, 212, 6, 2 },
+	{ ahc_patch0_func, 218, 6, 1 },
+	{ ahc_patch8_func, 226, 21, 2 },
+	{ ahc_patch1_func, 241, 1, 1 },
+	{ ahc_patch1_func, 249, 1, 2 },
+	{ ahc_patch0_func, 250, 2, 2 },
+	{ ahc_patch11_func, 251, 1, 1 },
+	{ ahc_patch9_func, 259, 27, 3 },
+	{ ahc_patch1_func, 275, 10, 2 },
+	{ ahc_patch13_func, 278, 1, 1 },
+	{ ahc_patch14_func, 286, 14, 1 },
+	{ ahc_patch1_func, 302, 1, 2 },
+	{ ahc_patch0_func, 303, 1, 1 },
+	{ ahc_patch9_func, 306, 1, 1 },
+	{ ahc_patch13_func, 311, 1, 1 },
+	{ ahc_patch9_func, 312, 2, 2 },
+	{ ahc_patch0_func, 314, 4, 1 },
+	{ ahc_patch14_func, 318, 1, 1 },
+	{ ahc_patch15_func, 320, 2, 3 },
+	{ ahc_patch9_func, 320, 1, 2 },
+	{ ahc_patch0_func, 321, 1, 1 },
+	{ ahc_patch6_func, 326, 1, 2 },
+	{ ahc_patch0_func, 327, 1, 1 },
+	{ ahc_patch1_func, 331, 47, 11 },
+	{ ahc_patch6_func, 338, 2, 4 },
+	{ ahc_patch7_func, 338, 1, 1 },
+	{ ahc_patch8_func, 339, 1, 1 },
+	{ ahc_patch0_func, 340, 1, 1 },
+	{ ahc_patch16_func, 341, 1, 1 },
+	{ ahc_patch6_func, 357, 6, 3 },
+	{ ahc_patch16_func, 357, 5, 1 },
+	{ ahc_patch0_func, 363, 7, 1 },
+	{ ahc_patch13_func, 373, 5, 1 },
+	{ ahc_patch0_func, 378, 52, 17 },
+	{ ahc_patch14_func, 378, 1, 1 },
+	{ ahc_patch7_func, 380, 2, 2 },
+	{ ahc_patch17_func, 381, 1, 1 },
+	{ ahc_patch9_func, 384, 1, 1 },
+	{ ahc_patch18_func, 391, 1, 1 },
+	{ ahc_patch14_func, 396, 9, 3 },
+	{ ahc_patch9_func, 397, 3, 2 },
+	{ ahc_patch0_func, 400, 3, 1 },
+	{ ahc_patch9_func, 408, 6, 2 },
+	{ ahc_patch0_func, 414, 9, 2 },
+	{ ahc_patch13_func, 414, 1, 1 },
+	{ ahc_patch13_func, 423, 2, 1 },
+	{ ahc_patch14_func, 425, 1, 1 },
+	{ ahc_patch9_func, 427, 1, 2 },
+	{ ahc_patch0_func, 428, 1, 1 },
+	{ ahc_patch7_func, 429, 1, 1 },
+	{ ahc_patch7_func, 430, 1, 1 },
+	{ ahc_patch8_func, 431, 3, 3 },
+	{ ahc_patch6_func, 432, 1, 2 },
+	{ ahc_patch0_func, 433, 1, 1 },
+	{ ahc_patch9_func, 434, 1, 1 },
+	{ ahc_patch15_func, 435, 1, 2 },
+	{ ahc_patch13_func, 435, 1, 1 },
+	{ ahc_patch14_func, 437, 9, 4 },
+	{ ahc_patch9_func, 437, 1, 1 },
+	{ ahc_patch9_func, 444, 2, 1 },
+	{ ahc_patch0_func, 446, 4, 3 },
+	{ ahc_patch9_func, 446, 1, 2 },
+	{ ahc_patch0_func, 447, 3, 1 },
+	{ ahc_patch1_func, 451, 2, 1 },
+	{ ahc_patch7_func, 453, 10, 2 },
+	{ ahc_patch0_func, 463, 1, 1 },
+	{ ahc_patch8_func, 464, 118, 22 },
+	{ ahc_patch1_func, 466, 3, 2 },
+	{ ahc_patch0_func, 469, 5, 3 },
+	{ ahc_patch9_func, 469, 2, 2 },
+	{ ahc_patch0_func, 471, 3, 1 },
+	{ ahc_patch1_func, 476, 2, 2 },
+	{ ahc_patch0_func, 478, 6, 3 },
+	{ ahc_patch9_func, 478, 2, 2 },
+	{ ahc_patch0_func, 480, 3, 1 },
+	{ ahc_patch1_func, 486, 2, 2 },
+	{ ahc_patch0_func, 488, 9, 7 },
+	{ ahc_patch9_func, 488, 5, 6 },
+	{ ahc_patch19_func, 488, 1, 2 },
+	{ ahc_patch0_func, 489, 1, 1 },
+	{ ahc_patch19_func, 491, 1, 2 },
+	{ ahc_patch0_func, 492, 1, 1 },
+	{ ahc_patch0_func, 493, 4, 1 },
+	{ ahc_patch6_func, 498, 3, 2 },
+	{ ahc_patch0_func, 501, 1, 1 },
+	{ ahc_patch6_func, 511, 1, 2 },
+	{ ahc_patch0_func, 512, 1, 1 },
+	{ ahc_patch20_func, 549, 7, 1 },
+	{ ahc_patch3_func, 584, 1, 2 },
+	{ ahc_patch0_func, 585, 1, 1 },
+	{ ahc_patch21_func, 588, 1, 1 },
+	{ ahc_patch8_func, 590, 106, 33 },
+	{ ahc_patch4_func, 592, 1, 1 },
+	{ ahc_patch1_func, 598, 2, 2 },
+	{ ahc_patch0_func, 600, 1, 1 },
+	{ ahc_patch1_func, 603, 1, 2 },
+	{ ahc_patch0_func, 604, 1, 1 },
+	{ ahc_patch9_func, 605, 3, 3 },
+	{ ahc_patch15_func, 606, 1, 1 },
+	{ ahc_patch0_func, 608, 4, 1 },
+	{ ahc_patch19_func, 617, 2, 2 },
+	{ ahc_patch0_func, 619, 1, 1 },
+	{ ahc_patch19_func, 623, 10, 3 },
+	{ ahc_patch5_func, 625, 8, 1 },
+	{ ahc_patch0_func, 633, 9, 2 },
+	{ ahc_patch5_func, 634, 8, 1 },
+	{ ahc_patch4_func, 644, 1, 2 },
+	{ ahc_patch0_func, 645, 1, 1 },
+	{ ahc_patch19_func, 646, 1, 2 },
+	{ ahc_patch0_func, 647, 3, 2 },
+	{ ahc_patch4_func, 649, 1, 1 },
+	{ ahc_patch5_func, 650, 1, 1 },
+	{ ahc_patch5_func, 653, 1, 1 },
+	{ ahc_patch5_func, 655, 1, 1 },
+	{ ahc_patch4_func, 657, 2, 2 },
+	{ ahc_patch0_func, 659, 2, 1 },
+	{ ahc_patch5_func, 661, 1, 1 },
+	{ ahc_patch5_func, 664, 1, 1 },
+	{ ahc_patch5_func, 667, 1, 1 },
+	{ ahc_patch19_func, 671, 1, 1 },
+	{ ahc_patch19_func, 674, 1, 1 },
+	{ ahc_patch4_func, 680, 1, 1 },
+	{ ahc_patch6_func, 683, 1, 2 },
+	{ ahc_patch0_func, 684, 1, 1 },
+	{ ahc_patch7_func, 696, 16, 1 },
+	{ ahc_patch4_func, 712, 20, 1 },
+	{ ahc_patch9_func, 733, 4, 2 },
+	{ ahc_patch0_func, 737, 4, 1 },
+	{ ahc_patch9_func, 741, 4, 2 },
+	{ ahc_patch0_func, 745, 3, 1 },
+	{ ahc_patch6_func, 751, 1, 1 },
+	{ ahc_patch22_func, 753, 14, 1 },
+	{ ahc_patch7_func, 767, 3, 1 },
+	{ ahc_patch9_func, 779, 24, 8 },
+	{ ahc_patch19_func, 783, 1, 2 },
+	{ ahc_patch0_func, 784, 1, 1 },
+	{ ahc_patch15_func, 789, 4, 2 },
+	{ ahc_patch0_func, 793, 7, 3 },
+	{ ahc_patch23_func, 793, 5, 2 },
+	{ ahc_patch0_func, 798, 2, 1 },
+	{ ahc_patch0_func, 803, 42, 3 },
+	{ ahc_patch18_func, 815, 18, 2 },
+	{ ahc_patch0_func, 833, 1, 1 },
+	{ ahc_patch4_func, 857, 1, 1 },
+	{ ahc_patch4_func, 858, 3, 2 },
+	{ ahc_patch0_func, 861, 1, 1 },
+	{ ahc_patch13_func, 862, 3, 1 },
+	{ ahc_patch4_func, 865, 12, 1 }
+};
+
+static const struct cs {
+	uint16_t	begin;
+	uint16_t	end;
+} critical_sections[] = {
+	{ 11, 18 },
+	{ 21, 30 },
+	{ 712, 728 },
+	{ 858, 861 },
+	{ 865, 871 },
+	{ 873, 875 },
+	{ 875, 877 }
+};
+
+static const int num_critical_sections = sizeof(critical_sections)
+				       / sizeof(*critical_sections);
diff --git a/drivers/scsi/aic7xxx/aicasm/Makefile b/drivers/scsi/aic7xxx/aicasm/Makefile
new file mode 100644
index 0000000..b98c5c1
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/Makefile
@@ -0,0 +1,80 @@
+PROG=	aicasm
+
+.SUFFIXES= .l .y .c .h
+
+CSRCS=	aicasm.c aicasm_symbol.c
+YSRCS=	aicasm_gram.y aicasm_macro_gram.y
+LSRCS=	aicasm_scan.l aicasm_macro_scan.l
+
+GENHDRS=	aicdb.h $(YSRCS:.y=.h)
+GENSRCS=	$(YSRCS:.y=.c) $(LSRCS:.l=.c)
+
+SRCS=	${CSRCS} ${GENSRCS}
+LIBS=	-ldb
+clean-files:= ${GENSRCS} ${GENHDRS} $(YSRCS:.y=.output) $(PROG)
+# Override default kernel CFLAGS.  This is a userland app.
+AICASM_CFLAGS:= -I/usr/include -I.
+LEX= flex
+YACC= bison
+YFLAGS= -d
+
+NOMAN=	noman
+
+ifneq ($(HOSTCC),)
+AICASM_CC= $(HOSTCC)
+else
+AICASM_CC= $(CC)
+endif
+
+ifdef DEBUG
+CFLAGS+= -DDEBUG -g
+YFLAGS+= -t -v
+LFLAGS= -d
+endif
+
+$(PROG):  ${GENHDRS} $(SRCS)
+	$(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG) $(LIBS)
+
+aicdb.h:
+	@if [ -e "/usr/include/db4/db_185.h" ]; then		\
+		echo "#include <db4/db_185.h>" > aicdb.h;	\
+	 elif [ -e "/usr/include/db3/db_185.h" ]; then		\
+		echo "#include <db3/db_185.h>" > aicdb.h;	\
+	 elif [ -e "/usr/include/db2/db_185.h" ]; then		\
+		echo "#include <db2/db_185.h>" > aicdb.h;	\
+	 elif [ -e "/usr/include/db1/db_185.h" ]; then		\
+		echo "#include <db1/db_185.h>" > aicdb.h;	\
+	 elif [ -e "/usr/include/db/db_185.h" ]; then		\
+		echo "#include <db/db_185.h>" > aicdb.h;	\
+	 elif [ -e "/usr/include/db_185.h" ]; then		\
+		echo "#include <db_185.h>" > aicdb.h;		\
+	 else							\
+		echo "*** Install db development libraries";	\
+	 fi
+
+clean:
+	rm -f $(clean-files)
+
+# Create a dependency chain in generated files
+# to avoid concurrent invocations of the single
+# rule that builds them all.
+aicasm_gram.c: aicasm_gram.h
+aicasm_gram.c aicasm_gram.h: aicasm_gram.y
+	$(YACC) $(YFLAGS) -b $(<:.y=) $<
+	mv $(<:.y=).tab.c $(<:.y=.c)
+	mv $(<:.y=).tab.h $(<:.y=.h)
+
+# Create a dependency chain in generated files
+# to avoid concurrent invocations of the single
+# rule that builds them all.
+aicasm_macro_gram.c: aicasm_macro_gram.h
+aicasm_macro_gram.c aicasm_macro_gram.h: aicasm_macro_gram.y
+	$(YACC) $(YFLAGS) -b $(<:.y=) -p mm $<
+	mv $(<:.y=).tab.c $(<:.y=.c)
+	mv $(<:.y=).tab.h $(<:.y=.h)
+
+aicasm_scan.c: aicasm_scan.l
+	$(LEX) $(LFLAGS) -o$@ $<
+
+aicasm_macro_scan.c: aicasm_macro_scan.l
+	$(LEX) $(LFLAGS) -Pmm -o$@ $<
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.c b/drivers/scsi/aic7xxx/aicasm/aicasm.c
new file mode 100644
index 0000000..2e3117a
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.c
@@ -0,0 +1,844 @@
+/*
+ * Aic7xxx SCSI host adapter firmware assembler
+ *
+ * Copyright (c) 1997, 1998, 2000, 2001 Justin T. Gibbs.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.c#23 $
+ *
+ * $FreeBSD$
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#if linux
+#include <endian.h>
+#else
+#include <machine/endian.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+typedef struct patch {
+	STAILQ_ENTRY(patch) links;
+	int		patch_func;
+	u_int		begin;
+	u_int		skip_instr;
+	u_int		skip_patch;
+} patch_t;
+
+STAILQ_HEAD(patch_list, patch) patches;
+
+static void usage(void);
+static void back_patch(void);
+static void output_code(void);
+static void output_listing(char *ifilename);
+static void dump_scope(scope_t *scope);
+static void emit_patch(scope_t *scope, int patch);
+static int check_patch(patch_t **start_patch, int start_instr,
+		       int *skip_addr, int *func_vals);
+
+struct path_list search_path;
+int includes_search_curdir;
+char *appname;
+char *stock_include_file;
+FILE *ofile;
+char *ofilename;
+char *regfilename;
+FILE *regfile;
+char *listfilename;
+FILE *listfile;
+char *regdiagfilename;
+FILE *regdiagfile;
+int   src_mode;
+int   dst_mode;
+
+static STAILQ_HEAD(,instruction) seq_program;
+struct cs_tailq cs_tailq;
+struct scope_list scope_stack;
+symlist_t patch_functions;
+
+#if DEBUG
+extern int yy_flex_debug;
+extern int mm_flex_debug;
+extern int yydebug;
+extern int mmdebug;
+#endif
+extern FILE *yyin;
+extern int yyparse(void);
+
+int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+	extern char *optarg;
+	extern int optind;
+	int  ch;
+	int  retval;
+	char *inputfilename;
+	scope_t *sentinal;
+
+	STAILQ_INIT(&patches);
+	SLIST_INIT(&search_path);
+	STAILQ_INIT(&seq_program);
+	TAILQ_INIT(&cs_tailq);
+	SLIST_INIT(&scope_stack);
+
+	/* Set Sentinal scope node */
+	sentinal = scope_alloc();
+	sentinal->type = SCOPE_ROOT;
+	
+	includes_search_curdir = 1;
+	appname = *argv;
+	regfile = NULL;
+	listfile = NULL;
+#if DEBUG
+	yy_flex_debug = 0;
+	mm_flex_debug = 0;
+	yydebug = 0;
+	mmdebug = 0;
+#endif
+	while ((ch = getopt(argc, argv, "d:i:l:n:o:p:r:I:")) != -1) {
+		switch(ch) {
+		case 'd':
+#if DEBUG
+			if (strcmp(optarg, "s") == 0) {
+				yy_flex_debug = 1;
+				mm_flex_debug = 1;
+			} else if (strcmp(optarg, "p") == 0) {
+				yydebug = 1;
+				mmdebug = 1;
+			} else {
+				fprintf(stderr, "%s: -d Requires either an "
+					"'s' or 'p' argument\n", appname);
+				usage();
+			}
+#else
+			stop("-d: Assembler not built with debugging "
+			     "information", EX_SOFTWARE);
+#endif
+			break;
+		case 'i':
+			stock_include_file = optarg;
+			break;
+		case 'l':
+			/* Create a program listing */
+			if ((listfile = fopen(optarg, "w")) == NULL) {
+				perror(optarg);
+				stop(NULL, EX_CANTCREAT);
+			}
+			listfilename = optarg;
+			break;
+		case 'n':
+			/* Don't complain about the -nostdinc directrive */
+			if (strcmp(optarg, "ostdinc")) {
+				fprintf(stderr, "%s: Unknown option -%c%s\n",
+					appname, ch, optarg);
+				usage();
+				/* NOTREACHED */
+			}
+			break;
+		case 'o':
+			if ((ofile = fopen(optarg, "w")) == NULL) {
+				perror(optarg);
+				stop(NULL, EX_CANTCREAT);
+			}
+			ofilename = optarg;
+			break;
+		case 'p':
+			/* Create Register Diagnostic "printing" Functions */
+			if ((regdiagfile = fopen(optarg, "w")) == NULL) {
+				perror(optarg);
+				stop(NULL, EX_CANTCREAT);
+			}
+			regdiagfilename = optarg;
+			break;
+		case 'r':
+			if ((regfile = fopen(optarg, "w")) == NULL) {
+				perror(optarg);
+				stop(NULL, EX_CANTCREAT);
+			}
+			regfilename = optarg;
+			break;
+		case 'I':
+		{
+			path_entry_t include_dir;
+
+			if (strcmp(optarg, "-") == 0) {
+				if (includes_search_curdir == 0) {
+					fprintf(stderr, "%s: Warning - '-I-' "
+							"specified multiple "
+							"times\n", appname);
+				}
+				includes_search_curdir = 0;
+				for (include_dir = SLIST_FIRST(&search_path);
+				     include_dir != NULL;
+				     include_dir = SLIST_NEXT(include_dir,
+							      links))
+					/*
+					 * All entries before a '-I-' only
+					 * apply to includes specified with
+					 * quotes instead of "<>".
+					 */
+					include_dir->quoted_includes_only = 1;
+			} else {
+				include_dir =
+				    (path_entry_t)malloc(sizeof(*include_dir));
+				if (include_dir == NULL) {
+					perror(optarg);
+					stop(NULL, EX_OSERR);
+				}
+				include_dir->directory = strdup(optarg);
+				if (include_dir->directory == NULL) {
+					perror(optarg);
+					stop(NULL, EX_OSERR);
+				}
+				include_dir->quoted_includes_only = 0;
+				SLIST_INSERT_HEAD(&search_path, include_dir,
+						  links);
+			}
+			break;
+		}
+		case '?':
+		default:
+			usage();
+			/* NOTREACHED */
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc != 1) {
+		fprintf(stderr, "%s: No input file specifiled\n", appname);
+		usage();
+		/* NOTREACHED */
+	}
+
+	if (regdiagfile != NULL
+	 && (regfile == NULL || stock_include_file == NULL)) {
+		fprintf(stderr,
+			"%s: The -p option requires the -r and -i options.\n",
+			appname);
+		usage();
+		/* NOTREACHED */
+	}
+	symtable_open();
+	inputfilename = *argv;
+	include_file(*argv, SOURCE_FILE);
+	retval = yyparse();
+	if (retval == 0) {
+		if (SLIST_FIRST(&scope_stack) == NULL
+		 || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) {
+			stop("Unterminated conditional expression", EX_DATAERR);
+			/* NOTREACHED */
+		}
+
+		/* Process outmost scope */
+		process_scope(SLIST_FIRST(&scope_stack));
+		/*
+		 * Decend the tree of scopes and insert/emit
+		 * patches as appropriate.  We perform a depth first
+		 * tranversal, recursively handling each scope.
+		 */
+		/* start at the root scope */
+		dump_scope(SLIST_FIRST(&scope_stack));
+
+		/* Patch up forward jump addresses */
+		back_patch();
+
+		if (ofile != NULL)
+			output_code();
+		if (regfile != NULL)
+			symtable_dump(regfile, regdiagfile);
+		if (listfile != NULL)
+			output_listing(inputfilename);
+	}
+
+	stop(NULL, 0);
+	/* NOTREACHED */
+	return (0);
+}
+
+static void
+usage()
+{
+
+	(void)fprintf(stderr,
+"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]\n"
+"	[-r register_output_file [-p register_diag_file -i includefile]]\n"
+"	[-l program_list_file]\n"
+"	input_file\n", appname);
+	exit(EX_USAGE);
+}
+
+static void
+back_patch()
+{
+	struct instruction *cur_instr;
+
+	for (cur_instr = STAILQ_FIRST(&seq_program);
+	     cur_instr != NULL;
+	     cur_instr = STAILQ_NEXT(cur_instr, links)) {
+		if (cur_instr->patch_label != NULL) {
+			struct ins_format3 *f3_instr;
+			u_int address;
+
+			if (cur_instr->patch_label->type != LABEL) {
+				char buf[255];
+
+				snprintf(buf, sizeof(buf),
+					 "Undefined label %s",
+					 cur_instr->patch_label->name);
+				stop(buf, EX_DATAERR);
+				/* NOTREACHED */
+			}
+			f3_instr = &cur_instr->format.format3;
+			address = f3_instr->address;
+			address += cur_instr->patch_label->info.linfo->address;
+			f3_instr->address = address;
+		}
+	}
+}
+
+static void
+output_code()
+{
+	struct instruction *cur_instr;
+	patch_t *cur_patch;
+	critical_section_t *cs;
+	symbol_node_t *cur_node;
+	int instrcount;
+
+	instrcount = 0;
+	fprintf(ofile,
+"/*\n"
+" * DO NOT EDIT - This file is automatically generated\n"
+" *		 from the following source files:\n"
+" *\n"
+"%s */\n", versions);
+
+	fprintf(ofile, "static const uint8_t seqprog[] = {\n");
+	for (cur_instr = STAILQ_FIRST(&seq_program);
+	     cur_instr != NULL;
+	     cur_instr = STAILQ_NEXT(cur_instr, links)) {
+
+		fprintf(ofile, "%s\t0x%02x, 0x%02x, 0x%02x, 0x%02x",
+			cur_instr == STAILQ_FIRST(&seq_program) ? "" : ",\n",
+#ifdef __LITTLE_ENDIAN
+			cur_instr->format.bytes[0],
+			cur_instr->format.bytes[1],
+			cur_instr->format.bytes[2],
+			cur_instr->format.bytes[3]);
+#else
+			cur_instr->format.bytes[3],
+			cur_instr->format.bytes[2],
+			cur_instr->format.bytes[1],
+			cur_instr->format.bytes[0]);
+#endif
+		instrcount++;
+	}
+	fprintf(ofile, "\n};\n\n");
+
+	if (patch_arg_list == NULL)
+		stop("Patch argument list not defined",
+		     EX_DATAERR);
+
+	/*
+	 *  Output patch information.  Patch functions first.
+	 */
+	fprintf(ofile,
+"typedef int %spatch_func_t (%s);\n", prefix, patch_arg_list);
+
+	for (cur_node = SLIST_FIRST(&patch_functions);
+	     cur_node != NULL;
+	     cur_node = SLIST_NEXT(cur_node,links)) {
+		fprintf(ofile,
+"static %spatch_func_t %spatch%d_func;\n"
+"\n"
+"static int\n"
+"%spatch%d_func(%s)\n"
+"{\n"
+"	return (%s);\n"
+"}\n\n",
+			prefix,
+			prefix,
+			cur_node->symbol->info.condinfo->func_num,
+			prefix,
+			cur_node->symbol->info.condinfo->func_num,
+			patch_arg_list,
+			cur_node->symbol->name);
+	}
+
+	fprintf(ofile,
+"static const struct patch {\n"
+"	%spatch_func_t		*patch_func;\n"
+"	uint32_t		 begin		:10,\n"
+"				 skip_instr	:10,\n"
+"				 skip_patch	:12;\n"
+"} patches[] = {\n", prefix);
+
+	for (cur_patch = STAILQ_FIRST(&patches);
+	     cur_patch != NULL;
+	     cur_patch = STAILQ_NEXT(cur_patch,links)) {
+		fprintf(ofile, "%s\t{ %spatch%d_func, %d, %d, %d }",
+			cur_patch == STAILQ_FIRST(&patches) ? "" : ",\n",
+			prefix,
+			cur_patch->patch_func, cur_patch->begin,
+			cur_patch->skip_instr, cur_patch->skip_patch);
+	}
+
+	fprintf(ofile, "\n};\n\n");
+
+	fprintf(ofile,
+"static const struct cs {\n"
+"	uint16_t	begin;\n"
+"	uint16_t	end;\n"
+"} critical_sections[] = {\n");
+
+	for (cs = TAILQ_FIRST(&cs_tailq);
+	     cs != NULL;
+	     cs = TAILQ_NEXT(cs, links)) {
+		fprintf(ofile, "%s\t{ %d, %d }",
+			cs == TAILQ_FIRST(&cs_tailq) ? "" : ",\n",
+			cs->begin_addr, cs->end_addr);
+	}
+
+	fprintf(ofile, "\n};\n\n");
+
+	fprintf(ofile,
+"static const int num_critical_sections = sizeof(critical_sections)\n"
+"				       / sizeof(*critical_sections);\n");
+
+	fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
+}
+
+static void
+dump_scope(scope_t *scope)
+{
+	scope_t *cur_scope;
+
+	/*
+	 * Emit the first patch for this scope
+	 */
+	emit_patch(scope, 0);
+
+	/*
+	 * Dump each scope within this one.
+	 */
+	cur_scope = TAILQ_FIRST(&scope->inner_scope);
+
+	while (cur_scope != NULL) {
+
+		dump_scope(cur_scope);
+
+		cur_scope = TAILQ_NEXT(cur_scope, scope_links);
+	}
+
+	/*
+	 * Emit the second, closing, patch for this scope
+	 */
+	emit_patch(scope, 1);
+}
+
+void
+emit_patch(scope_t *scope, int patch)
+{
+	patch_info_t *pinfo;
+	patch_t *new_patch;
+
+	pinfo = &scope->patches[patch];
+
+	if (pinfo->skip_instr == 0)
+		/* No-Op patch */
+		return;
+
+	new_patch = (patch_t *)malloc(sizeof(*new_patch));
+
+	if (new_patch == NULL)
+		stop("Could not malloc patch structure", EX_OSERR);
+
+	memset(new_patch, 0, sizeof(*new_patch));
+
+	if (patch == 0) {
+		new_patch->patch_func = scope->func_num;
+		new_patch->begin = scope->begin_addr;
+	} else {
+		new_patch->patch_func = 0;
+		new_patch->begin = scope->end_addr;
+	}
+	new_patch->skip_instr = pinfo->skip_instr;
+	new_patch->skip_patch = pinfo->skip_patch;
+	STAILQ_INSERT_TAIL(&patches, new_patch, links);
+}
+
+void
+output_listing(char *ifilename)
+{
+	char buf[1024];
+	FILE *ifile;
+	struct instruction *cur_instr;
+	patch_t *cur_patch;
+	symbol_node_t *cur_func;
+	int *func_values;
+	int instrcount;
+	int instrptr;
+	int line;
+	int func_count;
+	int skip_addr;
+
+	instrcount = 0;
+	instrptr = 0;
+	line = 1;
+	skip_addr = 0;
+	if ((ifile = fopen(ifilename, "r")) == NULL) {
+		perror(ifilename);
+		stop(NULL, EX_DATAERR);
+	}
+
+	/*
+	 * Determine which options to apply to this listing.
+	 */
+	for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions);
+	    cur_func != NULL;
+	    cur_func = SLIST_NEXT(cur_func, links))
+		func_count++;
+
+	func_values = NULL;
+	if (func_count != 0) {
+		func_values = (int *)malloc(func_count * sizeof(int));
+
+		if (func_values == NULL)
+			stop("Could not malloc", EX_OSERR);
+		
+		func_values[0] = 0; /* FALSE func */
+		func_count--;
+
+		/*
+		 * Ask the user to fill in the return values for
+		 * the rest of the functions.
+		 */
+		
+		
+		for (cur_func = SLIST_FIRST(&patch_functions);
+		     cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL;
+		     cur_func = SLIST_NEXT(cur_func, links), func_count--) {
+			int input;
+			
+			fprintf(stdout, "\n(%s)\n", cur_func->symbol->name);
+			fprintf(stdout,
+				"Enter the return value for "
+				"this expression[T/F]:");
+
+			while (1) {
+
+				input = getchar();
+				input = toupper(input);
+
+				if (input == 'T') {
+					func_values[func_count] = 1;
+					break;
+				} else if (input == 'F') {
+					func_values[func_count] = 0;
+					break;
+				}
+			}
+			if (isatty(fileno(stdin)) == 0)
+				putchar(input);
+		}
+		fprintf(stdout, "\nThanks!\n");
+	}
+
+	/* Now output the listing */
+	cur_patch = STAILQ_FIRST(&patches);
+	for (cur_instr = STAILQ_FIRST(&seq_program);
+	     cur_instr != NULL;
+	     cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) {
+
+		if (check_patch(&cur_patch, instrcount,
+				&skip_addr, func_values) == 0) {
+			/* Don't count this instruction as it is in a patch
+			 * that was removed.
+			 */
+                        continue;
+		}
+
+		while (line < cur_instr->srcline) {
+			fgets(buf, sizeof(buf), ifile);
+				fprintf(listfile, "             \t%s", buf);
+				line++;
+		}
+		fprintf(listfile, "%04x %02x%02x%02x%02x", instrptr,
+#ifdef __LITTLE_ENDIAN
+			cur_instr->format.bytes[0],
+			cur_instr->format.bytes[1],
+			cur_instr->format.bytes[2],
+			cur_instr->format.bytes[3]);
+#else
+			cur_instr->format.bytes[3],
+			cur_instr->format.bytes[2],
+			cur_instr->format.bytes[1],
+			cur_instr->format.bytes[0]);
+#endif
+		/*
+		 * Macro expansions can cause several instructions
+		 * to be output for a single source line.  Only
+		 * advance the line once in these cases.
+		 */
+		if (line == cur_instr->srcline) {
+			fgets(buf, sizeof(buf), ifile);
+			fprintf(listfile, "\t%s", buf);
+			line++;
+		} else {
+			fprintf(listfile, "\n");
+		}
+		instrptr++;
+	}
+	/* Dump the remainder of the file */
+	while(fgets(buf, sizeof(buf), ifile) != NULL)
+		fprintf(listfile, "             %s", buf);
+
+	fclose(ifile);
+}
+
+static int
+check_patch(patch_t **start_patch, int start_instr,
+	    int *skip_addr, int *func_vals)
+{
+	patch_t *cur_patch;
+
+	cur_patch = *start_patch;
+
+	while (cur_patch != NULL && start_instr == cur_patch->begin) {
+		if (func_vals[cur_patch->patch_func] == 0) {
+			int skip;
+
+			/* Start rejecting code */
+			*skip_addr = start_instr + cur_patch->skip_instr;
+			for (skip = cur_patch->skip_patch;
+			     skip > 0 && cur_patch != NULL;
+			     skip--)
+				cur_patch = STAILQ_NEXT(cur_patch, links);
+		} else {
+			/* Accepted this patch.  Advance to the next
+			 * one and wait for our intruction pointer to
+			 * hit this point.
+			 */
+			cur_patch = STAILQ_NEXT(cur_patch, links);
+		}
+	}
+
+	*start_patch = cur_patch;
+	if (start_instr < *skip_addr)
+		/* Still skipping */
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Print out error information if appropriate, and clean up before
+ * terminating the program.
+ */
+void
+stop(const char *string, int err_code)
+{
+	if (string != NULL) {
+		fprintf(stderr, "%s: ", appname);
+		if (yyfilename != NULL) {
+			fprintf(stderr, "Stopped at file %s, line %d - ",
+				yyfilename, yylineno);
+		}
+		fprintf(stderr, "%s\n", string);
+	}
+
+	if (ofile != NULL) {
+		fclose(ofile);
+		if (err_code != 0) {
+			fprintf(stderr, "%s: Removing %s due to error\n",
+				appname, ofilename);
+			unlink(ofilename);
+		}
+	}
+
+	if (regfile != NULL) {
+		fclose(regfile);
+		if (err_code != 0) {
+			fprintf(stderr, "%s: Removing %s due to error\n",
+				appname, regfilename);
+			unlink(regfilename);
+		}
+	}
+
+	if (listfile != NULL) {
+		fclose(listfile);
+		if (err_code != 0) {
+			fprintf(stderr, "%s: Removing %s due to error\n",
+				appname, listfilename);
+			unlink(listfilename);
+		}
+	}
+
+	symlist_free(&patch_functions);
+	symtable_close();
+
+	exit(err_code);
+}
+
+struct instruction *
+seq_alloc()
+{
+	struct instruction *new_instr;
+
+	new_instr = (struct instruction *)malloc(sizeof(struct instruction));
+	if (new_instr == NULL)
+		stop("Unable to malloc instruction object", EX_SOFTWARE);
+	memset(new_instr, 0, sizeof(*new_instr));
+	STAILQ_INSERT_TAIL(&seq_program, new_instr, links);
+	new_instr->srcline = yylineno;
+	return new_instr;
+}
+
+critical_section_t *
+cs_alloc()
+{
+	critical_section_t *new_cs;
+
+	new_cs= (critical_section_t *)malloc(sizeof(critical_section_t));
+	if (new_cs == NULL)
+		stop("Unable to malloc critical_section object", EX_SOFTWARE);
+	memset(new_cs, 0, sizeof(*new_cs));
+	
+	TAILQ_INSERT_TAIL(&cs_tailq, new_cs, links);
+	return new_cs;
+}
+
+scope_t *
+scope_alloc()
+{
+	scope_t *new_scope;
+
+	new_scope = (scope_t *)malloc(sizeof(scope_t));
+	if (new_scope == NULL)
+		stop("Unable to malloc scope object", EX_SOFTWARE);
+	memset(new_scope, 0, sizeof(*new_scope));
+	TAILQ_INIT(&new_scope->inner_scope);
+	
+	if (SLIST_FIRST(&scope_stack) != NULL) {
+		TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope,
+				  new_scope, scope_links);
+	}
+	/* This patch is now the current scope */
+	SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links);
+	return new_scope;
+}
+
+void
+process_scope(scope_t *scope)
+{
+	/*
+	 * We are "leaving" this scope.  We should now have
+	 * enough information to process the lists of scopes
+	 * we encapsulate.
+	 */
+	scope_t *cur_scope;
+	u_int skip_patch_count;
+	u_int skip_instr_count;
+
+	cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq);
+	skip_patch_count = 0;
+	skip_instr_count = 0;
+	while (cur_scope != NULL) {
+		u_int patch0_patch_skip;
+
+		patch0_patch_skip = 0;
+		switch (cur_scope->type) {
+		case SCOPE_IF:
+		case SCOPE_ELSE_IF:
+			if (skip_instr_count != 0) {
+				/* Create a tail patch */
+				patch0_patch_skip++;
+				cur_scope->patches[1].skip_patch =
+				    skip_patch_count + 1;
+				cur_scope->patches[1].skip_instr =
+				    skip_instr_count;
+			}
+
+			/* Count Head patch */
+			patch0_patch_skip++;
+
+			/* Count any patches contained in our inner scope */
+			patch0_patch_skip += cur_scope->inner_scope_patches;
+
+			cur_scope->patches[0].skip_patch = patch0_patch_skip;
+			cur_scope->patches[0].skip_instr =
+			    cur_scope->end_addr - cur_scope->begin_addr;
+
+			skip_instr_count += cur_scope->patches[0].skip_instr;
+
+			skip_patch_count += patch0_patch_skip;
+			if (cur_scope->type == SCOPE_IF) {
+				scope->inner_scope_patches += skip_patch_count;
+				skip_patch_count = 0;
+			        skip_instr_count = 0;
+			}
+			break;
+		case SCOPE_ELSE:
+			/* Count any patches contained in our innter scope */
+			skip_patch_count += cur_scope->inner_scope_patches;
+
+			skip_instr_count += cur_scope->end_addr
+					  - cur_scope->begin_addr;
+			break;
+		case SCOPE_ROOT:
+			stop("Unexpected scope type encountered", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+
+		cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links);
+	}
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm.h b/drivers/scsi/aic7xxx/aicasm/aicasm.h
new file mode 100644
index 0000000..51678dd
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.h
@@ -0,0 +1,95 @@
+/*
+ * Assembler for the sequencer program downloaded to Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.h#14 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef struct path_entry {
+	char	*directory;
+	int	quoted_includes_only;
+	SLIST_ENTRY(path_entry) links;
+} *path_entry_t;
+
+typedef enum {  
+	QUOTED_INCLUDE,
+	BRACKETED_INCLUDE,
+	SOURCE_FILE
+} include_type;
+
+SLIST_HEAD(path_list, path_entry);
+
+extern struct path_list search_path;
+extern struct cs_tailq cs_tailq;
+extern struct scope_list scope_stack;
+extern struct symlist patch_functions;
+extern int includes_search_curdir;		/* False if we've seen -I- */
+extern char *appname;
+extern char *stock_include_file;
+extern int yylineno;
+extern char *yyfilename;
+extern char *prefix;
+extern char *patch_arg_list;
+extern char *versions;
+extern int   src_mode;
+extern int   dst_mode;
+struct symbol;
+
+void stop(const char *errstring, int err_code);
+void include_file(char *file_name, include_type type);
+void expand_macro(struct symbol *macro_symbol);
+struct instruction *seq_alloc(void);
+struct critical_section *cs_alloc(void);
+struct scope *scope_alloc(void);
+void process_scope(struct scope *);
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
new file mode 100644
index 0000000..f1586a4
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
@@ -0,0 +1,2004 @@
+%{
+/*
+ * Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_gram.y#30 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+int yylineno;
+char *yyfilename;
+char stock_prefix[] = "aic_";
+char *prefix = stock_prefix;
+char *patch_arg_list;
+char *versions;
+static char errbuf[255];
+static char regex_pattern[255];
+static symbol_t *cur_symbol;
+static symbol_t *field_symbol;
+static symbol_t *scb_or_sram_symbol;
+static symtype cur_symtype;
+static symbol_ref_t accumulator;
+static symbol_ref_t mode_ptr;
+static symbol_ref_t allones;
+static symbol_ref_t allzeros;
+static symbol_ref_t none;
+static symbol_ref_t sindex;
+static int instruction_ptr;
+static int num_srams;
+static int sram_or_scb_offset;
+static int download_constant_count;
+static int in_critical_section;
+static u_int enum_increment;
+static u_int enum_next_value;
+
+static void process_field(int field_type, symbol_t *sym, int mask);
+static void initialize_symbol(symbol_t *symbol);
+static void add_macro_arg(const char *argtext, int position);
+static void add_macro_body(const char *bodytext);
+static void process_register(symbol_t **p_symbol);
+static void format_1_instr(int opcode, symbol_ref_t *dest,
+			   expression_t *immed, symbol_ref_t *src, int ret);
+static void format_2_instr(int opcode, symbol_ref_t *dest,
+			   expression_t *places, symbol_ref_t *src, int ret);
+static void format_3_instr(int opcode, symbol_ref_t *src,
+			   expression_t *immed, symbol_ref_t *address);
+static void test_readable_symbol(symbol_t *symbol);
+static void test_writable_symbol(symbol_t *symbol);
+static void type_check(symbol_ref_t *sym, expression_t *expression, int and_op);
+static void make_expression(expression_t *immed, int value);
+static void add_conditional(symbol_t *symbol);
+static void add_version(const char *verstring);
+static int  is_download_const(expression_t *immed);
+static int  is_location_address(symbol_t *symbol);
+void yyerror(const char *string);
+
+#define SRAM_SYMNAME "SRAM_BASE"
+#define SCB_SYMNAME "SCB_BASE"
+%}
+
+%union {
+	u_int		value;
+	char		*str;
+	symbol_t	*sym;
+	symbol_ref_t	sym_ref;
+	expression_t	expression;
+}
+
+%token T_REGISTER
+
+%token <value> T_CONST
+
+%token T_EXPORT
+
+%token T_DOWNLOAD
+
+%token T_SCB
+
+%token T_SRAM
+
+%token T_ALIAS
+
+%token T_SIZE
+
+%token T_EXPR_LSHIFT
+
+%token T_EXPR_RSHIFT
+
+%token <value> T_ADDRESS
+
+%token T_COUNT
+
+%token T_ACCESS_MODE
+
+%token T_DONT_GENERATE_DEBUG_CODE
+
+%token T_MODES
+
+%token T_DEFINE
+
+%token T_SET_SRC_MODE
+
+%token T_SET_DST_MODE
+
+%token <value> T_MODE
+
+%token T_BEGIN_CS
+
+%token T_END_CS
+
+%token T_PAD_PAGE
+
+%token T_FIELD
+
+%token T_ENUM
+
+%token T_MASK
+
+%token <value> T_NUMBER
+
+%token <str> T_PATH T_STRING T_ARG T_MACROBODY
+
+%token <sym> T_CEXPR
+
+%token T_EOF T_INCLUDE T_VERSION T_PREFIX T_PATCH_ARG_LIST
+
+%token <value> T_SHR T_SHL T_ROR T_ROL
+
+%token <value> T_MVI T_MOV T_CLR T_BMOV
+
+%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
+
+%token <value> T_ADD T_ADC
+
+%token <value> T_INC T_DEC
+
+%token <value> T_STC T_CLC
+
+%token <value> T_CMP T_NOT T_XOR
+
+%token <value> T_TEST T_AND
+
+%token <value> T_OR
+
+/* 16 bit extensions, not implemented
+ * %token <value> T_OR16 T_AND16 T_XOR16 T_ADD16
+ * %token <value> T_ADC16 T_MVI16 T_TEST16 T_CMP16 T_CMPXCHG
+ */
+%token T_RET
+
+%token T_NOP
+
+%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX T_MODE_PTR
+
+%token T_A
+
+%token <sym> T_SYMBOL
+
+%token T_NL
+
+%token T_IF T_ELSE T_ELSE_IF T_ENDIF
+
+%type <sym_ref> reg_symbol address destination source opt_source
+
+%type <expression> expression immediate immediate_or_a
+
+%type <value> export ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
+
+%type <value> mode_value mode_list macro_arglist
+
+%left '|'
+%left '&'
+%left T_EXPR_LSHIFT T_EXPR_RSHIFT
+%left '+' '-'
+%left '*' '/'
+%right '~'
+%nonassoc UMINUS
+%%
+
+program:
+	include
+|	program include
+|	prefix
+|	program prefix
+|	patch_arg_list
+|	program patch_arg_list
+|	version
+|	program version
+|	register
+|	program register
+|	constant
+|	program constant
+|	macrodefn
+|	program macrodefn
+|	scratch_ram
+|	program scratch_ram
+|	scb
+|	program scb
+|	label
+|	program label
+|	set_src_mode
+|	program set_src_mode
+|	set_dst_mode
+|	program set_dst_mode
+|	critical_section_start
+|	program critical_section_start
+|	critical_section_end
+|	program critical_section_end
+|	conditional
+|	program conditional
+|	code
+|	program code
+;
+
+include:
+	T_INCLUDE '<' T_PATH '>'
+	{
+		include_file($3, BRACKETED_INCLUDE);
+	}
+|	T_INCLUDE '"' T_PATH '"'
+	{
+		include_file($3, QUOTED_INCLUDE);
+	}
+;
+
+prefix:
+	T_PREFIX '=' T_STRING
+	{
+		if (prefix != stock_prefix)
+			stop("Prefix multiply defined",
+			     EX_DATAERR);
+		prefix = strdup($3);
+		if (prefix == NULL)
+			stop("Unable to record prefix", EX_SOFTWARE);
+	}
+;
+
+patch_arg_list:
+	T_PATCH_ARG_LIST '=' T_STRING
+	{
+		if (patch_arg_list != NULL)
+			stop("Patch argument list multiply defined",
+			     EX_DATAERR);
+		patch_arg_list = strdup($3);
+		if (patch_arg_list == NULL)
+			stop("Unable to record patch arg list", EX_SOFTWARE);
+	}
+;
+
+version:
+	T_VERSION '=' T_STRING
+	{ add_version($3); }
+;
+
+register:
+	T_REGISTER { cur_symtype = REGISTER; } reg_definition
+;
+
+reg_definition:
+	T_SYMBOL '{'
+		{
+			if ($1->type != UNINITIALIZED) {
+				stop("Register multiply defined", EX_DATAERR);
+				/* NOTREACHED */
+			}
+			cur_symbol = $1;
+			cur_symbol->type = cur_symtype;
+			initialize_symbol(cur_symbol);
+		}
+		reg_attribute_list
+	'}'
+		{
+			/*
+			 * Default to allowing everything in for registers
+			 * with no bit or mask definitions.
+			 */
+			if (cur_symbol->info.rinfo->valid_bitmask == 0)
+				cur_symbol->info.rinfo->valid_bitmask = 0xFF;
+
+			if (cur_symbol->info.rinfo->size == 0)
+				cur_symbol->info.rinfo->size = 1;
+
+			/*
+			 * This might be useful for registers too.
+			 */
+			if (cur_symbol->type != REGISTER) {
+				if (cur_symbol->info.rinfo->address == 0)
+					cur_symbol->info.rinfo->address =
+					    sram_or_scb_offset;
+				sram_or_scb_offset +=
+				    cur_symbol->info.rinfo->size;
+			}
+			cur_symbol = NULL;
+		}
+;
+
+reg_attribute_list:
+	reg_attribute
+|	reg_attribute_list reg_attribute
+;
+
+reg_attribute:
+	reg_address
+|	size
+|	count
+|	access_mode
+|	dont_generate_debug_code
+|	modes
+|	field_defn
+|	enum_defn
+|	mask_defn
+|	alias
+|	accumulator
+|	mode_pointer
+|	allones
+|	allzeros
+|	none
+|	sindex
+;
+
+reg_address:
+	T_ADDRESS T_NUMBER
+	{
+		cur_symbol->info.rinfo->address = $2;
+	}
+;
+
+size:
+	T_SIZE T_NUMBER
+	{
+		cur_symbol->info.rinfo->size = $2;
+		if (scb_or_sram_symbol != NULL) {
+			u_int max_addr;
+			u_int sym_max_addr;
+
+			max_addr = scb_or_sram_symbol->info.rinfo->address
+				 + scb_or_sram_symbol->info.rinfo->size;
+			sym_max_addr = cur_symbol->info.rinfo->address
+				     + cur_symbol->info.rinfo->size;
+
+			if (sym_max_addr > max_addr)
+				stop("SCB or SRAM space exhausted", EX_DATAERR);
+		}
+	}
+;
+
+count:
+	T_COUNT T_NUMBER
+	{
+		cur_symbol->count += $2;
+	}
+;
+
+access_mode:
+	T_ACCESS_MODE T_MODE
+	{
+		cur_symbol->info.rinfo->mode = $2;
+	}
+;
+
+dont_generate_debug_code:
+	T_DONT_GENERATE_DEBUG_CODE
+	{
+		cur_symbol->dont_generate_debug_code = 1;
+	}
+;
+
+modes:
+	T_MODES mode_list
+	{
+		cur_symbol->info.rinfo->modes = $2;
+	}
+;
+
+mode_list:
+	mode_value
+	{
+		$$ = $1;
+	}
+|	mode_list ',' mode_value
+	{
+		$$ = $1 | $3;
+	}
+;
+
+mode_value:
+	T_NUMBER
+	{
+		if ($1 > 4) {
+			stop("Valid register modes range between 0 and 4.",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+
+		$$ = (0x1 << $1);
+	}
+|	T_SYMBOL
+	{
+		symbol_t *symbol;
+
+		symbol = $1;
+		if (symbol->type != CONST) {
+			stop("Only \"const\" symbols allowed in "
+			     "mode definitions.", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		if (symbol->info.cinfo->value > 4) {
+			stop("Valid register modes range between 0 and 4.",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$ = (0x1 << symbol->info.cinfo->value);
+	}
+;
+
+field_defn:
+	T_FIELD
+		{
+			field_symbol = NULL;
+			enum_next_value = 0;
+			enum_increment = 1;
+		}
+	'{' enum_entry_list '}'
+|	T_FIELD T_SYMBOL expression
+		{
+			process_field(FIELD, $2, $3.value);
+			field_symbol = $2;
+			enum_next_value = 0;
+			enum_increment = 0x01 << (ffs($3.value) - 1);
+		}
+	'{' enum_entry_list '}'
+|	T_FIELD T_SYMBOL expression
+	{
+		process_field(FIELD, $2, $3.value);
+	}
+;
+
+enum_defn:
+	T_ENUM
+		{
+			field_symbol = NULL;
+			enum_next_value = 0;
+			enum_increment = 1;
+		}
+	'{' enum_entry_list '}'
+|	T_ENUM T_SYMBOL expression
+		{
+			process_field(ENUM, $2, $3.value);
+			field_symbol = $2;
+			enum_next_value = 0;
+			enum_increment = 0x01 << (ffs($3.value) - 1);
+		}
+	'{' enum_entry_list '}'
+;
+
+enum_entry_list:
+	enum_entry
+|	enum_entry_list ',' enum_entry
+;
+
+enum_entry:
+	T_SYMBOL
+	{
+		process_field(ENUM_ENTRY, $1, enum_next_value);
+		enum_next_value += enum_increment;
+	}
+|	T_SYMBOL expression
+	{
+		process_field(ENUM_ENTRY, $1, $2.value);
+		enum_next_value = $2.value + enum_increment;
+	}
+;
+
+mask_defn:
+	T_MASK T_SYMBOL expression
+	{
+		process_field(MASK, $2, $3.value);
+	}
+;
+
+alias:
+	T_ALIAS	T_SYMBOL
+	{
+		if ($2->type != UNINITIALIZED) {
+			stop("Re-definition of register alias",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$2->type = ALIAS;
+		initialize_symbol($2);
+		$2->info.ainfo->parent = cur_symbol;
+	}
+;
+
+accumulator:
+	T_ACCUM
+	{
+		if (accumulator.symbol != NULL) {
+			stop("Only one accumulator definition allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		accumulator.symbol = cur_symbol;
+	}
+;
+
+mode_pointer:
+	T_MODE_PTR
+	{
+		if (mode_ptr.symbol != NULL) {
+			stop("Only one mode pointer definition allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		mode_ptr.symbol = cur_symbol;
+	}
+;
+
+allones:
+	T_ALLONES
+	{
+		if (allones.symbol != NULL) {
+			stop("Only one definition of allones allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		allones.symbol = cur_symbol;
+	}
+;
+
+allzeros:
+	T_ALLZEROS
+	{
+		if (allzeros.symbol != NULL) {
+			stop("Only one definition of allzeros allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		allzeros.symbol = cur_symbol;
+	}
+;
+
+none:
+	T_NONE
+	{
+		if (none.symbol != NULL) {
+			stop("Only one definition of none allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		none.symbol = cur_symbol;
+	}
+;
+
+sindex:
+	T_SINDEX
+	{
+		if (sindex.symbol != NULL) {
+			stop("Only one definition of sindex allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		sindex.symbol = cur_symbol;
+	}
+;
+
+expression:
+	expression '|' expression
+	{
+		 $$.value = $1.value | $3.value;
+		 symlist_merge(&$$.referenced_syms,
+			       &$1.referenced_syms,
+			       &$3.referenced_syms);
+	}
+|	expression '&' expression
+	{
+		$$.value = $1.value & $3.value;
+		symlist_merge(&$$.referenced_syms,
+			       &$1.referenced_syms,
+			       &$3.referenced_syms);
+	}
+|	expression '+' expression
+	{
+		$$.value = $1.value + $3.value;
+		symlist_merge(&$$.referenced_syms,
+			       &$1.referenced_syms,
+			       &$3.referenced_syms);
+	}
+|	expression '-' expression
+	{
+		$$.value = $1.value - $3.value;
+		symlist_merge(&($$.referenced_syms),
+			       &($1.referenced_syms),
+			       &($3.referenced_syms));
+	}
+|	expression '*' expression
+	{
+		$$.value = $1.value * $3.value;
+		symlist_merge(&($$.referenced_syms),
+			       &($1.referenced_syms),
+			       &($3.referenced_syms));
+	}
+|	expression '/' expression
+	{
+		$$.value = $1.value / $3.value;
+		symlist_merge(&($$.referenced_syms),
+			       &($1.referenced_syms),
+			       &($3.referenced_syms));
+	}
+|	expression T_EXPR_LSHIFT expression
+	{
+		$$.value = $1.value << $3.value;
+		symlist_merge(&$$.referenced_syms,
+			       &$1.referenced_syms,
+			       &$3.referenced_syms);
+	}
+|	expression T_EXPR_RSHIFT expression
+	{
+		$$.value = $1.value >> $3.value;
+		symlist_merge(&$$.referenced_syms,
+			       &$1.referenced_syms,
+			       &$3.referenced_syms);
+	}
+|	'(' expression ')'
+	{
+		$$ = $2;
+	}
+|	'~' expression
+	{
+		$$ = $2;
+		$$.value = (~$$.value) & 0xFF;
+	}
+|	'-' expression %prec UMINUS
+	{
+		$$ = $2;
+		$$.value = -$$.value;
+	}
+|	T_NUMBER
+	{
+		$$.value = $1;
+		SLIST_INIT(&$$.referenced_syms);
+	}
+|	T_SYMBOL
+	{
+		symbol_t *symbol;
+
+		symbol = $1;
+		switch (symbol->type) {
+		case ALIAS:
+			symbol = $1->info.ainfo->parent;
+		case REGISTER:
+		case SCBLOC:
+		case SRAMLOC:
+			$$.value = symbol->info.rinfo->address;
+			break;
+		case MASK:
+		case FIELD:
+		case ENUM:
+		case ENUM_ENTRY:
+			$$.value = symbol->info.finfo->value;
+			break;
+		case DOWNLOAD_CONST:
+		case CONST:
+			$$.value = symbol->info.cinfo->value;
+			break;
+		case UNINITIALIZED:
+		default:
+		{
+			snprintf(errbuf, sizeof(errbuf),
+				 "Undefined symbol %s referenced",
+				 symbol->name);
+			stop(errbuf, EX_DATAERR);
+			/* NOTREACHED */
+			break;
+		}
+		}
+		SLIST_INIT(&$$.referenced_syms);
+		symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD);
+	}
+;
+
+constant:
+	T_CONST T_SYMBOL expression
+	{
+		if ($2->type != UNINITIALIZED) {
+			stop("Re-definition of symbol as a constant",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$2->type = CONST;
+		initialize_symbol($2);
+		$2->info.cinfo->value = $3.value;
+	}
+|	T_CONST T_SYMBOL T_DOWNLOAD
+	{
+		if ($1) {
+			stop("Invalid downloaded constant declaration",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		if ($2->type != UNINITIALIZED) {
+			stop("Re-definition of symbol as a downloaded constant",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$2->type = DOWNLOAD_CONST;
+		initialize_symbol($2);
+		$2->info.cinfo->value = download_constant_count++;
+	}
+;
+
+macrodefn_prologue:
+	T_DEFINE T_SYMBOL
+	{
+		if ($2->type != UNINITIALIZED) {
+			stop("Re-definition of symbol as a macro",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		cur_symbol = $2;
+		cur_symbol->type = MACRO;
+		initialize_symbol(cur_symbol);
+	}
+;
+
+macrodefn:
+	macrodefn_prologue T_MACROBODY
+	{
+		add_macro_body($2);
+	}
+|	macrodefn_prologue '(' macro_arglist ')' T_MACROBODY
+	{
+		add_macro_body($5);
+		cur_symbol->info.macroinfo->narg = $3;
+	}
+;
+
+macro_arglist:
+	{
+		/* Macros can take no arguments */
+		$$ = 0;
+	}
+|	T_ARG
+	{
+		$$ = 1;
+		add_macro_arg($1, 0);
+	}
+|	macro_arglist ',' T_ARG
+	{
+		if ($1 == 0) {
+			stop("Comma without preceding argument in arg list",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$ = $1 + 1;
+		add_macro_arg($3, $1);
+	}
+;
+
+scratch_ram:
+	T_SRAM '{'
+		{
+			snprintf(errbuf, sizeof(errbuf), "%s%d", SRAM_SYMNAME,
+				 num_srams);
+			cur_symbol = symtable_get(SRAM_SYMNAME);
+			cur_symtype = SRAMLOC;
+			cur_symbol->type = SRAMLOC;
+			initialize_symbol(cur_symbol);
+			cur_symbol->count += 1;
+		}
+		reg_address
+		{
+			sram_or_scb_offset = cur_symbol->info.rinfo->address;
+		}
+		size
+		{
+			scb_or_sram_symbol = cur_symbol;
+		}
+		scb_or_sram_attributes
+	'}'
+		{
+			cur_symbol = NULL;
+			scb_or_sram_symbol = NULL;
+		}
+;
+
+scb:
+	T_SCB '{'
+		{
+			cur_symbol = symtable_get(SCB_SYMNAME);
+			cur_symtype = SCBLOC;
+			if (cur_symbol->type != UNINITIALIZED) {
+				stop("Only one SRAM definition allowed",
+				     EX_SOFTWARE);
+				/* NOTREACHED */
+			}
+			cur_symbol->type = SCBLOC;
+			initialize_symbol(cur_symbol);
+			/* 64 bytes of SCB space */
+			cur_symbol->info.rinfo->size = 64;
+			cur_symbol->count += 1;
+		}
+		reg_address
+		{
+			sram_or_scb_offset = cur_symbol->info.rinfo->address;
+		}
+		size
+		{
+			scb_or_sram_symbol = cur_symbol;
+		}
+		scb_or_sram_attributes
+	'}'
+		{
+			cur_symbol = NULL;
+			scb_or_sram_symbol = NULL;
+		}
+;
+
+scb_or_sram_attributes:
+	/* NULL definition is okay */
+|	modes
+|	scb_or_sram_reg_list
+|	modes scb_or_sram_reg_list
+;
+
+scb_or_sram_reg_list:
+	reg_definition
+|	scb_or_sram_reg_list reg_definition
+;
+
+reg_symbol:
+	T_SYMBOL
+	{
+		process_register(&$1);
+		$$.symbol = $1;
+		$$.offset = 0;
+	}
+|	T_SYMBOL '[' T_SYMBOL ']'
+	{
+		process_register(&$1);
+		if ($3->type != CONST) {
+			stop("register offset must be a constant", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		if (($3->info.cinfo->value + 1) > $1->info.rinfo->size) {
+			stop("Accessing offset beyond range of register",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$.symbol = $1;
+		$$.offset = $3->info.cinfo->value;
+	}
+|	T_SYMBOL '[' T_NUMBER ']'
+	{
+		process_register(&$1);
+		if (($3 + 1) > $1->info.rinfo->size) {
+			stop("Accessing offset beyond range of register",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$.symbol = $1;
+		$$.offset = $3;
+	}
+|	T_A
+	{
+		if (accumulator.symbol == NULL) {
+			stop("No accumulator has been defined", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$.symbol = accumulator.symbol;
+		$$.offset = 0;
+	}
+;
+
+destination:
+	reg_symbol
+	{
+		test_writable_symbol($1.symbol);
+		$$ = $1;
+	}
+;
+
+immediate:
+	expression
+	{ $$ = $1; }
+;
+
+immediate_or_a:
+	expression
+	{
+		if ($1.value == 0 && is_download_const(&$1) == 0) {
+			snprintf(errbuf, sizeof(errbuf),
+				 "\nExpression evaluates to 0 and thus "
+				 "references the accumulator.\n "
+				 "If this is the desired effect, use 'A' "
+				 "instead.\n");
+			stop(errbuf, EX_DATAERR);
+		}
+		$$ = $1;
+	}
+|	T_A
+	{
+		SLIST_INIT(&$$.referenced_syms);
+		symlist_add(&$$.referenced_syms, accumulator.symbol,
+			    SYMLIST_INSERT_HEAD);
+		$$.value = 0;
+	}
+;
+
+source:
+	reg_symbol
+	{
+		test_readable_symbol($1.symbol);
+		$$ = $1;
+	}
+;
+
+opt_source:
+	{
+		$$.symbol = NULL;
+		$$.offset = 0;
+	}
+|	',' source
+	{ $$ = $2; }
+;
+
+ret:
+	{ $$ = 0; }
+|	T_RET
+	{ $$ = 1; }
+;
+
+set_src_mode:
+	T_SET_SRC_MODE T_NUMBER ';'
+	{
+		src_mode = $2;
+	}
+;
+
+set_dst_mode:
+	T_SET_DST_MODE T_NUMBER ';'
+	{
+		dst_mode = $2;
+	}
+;
+
+critical_section_start:
+	T_BEGIN_CS ';'
+	{
+		critical_section_t *cs;
+
+		if (in_critical_section != FALSE) {
+			stop("Critical Section within Critical Section",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		cs = cs_alloc();
+		cs->begin_addr = instruction_ptr;
+		in_critical_section = TRUE;
+	}
+;
+
+critical_section_end:
+	T_END_CS ';'
+	{
+		critical_section_t *cs;
+
+		if (in_critical_section == FALSE) {
+			stop("Unballanced 'end_cs'", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		cs = TAILQ_LAST(&cs_tailq, cs_tailq);
+		cs->end_addr = instruction_ptr;
+		in_critical_section = FALSE;
+	}
+;
+
+export:
+	{ $$ = 0; }
+|	T_EXPORT
+	{ $$ = 1; }
+;
+
+label:
+	export T_SYMBOL ':'
+	{
+		if ($2->type != UNINITIALIZED) {
+			stop("Program label multiply defined", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$2->type = LABEL;
+		initialize_symbol($2);
+		$2->info.linfo->address = instruction_ptr;
+		$2->info.linfo->exported = $1;
+	}
+;
+
+address:
+	T_SYMBOL
+	{
+		$$.symbol = $1;
+		$$.offset = 0;
+	}
+|	T_SYMBOL '+' T_NUMBER
+	{
+		$$.symbol = $1;
+		$$.offset = $3;
+	}
+|	T_SYMBOL '-' T_NUMBER
+	{
+		$$.symbol = $1;
+		$$.offset = -$3;
+	}
+|	'.'
+	{
+		$$.symbol = NULL;
+		$$.offset = 0;
+	}
+|	'.' '+' T_NUMBER
+	{
+		$$.symbol = NULL;
+		$$.offset = $3;
+	}
+|	'.' '-' T_NUMBER
+	{
+		$$.symbol = NULL;
+		$$.offset = -$3;
+	}
+;
+
+conditional:
+	T_IF T_CEXPR '{'
+	{
+		scope_t *new_scope;
+
+		add_conditional($2);
+		new_scope = scope_alloc();
+		new_scope->type = SCOPE_IF;
+		new_scope->begin_addr = instruction_ptr;
+		new_scope->func_num = $2->info.condinfo->func_num;
+	}
+|	T_ELSE T_IF T_CEXPR '{'
+	{
+		scope_t *new_scope;
+		scope_t *scope_context;
+		scope_t *last_scope;
+
+		/*
+		 * Ensure that the previous scope is either an
+		 * if or and else if.
+		 */
+		scope_context = SLIST_FIRST(&scope_stack);
+		last_scope = TAILQ_LAST(&scope_context->inner_scope,
+					scope_tailq);
+		if (last_scope == NULL
+		 || last_scope->type == T_ELSE) {
+
+			stop("'else if' without leading 'if'", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		add_conditional($3);
+		new_scope = scope_alloc();
+		new_scope->type = SCOPE_ELSE_IF;
+		new_scope->begin_addr = instruction_ptr;
+		new_scope->func_num = $3->info.condinfo->func_num;
+	}
+|	T_ELSE '{'
+	{
+		scope_t *new_scope;
+		scope_t *scope_context;
+		scope_t *last_scope;
+
+		/*
+		 * Ensure that the previous scope is either an
+		 * if or and else if.
+		 */
+		scope_context = SLIST_FIRST(&scope_stack);
+		last_scope = TAILQ_LAST(&scope_context->inner_scope,
+					scope_tailq);
+		if (last_scope == NULL
+		 || last_scope->type == SCOPE_ELSE) {
+
+			stop("'else' without leading 'if'", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		new_scope = scope_alloc();
+		new_scope->type = SCOPE_ELSE;
+		new_scope->begin_addr = instruction_ptr;
+	}
+;
+
+conditional:
+	'}'
+	{
+		scope_t *scope_context;
+
+		scope_context = SLIST_FIRST(&scope_stack);
+		if (scope_context->type == SCOPE_ROOT) {
+			stop("Unexpected '}' encountered", EX_DATAERR);
+			/* NOTREACHED */
+		}
+
+		scope_context->end_addr = instruction_ptr;
+
+		/* Pop the scope */
+		SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links);
+
+		process_scope(scope_context);
+
+		if (SLIST_FIRST(&scope_stack) == NULL) {
+			stop("Unexpected '}' encountered", EX_DATAERR);
+			/* NOTREACHED */
+		}
+	}
+;
+
+f1_opcode:
+	T_AND { $$ = AIC_OP_AND; }
+|	T_XOR { $$ = AIC_OP_XOR; }
+|	T_ADD { $$ = AIC_OP_ADD; }
+|	T_ADC { $$ = AIC_OP_ADC; }
+;
+
+code:
+	f1_opcode destination ',' immediate_or_a opt_source ret ';'
+	{
+		format_1_instr($1, &$2, &$4, &$5, $6);
+	}
+;
+
+code:
+	T_OR reg_symbol ',' immediate_or_a opt_source ret ';'
+	{
+		format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6);
+	}
+;
+
+code:
+	T_INC destination opt_source ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 1);
+		format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+	}
+;
+
+code:
+	T_DEC destination opt_source ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, -1);
+		format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+	}
+;
+
+code:
+	T_CLC ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, -1);
+		format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2);
+	}
+|	T_CLC T_MVI destination ',' immediate_or_a ret ';'
+	{
+		format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6);
+	}
+;
+
+code:
+	T_STC ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 1);
+		format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2);
+	}
+|	T_STC destination ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 1);
+		format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3);
+	}
+;
+
+code:
+	T_BMOV destination ',' source ',' immediate ret ';'
+	{
+		format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
+	}
+;
+
+code:
+	T_MOV destination ',' source ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 1);
+		format_1_instr(AIC_OP_BMOV, &$2, &immed, &$4, $5);
+	}
+;
+
+code:
+	T_MVI destination ',' immediate ret ';'
+	{
+		if ($4.value == 0
+		 && is_download_const(&$4) == 0) {
+			expression_t immed;
+
+			/*
+			 * Allow move immediates of 0 so that macros,
+			 * that can't know the immediate's value and
+			 * otherwise compensate, still work.
+			 */
+			make_expression(&immed, 1);
+			format_1_instr(AIC_OP_BMOV, &$2, &immed, &allzeros, $5);
+		} else {
+			format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
+		}
+	}
+;
+
+code:
+	T_NOT destination opt_source ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0xff);
+		format_1_instr(AIC_OP_XOR, &$2, &immed, &$3, $4);
+	}
+;
+
+code:
+	T_CLR destination ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0xff);
+		format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3);
+	}
+;
+
+code:
+	T_NOP ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0xff);
+		format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2);
+	}
+;
+
+code:
+	T_RET ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0xff);
+		format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE);
+	}
+;
+
+	/*
+	 * This grammar differs from the one in the aic7xxx
+	 * reference manual since the grammar listed there is
+	 * ambiguous and causes a shift/reduce conflict.
+	 * It also seems more logical as the "immediate"
+	 * argument is listed as the second arg like the
+	 * other formats.
+	 */
+
+f2_opcode:
+	T_SHL { $$ = AIC_OP_SHL; }
+|	T_SHR { $$ = AIC_OP_SHR; }
+|	T_ROL { $$ = AIC_OP_ROL; }
+|	T_ROR { $$ = AIC_OP_ROR; }
+;
+
+/*
+ * 16bit opcodes, not used
+ *
+ *f4_opcode:
+ *	T_OR16	{ $$ = AIC_OP_OR16; }
+ *|	T_AND16 { $$ = AIC_OP_AND16; }
+ *|	T_XOR16 { $$ = AIC_OP_XOR16; }
+ *|	T_ADD16 { $$ = AIC_OP_ADD16; }
+ *|	T_ADC16 { $$ = AIC_OP_ADC16; }
+ *|	T_MVI16 { $$ = AIC_OP_MVI16; }
+ *;
+ */
+
+code:
+	f2_opcode destination ',' expression opt_source ret ';'
+	{
+		format_2_instr($1, &$2, &$4, &$5, $6);
+	}
+;
+
+jmp_jc_jnc_call:
+	T_JMP	{ $$ = AIC_OP_JMP; }
+|	T_JC	{ $$ = AIC_OP_JC; }
+|	T_JNC	{ $$ = AIC_OP_JNC; }
+|	T_CALL	{ $$ = AIC_OP_CALL; }
+;
+
+jz_jnz:
+	T_JZ	{ $$ = AIC_OP_JZ; }
+|	T_JNZ	{ $$ = AIC_OP_JNZ; }
+;
+
+je_jne:
+	T_JE	{ $$ = AIC_OP_JE; }
+|	T_JNE	{ $$ = AIC_OP_JNE; }
+;
+
+code:
+	jmp_jc_jnc_call address ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0);
+		format_3_instr($1, &sindex, &immed, &$2);
+	}
+;
+
+code:
+	T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';'
+	{
+		type_check(&$2, &$4, AIC_OP_OR);
+		format_3_instr($5, &$2, &$4, &$6);
+	}
+;
+
+code:
+	T_TEST source ',' immediate_or_a jz_jnz address ';'
+	{
+		format_3_instr($5, &$2, &$4, &$6);
+	}
+;
+
+code:
+	T_CMP source ',' immediate_or_a je_jne address ';'
+	{
+		format_3_instr($5, &$2, &$4, &$6);
+	}
+;
+
+code:
+	T_MOV source jmp_jc_jnc_call address ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0);
+		format_3_instr($3, &$2, &immed, &$4);
+	}
+;
+
+code:
+	T_MVI immediate jmp_jc_jnc_call address ';'
+	{
+		format_3_instr($3, &allzeros, &$2, &$4);
+	}
+;
+
+%%
+
+static void
+process_field(int field_type, symbol_t *sym, int value)
+{
+	/*
+	 * Add the current register to its
+	 * symbol list, if it already exists,
+	 * warn if we are setting it to a
+	 * different value, or in the bit to
+	 * the "allowed bits" of this register.
+	 */
+	if (sym->type == UNINITIALIZED) {
+		sym->type = field_type;
+		initialize_symbol(sym);
+		sym->info.finfo->value = value;
+		if (field_type != ENUM_ENTRY) {
+			if (field_type != MASK && value == 0) {
+				stop("Empty Field, or Enum", EX_DATAERR);
+				/* NOTREACHED */
+			}
+			sym->info.finfo->value = value;
+			sym->info.finfo->mask = value;
+		} else if (field_symbol != NULL) {
+			sym->info.finfo->mask = field_symbol->info.finfo->value;
+		} else {
+			sym->info.finfo->mask = 0xFF;
+		}
+	} else if (sym->type != field_type) {
+		stop("Field definition mirrors a definition of the same "
+		     " name, but a different type", EX_DATAERR);
+		/* NOTREACHED */
+	} else if (value != sym->info.finfo->value) {
+		stop("Field redefined with a conflicting value", EX_DATAERR);
+		/* NOTREACHED */
+	}
+	/* Fail if this symbol is already listed */
+	if (symlist_search(&(sym->info.finfo->symrefs),
+			   cur_symbol->name) != NULL) {
+		stop("Field defined multiple times for register", EX_DATAERR);
+		/* NOTREACHED */
+	}
+	symlist_add(&(sym->info.finfo->symrefs), cur_symbol,
+		    SYMLIST_INSERT_HEAD);
+	cur_symbol->info.rinfo->valid_bitmask |= sym->info.finfo->mask;
+	cur_symbol->info.rinfo->typecheck_masks = TRUE;
+	symlist_add(&(cur_symbol->info.rinfo->fields), sym, SYMLIST_SORT);
+}
+
+static void
+initialize_symbol(symbol_t *symbol)
+{
+	switch (symbol->type) {
+	case UNINITIALIZED:
+		stop("Call to initialize_symbol with type field unset",
+		     EX_SOFTWARE);
+		/* NOTREACHED */
+		break;
+	case REGISTER:
+	case SRAMLOC:
+	case SCBLOC:
+		symbol->info.rinfo =
+		    (struct reg_info *)malloc(sizeof(struct reg_info));
+		if (symbol->info.rinfo == NULL) {
+			stop("Can't create register info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.rinfo, 0,
+		       sizeof(struct reg_info));
+		SLIST_INIT(&(symbol->info.rinfo->fields));
+		/*
+		 * Default to allowing access in all register modes
+		 * or to the mode specified by the SCB or SRAM space
+		 * we are in.
+		 */
+		if (scb_or_sram_symbol != NULL)
+			symbol->info.rinfo->modes =
+			    scb_or_sram_symbol->info.rinfo->modes;
+		else
+			symbol->info.rinfo->modes = ~0;
+		break;
+	case ALIAS:
+		symbol->info.ainfo =
+		    (struct alias_info *)malloc(sizeof(struct alias_info));
+		if (symbol->info.ainfo == NULL) {
+			stop("Can't create alias info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.ainfo, 0,
+		       sizeof(struct alias_info));
+		break;
+	case MASK:
+	case FIELD:
+	case ENUM:
+	case ENUM_ENTRY:
+		symbol->info.finfo =
+		    (struct field_info *)malloc(sizeof(struct field_info));
+		if (symbol->info.finfo == NULL) {
+			stop("Can't create field info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.finfo, 0, sizeof(struct field_info));
+		SLIST_INIT(&(symbol->info.finfo->symrefs));
+		break;
+	case CONST:
+	case DOWNLOAD_CONST:
+		symbol->info.cinfo =
+		    (struct const_info *)malloc(sizeof(struct const_info));
+		if (symbol->info.cinfo == NULL) {
+			stop("Can't create alias info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.cinfo, 0,
+		       sizeof(struct const_info));
+		break;
+	case LABEL:
+		symbol->info.linfo =
+		    (struct label_info *)malloc(sizeof(struct label_info));
+		if (symbol->info.linfo == NULL) {
+			stop("Can't create label info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.linfo, 0,
+		       sizeof(struct label_info));
+		break;
+	case CONDITIONAL:
+		symbol->info.condinfo =
+		    (struct cond_info *)malloc(sizeof(struct cond_info));
+		if (symbol->info.condinfo == NULL) {
+			stop("Can't create conditional info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.condinfo, 0,
+		       sizeof(struct cond_info));
+		break;
+	case MACRO:
+		symbol->info.macroinfo =
+		    (struct macro_info *)malloc(sizeof(struct macro_info));
+		if (symbol->info.macroinfo == NULL) {
+			stop("Can't create macro info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.macroinfo, 0,
+		       sizeof(struct macro_info));
+		STAILQ_INIT(&symbol->info.macroinfo->args);
+		break;
+	default:
+		stop("Call to initialize_symbol with invalid symbol type",
+		     EX_SOFTWARE);
+		/* NOTREACHED */
+		break;
+	}
+}
+
+static void
+add_macro_arg(const char *argtext, int argnum)
+{
+	struct macro_arg *marg;
+	int i;
+	int retval;
+
+	if (cur_symbol == NULL || cur_symbol->type != MACRO) {
+		stop("Invalid current symbol for adding macro arg",
+		     EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+
+	marg = (struct macro_arg *)malloc(sizeof(*marg));
+	if (marg == NULL) {
+		stop("Can't create macro_arg structure", EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+	marg->replacement_text = NULL;
+	retval = snprintf(regex_pattern, sizeof(regex_pattern),
+			  "[^-/A-Za-z0-9_](%s)([^-/A-Za-z0-9_]|$)",
+			  argtext);
+	if (retval >= sizeof(regex_pattern)) {
+		stop("Regex text buffer too small for arg",
+		     EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+	retval = regcomp(&marg->arg_regex, regex_pattern, REG_EXTENDED);
+	if (retval != 0) {
+		stop("Regex compilation failed", EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+	STAILQ_INSERT_TAIL(&cur_symbol->info.macroinfo->args, marg, links);
+}
+
+static void
+add_macro_body(const char *bodytext)
+{
+	if (cur_symbol == NULL || cur_symbol->type != MACRO) {
+		stop("Invalid current symbol for adding macro arg",
+		     EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+	cur_symbol->info.macroinfo->body = strdup(bodytext);
+	if (cur_symbol->info.macroinfo->body == NULL) {
+		stop("Can't duplicate macro body text", EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+}
+
+static void
+process_register(symbol_t **p_symbol)
+{
+	symbol_t *symbol = *p_symbol;
+
+	if (symbol->type == UNINITIALIZED) {
+		snprintf(errbuf, sizeof(errbuf), "Undefined register %s",
+			 symbol->name);
+		stop(errbuf, EX_DATAERR);
+		/* NOTREACHED */
+	} else if (symbol->type == ALIAS) {
+		*p_symbol = symbol->info.ainfo->parent;
+	} else if ((symbol->type != REGISTER)
+		&& (symbol->type != SCBLOC)
+		&& (symbol->type != SRAMLOC)) {
+		snprintf(errbuf, sizeof(errbuf),
+			 "Specified symbol %s is not a register",
+			 symbol->name);
+		stop(errbuf, EX_DATAERR);
+	}
+}
+
+static void
+format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed,
+	       symbol_ref_t *src, int ret)
+{
+	struct instruction *instr;
+	struct ins_format1 *f1_instr;
+
+	if (src->symbol == NULL)
+		src = dest;
+
+	/* Test register permissions */
+	test_writable_symbol(dest->symbol);
+	test_readable_symbol(src->symbol);
+
+	if (!is_location_address(dest->symbol)) {
+		/* Ensure that immediate makes sense for this destination */
+		type_check(dest, immed, opcode);
+	}
+
+	/* Allocate sequencer space for the instruction and fill it out */
+	instr = seq_alloc();
+	f1_instr = &instr->format.format1;
+	f1_instr->ret = ret ? 1 : 0;
+	f1_instr->opcode = opcode;
+	f1_instr->destination = dest->symbol->info.rinfo->address
+			      + dest->offset;
+	f1_instr->source = src->symbol->info.rinfo->address
+			 + src->offset;
+	f1_instr->immediate = immed->value;
+
+	if (is_download_const(immed))
+		f1_instr->parity = 1;
+	else if (dest->symbol == mode_ptr.symbol) {
+		u_int src_value;
+		u_int dst_value;
+
+		/*
+		 * Attempt to update mode information if
+		 * we are operating on the mode register.
+		 */
+		if (src->symbol == allones.symbol)
+			src_value = 0xFF;
+		else if (src->symbol == allzeros.symbol)
+			src_value = 0;
+		else if (src->symbol == mode_ptr.symbol)
+			src_value = (dst_mode << 4) | src_mode;
+		else
+			goto cant_update;
+
+		switch (opcode) {
+		case AIC_OP_AND:
+			dst_value = src_value & immed->value;
+			break;
+		case AIC_OP_XOR:
+			dst_value = src_value ^ immed->value;
+			break;
+		case AIC_OP_ADD:
+			dst_value = (src_value + immed->value) & 0xFF;
+			break;
+		case AIC_OP_OR:
+			dst_value = src_value | immed->value;
+			break;
+		case AIC_OP_BMOV:
+			dst_value = src_value;
+			break;
+		default:
+			goto cant_update;
+		}
+		src_mode = dst_value & 0xF;
+		dst_mode = (dst_value >> 4) & 0xF;
+	}
+
+cant_update:
+	symlist_free(&immed->referenced_syms);
+	instruction_ptr++;
+}
+
+static void
+format_2_instr(int opcode, symbol_ref_t *dest, expression_t *places,
+	       symbol_ref_t *src, int ret)
+{
+	struct instruction *instr;
+	struct ins_format2 *f2_instr;
+	uint8_t shift_control;
+
+	if (src->symbol == NULL)
+		src = dest;
+
+	/* Test register permissions */
+	test_writable_symbol(dest->symbol);
+	test_readable_symbol(src->symbol);
+
+	/* Allocate sequencer space for the instruction and fill it out */
+	instr = seq_alloc();
+	f2_instr = &instr->format.format2;
+	f2_instr->ret = ret ? 1 : 0;
+	f2_instr->opcode = AIC_OP_ROL;
+	f2_instr->destination = dest->symbol->info.rinfo->address
+			      + dest->offset;
+	f2_instr->source = src->symbol->info.rinfo->address
+			 + src->offset;
+	if (places->value > 8 || places->value <= 0) {
+		stop("illegal shift value", EX_DATAERR);
+		/* NOTREACHED */
+	}
+	switch (opcode) {
+	case AIC_OP_SHL:
+		if (places->value == 8)
+			shift_control = 0xf0;
+		else
+			shift_control = (places->value << 4) | places->value;
+		break;
+	case AIC_OP_SHR:
+		if (places->value == 8) {
+			shift_control = 0xf8;
+		} else {
+			shift_control = (places->value << 4)
+				      | (8 - places->value)
+				      | 0x08;
+		}
+		break;
+	case AIC_OP_ROL:
+		shift_control = places->value & 0x7;
+		break;
+	case AIC_OP_ROR:
+		shift_control = (8 - places->value) | 0x08;
+		break;
+	default:
+		shift_control = 0; /* Quiet Compiler */
+		stop("Invalid shift operation specified", EX_SOFTWARE);
+		/* NOTREACHED */
+		break;
+	};
+	f2_instr->shift_control = shift_control;
+	symlist_free(&places->referenced_syms);
+	instruction_ptr++;
+}
+
+static void
+format_3_instr(int opcode, symbol_ref_t *src,
+	       expression_t *immed, symbol_ref_t *address)
+{
+	struct instruction *instr;
+	struct ins_format3 *f3_instr;
+	int addr;
+
+	/* Test register permissions */
+	test_readable_symbol(src->symbol);
+
+	/* Allocate sequencer space for the instruction and fill it out */
+	instr = seq_alloc();
+	f3_instr = &instr->format.format3;
+	if (address->symbol == NULL) {
+		/* 'dot' reference.  Use the current instruction pointer */
+		addr = instruction_ptr + address->offset;
+	} else if (address->symbol->type == UNINITIALIZED) {
+		/* forward reference */
+		addr = address->offset;
+		instr->patch_label = address->symbol;
+	} else
+		addr = address->symbol->info.linfo->address + address->offset;
+	f3_instr->opcode = opcode;
+	f3_instr->address = addr;
+	f3_instr->source = src->symbol->info.rinfo->address
+			 + src->offset;
+	f3_instr->immediate = immed->value;
+
+	if (is_download_const(immed))
+		f3_instr->parity = 1;
+
+	symlist_free(&immed->referenced_syms);
+	instruction_ptr++;
+}
+
+static void
+test_readable_symbol(symbol_t *symbol)
+{
+	if ((symbol->info.rinfo->modes & (0x1 << src_mode)) == 0) {
+		snprintf(errbuf, sizeof(errbuf),
+			"Register %s unavailable in source reg mode %d",
+			symbol->name, src_mode);
+		stop(errbuf, EX_DATAERR);
+	}
+
+	if (symbol->info.rinfo->mode == WO) {
+		stop("Write Only register specified as source",
+		     EX_DATAERR);
+		/* NOTREACHED */
+	}
+}
+
+static void
+test_writable_symbol(symbol_t *symbol)
+{
+	if ((symbol->info.rinfo->modes & (0x1 << dst_mode)) == 0) {
+		snprintf(errbuf, sizeof(errbuf),
+			"Register %s unavailable in destination reg mode %d",
+			symbol->name, dst_mode);
+		stop(errbuf, EX_DATAERR);
+	}
+
+	if (symbol->info.rinfo->mode == RO) {
+		stop("Read Only register specified as destination",
+		     EX_DATAERR);
+		/* NOTREACHED */
+	}
+}
+
+static void
+type_check(symbol_ref_t *sym, expression_t *expression, int opcode)
+{
+	symbol_t *symbol = sym->symbol;
+	symbol_node_t *node;
+	int and_op;
+	int8_t value, mask;
+
+	and_op = FALSE;
+	/*
+	 * Make sure that we aren't attempting to write something
+	 * that hasn't been defined.  If this is an and operation,
+	 * this is a mask, so "undefined" bits are okay.
+	 */
+	if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ ||
+	    opcode == AIC_OP_JZ  || opcode == AIC_OP_JNE ||
+	    opcode == AIC_OP_BMOV)
+		and_op = TRUE;
+
+	/*
+	 * Defaulting to 8 bit logic
+	 */
+	mask = (int8_t)~symbol->info.rinfo->valid_bitmask;
+	value = (int8_t)expression->value;
+
+	if (and_op == FALSE && (mask & value) != 0 ) {
+		snprintf(errbuf, sizeof(errbuf),
+			 "Invalid bit(s) 0x%x in immediate written to %s",
+			 (mask & value),
+			 symbol->name);
+		stop(errbuf, EX_DATAERR);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * Now make sure that all of the symbols referenced by the
+	 * expression are defined for this register.
+	 */
+	if (symbol->info.rinfo->typecheck_masks != FALSE) {
+		for(node = expression->referenced_syms.slh_first;
+		    node != NULL;
+		    node = node->links.sle_next) {
+			if ((node->symbol->type == MASK
+			  || node->symbol->type == FIELD
+			  || node->symbol->type == ENUM
+			  || node->symbol->type == ENUM_ENTRY)
+			 && symlist_search(&node->symbol->info.finfo->symrefs,
+					   symbol->name) == NULL) {
+				snprintf(errbuf, sizeof(errbuf),
+					 "Invalid field or mask %s "
+					 "for register %s",
+					 node->symbol->name, symbol->name);
+				stop(errbuf, EX_DATAERR);
+				/* NOTREACHED */
+			}
+		}
+	}
+}
+
+static void
+make_expression(expression_t *immed, int value)
+{
+	SLIST_INIT(&immed->referenced_syms);
+	immed->value = value & 0xff;
+}
+
+static void
+add_conditional(symbol_t *symbol)
+{
+	static int numfuncs;
+
+	if (numfuncs == 0) {
+		/* add a special conditional, "0" */
+		symbol_t *false_func;
+
+		false_func = symtable_get("0");
+		if (false_func->type != UNINITIALIZED) {
+			stop("Conditional expression '0' "
+			     "conflicts with a symbol", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		false_func->type = CONDITIONAL;
+		initialize_symbol(false_func);
+		false_func->info.condinfo->func_num = numfuncs++;
+		symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD);
+	}
+
+	/* This condition has occurred before */
+	if (symbol->type == CONDITIONAL)
+		return;
+
+	if (symbol->type != UNINITIALIZED) {
+		stop("Conditional expression conflicts with a symbol",
+		     EX_DATAERR);
+		/* NOTREACHED */
+	}
+
+	symbol->type = CONDITIONAL;
+	initialize_symbol(symbol);
+	symbol->info.condinfo->func_num = numfuncs++;
+	symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
+}
+
+static void
+add_version(const char *verstring)
+{
+	const char prefix[] = " * ";
+	int newlen;
+	int oldlen;
+
+	newlen = strlen(verstring) + strlen(prefix);
+	oldlen = 0;
+	if (versions != NULL)
+		oldlen = strlen(versions);
+	versions = realloc(versions, newlen + oldlen + 2);
+	if (versions == NULL)
+		stop("Can't allocate version string", EX_SOFTWARE);
+	strcpy(&versions[oldlen], prefix);
+	strcpy(&versions[oldlen + strlen(prefix)], verstring);
+	versions[newlen + oldlen] = '\n';
+	versions[newlen + oldlen + 1] = '\0';
+}
+
+void
+yyerror(const char *string)
+{
+	stop(string, EX_DATAERR);
+}
+
+static int
+is_download_const(expression_t *immed)
+{
+	if ((immed->referenced_syms.slh_first != NULL)
+	 && (immed->referenced_syms.slh_first->symbol->type == DOWNLOAD_CONST))
+		return (TRUE);
+
+	return (FALSE);
+}
+
+static int
+is_location_address(symbol_t *sym)
+{
+	if (sym->type == SCBLOC ||
+	    sym->type == SRAMLOC)
+		return (TRUE);
+	return (FALSE);
+}
+
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h b/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
new file mode 100644
index 0000000..8373447
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
@@ -0,0 +1,218 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_insformat.h#12 $
+ *
+ * $FreeBSD$
+ */
+
+#include <asm/byteorder.h>
+
+/* 8bit ALU logic operations */
+struct ins_format1 {
+#ifdef __LITTLE_ENDIAN
+	uint32_t	immediate	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			immediate	: 8;
+#endif
+};
+
+/* 8bit ALU shift/rotate operations */
+struct ins_format2 {
+#ifdef __LITTLE_ENDIAN
+	uint32_t	shift_control	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			shift_control	: 8;
+#endif
+};
+
+/* 8bit branch control operations */
+struct ins_format3 {
+#ifdef __LITTLE_ENDIAN
+	uint32_t	immediate	: 8,
+			source		: 9,
+			address		: 10,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			address		: 10,
+			source		: 9,
+			immediate	: 8;
+#endif
+};
+
+/* 16bit ALU logic operations */
+struct ins_format4 {
+#ifdef __LITTLE_ENDIAN
+	uint32_t	opcode_ext	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			opcode_ext	: 8;
+#endif
+};
+
+/* 16bit branch control operations */
+struct ins_format5 {
+#ifdef __LITTLE_ENDIAN
+	uint32_t	opcode_ext	: 8,
+			source		: 9,
+			address		: 10,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			address		: 10,
+			source		: 9,
+			opcode_ext	: 8;
+#endif
+};
+
+/*  Far branch operations */
+struct ins_format6 {
+#ifdef __LITTLE_ENDIAN
+	uint32_t	page		: 3,
+			opcode_ext	: 5,
+			source		: 9,
+			address		: 10,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			address		: 10,
+			source		: 9,
+			opcode_ext	: 5,
+			page		: 3;
+#endif
+};
+
+union ins_formats {
+		struct ins_format1 format1;
+		struct ins_format2 format2;
+		struct ins_format3 format3;
+		struct ins_format4 format4;
+		struct ins_format5 format5;
+		struct ins_format6 format6;
+		uint8_t		   bytes[4];
+		uint32_t	   integer;
+};
+struct instruction {
+	union	ins_formats format;
+	u_int	srcline;
+	struct symbol *patch_label;
+	STAILQ_ENTRY(instruction) links;
+};
+
+#define	AIC_OP_OR	0x0
+#define	AIC_OP_AND	0x1
+#define AIC_OP_XOR	0x2
+#define	AIC_OP_ADD	0x3
+#define	AIC_OP_ADC	0x4
+#define	AIC_OP_ROL	0x5
+#define	AIC_OP_BMOV	0x6
+
+#define	AIC_OP_MVI16	0x7
+
+#define	AIC_OP_JMP	0x8
+#define AIC_OP_JC	0x9
+#define AIC_OP_JNC	0xa
+#define AIC_OP_CALL	0xb
+#define	AIC_OP_JNE	0xc
+#define	AIC_OP_JNZ	0xd
+#define	AIC_OP_JE	0xe
+#define	AIC_OP_JZ	0xf
+
+/* Pseudo Ops */
+#define	AIC_OP_SHL	0x10
+#define	AIC_OP_SHR	0x20
+#define	AIC_OP_ROR	0x30
+
+/* 16bit Ops. Low byte main opcode.  High byte extended opcode. */ 
+#define	AIC_OP_OR16	0x8005
+#define	AIC_OP_AND16	0x8105
+#define	AIC_OP_XOR16	0x8205
+#define	AIC_OP_ADD16	0x8305
+#define	AIC_OP_ADC16	0x8405
+#define AIC_OP_JNE16	0x8805
+#define AIC_OP_JNZ16	0x8905
+#define AIC_OP_JE16	0x8C05
+#define AIC_OP_JZ16	0x8B05
+#define AIC_OP_JMP16	0x9005
+#define AIC_OP_JC16	0x9105
+#define AIC_OP_JNC16	0x9205
+#define AIC_OP_CALL16	0x9305
+
+/* Page extension is low three bits of second opcode byte. */
+#define AIC_OP_JMPF	0xA005
+#define AIC_OP_CALLF	0xB005
+#define AIC_OP_JCF	0xC005
+#define AIC_OP_JNCF	0xD005
+#define AIC_OP_CMPXCHG	0xE005
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
new file mode 100644
index 0000000..708326d
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
@@ -0,0 +1,165 @@
+%{
+/*
+ * Sub-parser for macro invocation in the Aic7xxx SCSI
+ * Host adapter sequencer assembler.
+ *
+ * Copyright (c) 2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_macro_gram.y#5 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+static symbol_t *macro_symbol;
+
+static void add_macro_arg(const char *argtext, int position);
+void mmerror(const char *string);
+
+%}
+
+%union {
+	int		value;
+	char		*str;
+	symbol_t	*sym;
+}
+
+
+%token <str> T_ARG
+
+%token <sym> T_SYMBOL
+
+%type <value> macro_arglist
+
+%%
+
+macrocall:
+	T_SYMBOL '('
+	{
+		macro_symbol = $1;
+	}
+	macro_arglist ')'
+	{
+		if (macro_symbol->info.macroinfo->narg != $4) {
+			printf("Narg == %d", macro_symbol->info.macroinfo->narg);
+			stop("Too few arguments for macro invocation",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		macro_symbol = NULL;
+		YYACCEPT;
+	}
+;
+
+macro_arglist:
+	{
+		/* Macros can take 0 arguments */
+		$$ = 0;
+	}
+|	T_ARG
+	{
+		$$ = 1;
+		add_macro_arg($1, 1);
+	}
+|	macro_arglist ',' T_ARG
+	{
+		if ($1 == 0) {
+			stop("Comma without preceding argument in arg list",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$ = $1 + 1;
+		add_macro_arg($3, $$);
+	}
+;
+
+%%
+
+static void
+add_macro_arg(const char *argtext, int argnum)
+{
+	struct macro_arg *marg;
+	int i;
+
+	if (macro_symbol == NULL || macro_symbol->type != MACRO) {
+		stop("Invalid current symbol for adding macro arg",
+		     EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+	/*
+	 * Macro Invocation.  Find the appropriate argument and fill
+	 * in the replace ment text for this call.
+	 */
+	i = 0;
+	STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
+		i++;
+		if (i == argnum)
+			break;
+	}
+	if (marg == NULL) {
+		stop("Too many arguments for macro invocation", EX_DATAERR);
+		/* NOTREACHED */
+	}
+	marg->replacement_text = strdup(argtext);
+	if (marg->replacement_text == NULL) {
+		stop("Unable to replicate replacement text", EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+}
+
+void
+mmerror(const char *string)
+{
+	stop(string, EX_DATAERR);
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
new file mode 100644
index 0000000..c0457b8
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
@@ -0,0 +1,157 @@
+%{
+/*
+ * Sub-Lexical Analyzer for macro invokation in 
+ * the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 2001 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_macro_scan.l#8 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_macro_gram.h"
+
+#define MAX_STR_CONST 4096
+static char string_buf[MAX_STR_CONST];
+static char *string_buf_ptr;
+static int  parren_count;
+static char buf[255];
+int  mmlineno;
+%}
+
+WORD		[A-Za-z_][-A-Za-z_0-9]*
+SPACE		[ \t]+
+MCARG		[^(), \t]+
+
+%x ARGLIST
+
+%%
+\n			{
+				++mmlineno;
+			}
+\r			;
+<ARGLIST>{SPACE}	;
+<ARGLIST>\(		{
+				parren_count++;
+				if (parren_count == 1) {
+					string_buf_ptr = string_buf;
+					return ('(');
+				}
+				*string_buf_ptr++ = '(';
+			}
+<ARGLIST>\)		{
+				if (parren_count == 1) {
+					if (string_buf_ptr != string_buf) {
+						/*
+						 * Return an argument and
+						 * rescan this parren so we
+						 * can return it as well.
+						 */
+						*string_buf_ptr = '\0';
+						mmlval.str = string_buf;
+						string_buf_ptr = string_buf;
+						unput(')');
+						return T_ARG;
+					}
+					BEGIN INITIAL;
+					return (')');
+				}
+				parren_count--;
+				*string_buf_ptr++ = ')';
+			}
+<ARGLIST>{MCARG}	{
+				char *yptr;
+
+				yptr = mmtext;
+				while (*yptr)
+					*string_buf_ptr++ = *yptr++;
+			}
+<ARGLIST>\,		{
+				if (string_buf_ptr != string_buf) {
+					/*
+					 * Return an argument and
+					 * rescan this comma so we
+					 * can return it as well.
+					 */
+					*string_buf_ptr = '\0';
+					mmlval.str = string_buf;
+					string_buf_ptr = string_buf;
+					unput(',');
+					return T_ARG;
+				}
+				return ',';
+			}
+{WORD}[(]		{
+				/* May be a symbol or a macro invocation. */
+				mmlval.sym = symtable_get(mmtext);
+				if (mmlval.sym->type != MACRO) {
+					stop("Expecting Macro Name",
+					     EX_DATAERR);
+				}
+				unput('(');
+				parren_count = 0;
+				BEGIN ARGLIST;
+				return T_SYMBOL;
+			}
+.			{ 
+				snprintf(buf, sizeof(buf), "Invalid character "
+					 "'%c'", mmtext[0]);
+				stop(buf, EX_DATAERR);
+			}
+%%
+
+int
+mmwrap()
+{
+	stop("EOF encountered in macro call", EX_DATAERR);
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
new file mode 100644
index 0000000..93c8667
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
@@ -0,0 +1,622 @@
+%{
+/*
+ * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * Copyright (c) 2001, 2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_scan.l#20 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_gram.h"
+
+/* This is used for macro body capture too, so err on the large size. */
+#define MAX_STR_CONST 4096
+static char string_buf[MAX_STR_CONST];
+static char *string_buf_ptr;
+static int  parren_count;
+static int  quote_count;
+static char buf[255];
+%}
+
+PATH		([/]*[-A-Za-z0-9_.])+
+WORD		[A-Za-z_][-A-Za-z_0-9]*
+SPACE		[ \t]+
+MCARG		[^(), \t]+
+MBODY		((\\[^\n])*[^\n\\]*)+
+
+%x COMMENT
+%x CEXPR
+%x INCLUDE
+%x STRING
+%x MACRODEF
+%x MACROARGLIST
+%x MACROCALLARGS
+%x MACROBODY
+
+%%
+\n			{ ++yylineno; }
+\r			;
+"/*"			{ BEGIN COMMENT;  /* Enter comment eating state */ }
+<COMMENT>"/*"		{ fprintf(stderr, "Warning! Comment within comment."); }
+<COMMENT>\n		{ ++yylineno; }
+<COMMENT>[^*/\n]*	;
+<COMMENT>"*"+[^*/\n]*	;
+<COMMENT>"/"+[^*/\n]*	;
+<COMMENT>"*"+"/"	{ BEGIN INITIAL; }
+if[ \t]*\(		{
+				string_buf_ptr = string_buf;
+				parren_count = 1;
+				BEGIN CEXPR;
+				return T_IF;
+			}
+<CEXPR>\(		{	*string_buf_ptr++ = '('; parren_count++; }
+<CEXPR>\)		{
+				parren_count--;
+				if (parren_count == 0) {
+					/* All done */
+					BEGIN INITIAL;
+					*string_buf_ptr = '\0';
+					yylval.sym = symtable_get(string_buf);
+					return T_CEXPR;
+				} else {
+					*string_buf_ptr++ = ')';
+				}
+			}
+<CEXPR>\n		{ ++yylineno; }
+<CEXPR>\r		;
+<CEXPR>[^()\n]+	{
+				char *yptr;
+
+				yptr = yytext;
+				while (*yptr != '\0') {
+					/* Remove duplicate spaces */
+					if (*yptr == '\t')
+						*yptr = ' ';
+					if (*yptr == ' '
+					 && string_buf_ptr != string_buf
+					 && string_buf_ptr[-1] == ' ')
+						yptr++;
+					else 
+						*string_buf_ptr++ = *yptr++;
+				}
+			}
+else			{ return T_ELSE; }
+VERSION			{ return T_VERSION; }
+PREFIX			{ return T_PREFIX; }
+PATCH_ARG_LIST		{ return T_PATCH_ARG_LIST; }
+\"			{
+				string_buf_ptr = string_buf;
+				BEGIN STRING;
+			}
+<STRING>[^"]+		{
+				char *yptr;
+
+				yptr = yytext;
+				while (*yptr)
+					*string_buf_ptr++ = *yptr++;
+			}
+<STRING>\"		{
+				/* All done */
+				BEGIN INITIAL;
+				*string_buf_ptr = '\0';
+				yylval.str = string_buf;
+				return T_STRING;
+			}
+{SPACE}			 ;
+
+	/* Register/SCB/SRAM definition keywords */
+export			{ return T_EXPORT; }
+register		{ return T_REGISTER; }
+const			{ yylval.value = FALSE; return T_CONST; }
+download		{ return T_DOWNLOAD; }
+address			{ return T_ADDRESS; }
+count			{ return T_COUNT; }
+access_mode		{ return T_ACCESS_MODE; }
+dont_generate_debug_code { return T_DONT_GENERATE_DEBUG_CODE; }
+modes			{ return T_MODES; }
+RW|RO|WO		{
+				 if (strcmp(yytext, "RW") == 0)
+					yylval.value = RW;
+				 else if (strcmp(yytext, "RO") == 0)
+					yylval.value = RO;
+				 else
+					yylval.value = WO;
+				 return T_MODE;
+			}
+field			{ return T_FIELD; }
+enum			{ return T_ENUM; }
+mask			{ return T_MASK; }
+alias			{ return T_ALIAS; }
+size			{ return T_SIZE; }
+scb			{ return T_SCB; }
+scratch_ram		{ return T_SRAM; }
+accumulator		{ return T_ACCUM; }
+mode_pointer		{ return T_MODE_PTR; }
+allones			{ return T_ALLONES; }
+allzeros		{ return T_ALLZEROS; }
+none			{ return T_NONE; }
+sindex			{ return T_SINDEX; }
+A			{ return T_A; }
+
+	/* Instruction Formatting */
+PAD_PAGE		{ return T_PAD_PAGE; }
+BEGIN_CRITICAL		{ return T_BEGIN_CS; }
+END_CRITICAL		{ return T_END_CS; }
+SET_SRC_MODE		{ return T_SET_SRC_MODE; }
+SET_DST_MODE		{ return T_SET_DST_MODE; }
+
+	/* Opcodes */
+shl			{ return T_SHL; }
+shr			{ return T_SHR; }
+ror			{ return T_ROR; }
+rol			{ return T_ROL; }
+mvi			{ return T_MVI; }
+mov			{ return T_MOV; }
+clr			{ return T_CLR; }
+jmp			{ return T_JMP; }
+jc			{ return T_JC;	}
+jnc			{ return T_JNC;	}
+je			{ return T_JE;	}
+jne			{ return T_JNE;	}
+jz			{ return T_JZ;	}
+jnz			{ return T_JNZ;	}
+call			{ return T_CALL; }
+add			{ return T_ADD; }
+adc			{ return T_ADC; }
+bmov			{ return T_BMOV; }
+inc			{ return T_INC; }
+dec			{ return T_DEC; }
+stc			{ return T_STC;	}
+clc			{ return T_CLC; }
+cmp			{ return T_CMP;	}
+not			{ return T_NOT;	}
+xor			{ return T_XOR;	}
+test			{ return T_TEST;}
+and			{ return T_AND;	}
+or			{ return T_OR;	}
+ret			{ return T_RET; }
+nop			{ return T_NOP; }
+
+	/* ARP2 16bit extensions */
+	/* or16			{ return T_OR16; } */
+	/* and16			{ return T_AND16; }*/
+	/* xor16			{ return T_XOR16; }*/
+	/* add16			{ return T_ADD16; }*/
+	/* adc16			{ return T_ADC16; }*/
+	/* mvi16			{ return T_MVI16; }*/
+	/* test16			{ return T_TEST16; }*/
+	/* cmp16			{ return T_CMP16; }*/
+	/* cmpxchg			{ return T_CMPXCHG; }*/
+
+	/* Allowed Symbols */
+\<\<			{ return T_EXPR_LSHIFT; }
+\>\>			{ return T_EXPR_RSHIFT; }
+[-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; }
+
+	/* Number processing */
+0[0-7]*			{
+				yylval.value = strtol(yytext, NULL, 8);
+				return T_NUMBER;
+			}
+
+0[xX][0-9a-fA-F]+	{
+				yylval.value = strtoul(yytext + 2, NULL, 16);
+				return T_NUMBER;
+			}
+
+[1-9][0-9]*		{
+				yylval.value = strtol(yytext, NULL, 10);
+				return T_NUMBER;
+			}
+	/* Include Files */
+#include{SPACE}		{
+				BEGIN INCLUDE;
+				quote_count = 0;
+				return T_INCLUDE;
+			}
+<INCLUDE>[<]		{ return yytext[0]; }
+<INCLUDE>[>]		{ BEGIN INITIAL; return yytext[0]; }
+<INCLUDE>[\"]		{
+				if (quote_count != 0)
+					BEGIN INITIAL;
+				quote_count++;
+				return yytext[0];
+			}
+<INCLUDE>{PATH}		{
+				char *yptr;
+
+				yptr = yytext;
+				string_buf_ptr = string_buf;
+				while (*yptr)
+					*string_buf_ptr++ = *yptr++;
+				yylval.str = string_buf;
+				*string_buf_ptr = '\0';
+				return T_PATH;
+			}
+<INCLUDE>.		{ stop("Invalid include line", EX_DATAERR); }
+#define{SPACE}		{
+				BEGIN MACRODEF;
+				return T_DEFINE;
+			}
+<MACRODEF>{WORD}{SPACE}	{ 
+				char *yptr;
+
+				/* Strip space and return as a normal symbol */
+				yptr = yytext;
+				while (*yptr != ' ' && *yptr != '\t')
+					yptr++;
+				*yptr = '\0';
+				yylval.sym = symtable_get(yytext);
+				string_buf_ptr = string_buf;
+				BEGIN MACROBODY;
+				return T_SYMBOL;
+			}
+<MACRODEF>{WORD}\(	{
+				/*
+				 * We store the symbol with its opening
+				 * parren so we can differentiate macros
+				 * that take args from macros with the
+				 * same name that do not take args as
+				 * is allowed in C.
+				 */
+				BEGIN MACROARGLIST;
+				yylval.sym = symtable_get(yytext);
+				unput('(');
+				return T_SYMBOL;
+			}
+<MACROARGLIST>{WORD}	{
+				yylval.str = yytext;
+				return T_ARG;
+			}
+<MACROARGLIST>{SPACE}   ;
+<MACROARGLIST>[(,]	{
+				return yytext[0];
+			}
+<MACROARGLIST>[)]	{
+				string_buf_ptr = string_buf;
+				BEGIN MACROBODY;
+				return ')';
+			}
+<MACROARGLIST>.		{
+				snprintf(buf, sizeof(buf), "Invalid character "
+					 "'%c' in macro argument list",
+					 yytext[0]);
+				stop(buf, EX_DATAERR);
+			}
+<MACROCALLARGS>{SPACE}  ;
+<MACROCALLARGS>\(	{
+				parren_count++;
+				if (parren_count == 1)
+					return ('(');
+				*string_buf_ptr++ = '(';
+			}
+<MACROCALLARGS>\)	{
+				parren_count--;
+				if (parren_count == 0) {
+					BEGIN INITIAL;
+					return (')');
+				}
+				*string_buf_ptr++ = ')';
+			}
+<MACROCALLARGS>{MCARG}	{
+				char *yptr;
+
+				yptr = yytext;
+				while (*yptr)
+					*string_buf_ptr++ = *yptr++;
+			}
+<MACROCALLARGS>\,	{
+				if (string_buf_ptr != string_buf) {
+					/*
+					 * Return an argument and
+					 * rescan this comma so we
+					 * can return it as well.
+					 */
+					*string_buf_ptr = '\0';
+					yylval.str = string_buf;
+					string_buf_ptr = string_buf;
+					unput(',');
+					return T_ARG;
+				}
+				return ',';
+			}
+<MACROBODY>\\\n		{
+				/* Eat escaped newlines. */
+				++yylineno;
+			}
+<MACROBODY>\r		;
+<MACROBODY>\n		{
+				/* Macros end on the first unescaped newline. */
+				BEGIN INITIAL;
+				*string_buf_ptr = '\0';
+				yylval.str = string_buf;
+				++yylineno;
+				return T_MACROBODY;
+			}
+<MACROBODY>{MBODY}	{
+				char *yptr;
+				char c;
+
+				yptr = yytext;
+				while (c = *yptr++) {
+					/*
+					 * Strip carriage returns.
+					 */
+					if (c == '\r')
+						continue;
+					*string_buf_ptr++ = c;
+				}
+			}
+{WORD}\(		{
+				char *yptr;
+				char *ycopy;
+
+				/* May be a symbol or a macro invocation. */
+				yylval.sym = symtable_get(yytext);
+				if (yylval.sym->type == MACRO) {
+					YY_BUFFER_STATE old_state;
+					YY_BUFFER_STATE temp_state;
+
+					ycopy = strdup(yytext);
+					yptr = ycopy + yyleng;
+					while (yptr > ycopy)
+						unput(*--yptr);
+					old_state = YY_CURRENT_BUFFER;
+					temp_state =
+					    yy_create_buffer(stdin,
+							     YY_BUF_SIZE);
+					yy_switch_to_buffer(temp_state);
+					mm_switch_to_buffer(old_state);
+					mmparse();
+					mm_switch_to_buffer(temp_state);
+					yy_switch_to_buffer(old_state);
+					mm_delete_buffer(temp_state);
+					expand_macro(yylval.sym);
+				} else {
+					if (yylval.sym->type == UNINITIALIZED) {
+						/* Try without the '(' */
+						symbol_delete(yylval.sym);
+						yytext[yyleng-1] = '\0';
+						yylval.sym =
+						    symtable_get(yytext);
+					}
+					unput('(');
+					return T_SYMBOL;
+				}
+			}
+{WORD}			{
+				yylval.sym = symtable_get(yytext);
+				if (yylval.sym->type == MACRO) {
+					expand_macro(yylval.sym);
+				} else {
+					return T_SYMBOL;
+				}
+			}
+.			{ 
+				snprintf(buf, sizeof(buf), "Invalid character "
+					 "'%c'", yytext[0]);
+				stop(buf, EX_DATAERR);
+			}
+%%
+
+typedef struct include {
+        YY_BUFFER_STATE  buffer;
+        int              lineno;
+        char            *filename;
+	SLIST_ENTRY(include) links;
+}include_t;
+
+SLIST_HEAD(, include) include_stack;
+
+void
+include_file(char *file_name, include_type type)
+{
+	FILE *newfile;
+	include_t *include;
+
+	newfile = NULL;
+	/* Try the current directory first */
+	if (includes_search_curdir != 0 || type == SOURCE_FILE)
+		newfile = fopen(file_name, "r");
+
+	if (newfile == NULL && type != SOURCE_FILE) {
+                path_entry_t include_dir;
+                for (include_dir = search_path.slh_first;
+                     include_dir != NULL;                
+                     include_dir = include_dir->links.sle_next) {
+			char fullname[PATH_MAX];
+
+			if ((include_dir->quoted_includes_only == TRUE)
+			 && (type != QUOTED_INCLUDE))
+				continue;
+
+			snprintf(fullname, sizeof(fullname),
+				 "%s/%s", include_dir->directory, file_name);
+
+			if ((newfile = fopen(fullname, "r")) != NULL)
+				break;
+                }
+        }
+
+	if (newfile == NULL) {
+		perror(file_name);
+		stop("Unable to open input file", EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+
+	if (type != SOURCE_FILE) {
+		include = (include_t *)malloc(sizeof(include_t));
+		if (include == NULL) {
+			stop("Unable to allocate include stack entry",
+			     EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		include->buffer = YY_CURRENT_BUFFER;
+		include->lineno = yylineno;
+		include->filename = yyfilename;
+		SLIST_INSERT_HEAD(&include_stack, include, links);
+	}
+	yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE));
+	yylineno = 1;
+	yyfilename = strdup(file_name);
+}
+
+static void next_substitution(struct symbol *mac_symbol, const char *body_pos,
+			      const char **next_match,
+			      struct macro_arg **match_marg, regmatch_t *match);
+
+void
+expand_macro(struct symbol *macro_symbol)
+{
+	struct macro_arg *marg;
+	struct macro_arg *match_marg;
+	const char *body_head;
+	const char *body_pos;
+	const char *next_match;
+
+	/*
+	 * Due to the nature of unput, we must work
+	 * backwards through the macro body performing
+	 * any expansions.
+	 */
+	body_head = macro_symbol->info.macroinfo->body;
+	body_pos = body_head + strlen(body_head);
+	while (body_pos > body_head) {
+		regmatch_t match;
+
+		next_match = body_head;
+		match_marg = NULL;
+		next_substitution(macro_symbol, body_pos, &next_match,
+				  &match_marg, &match);
+
+		/* Put back everything up until the replacement. */
+		while (body_pos > next_match)
+			unput(*--body_pos);
+
+		/* Perform the replacement. */
+		if (match_marg != NULL) {
+			const char *strp;
+
+			next_match = match_marg->replacement_text;
+			strp = next_match + strlen(next_match);
+			while (strp > next_match)
+				unput(*--strp);
+
+			/* Skip past the unexpanded macro arg. */
+			body_pos -= match.rm_eo - match.rm_so;
+		}
+	}
+
+	/* Cleanup replacement text. */
+	STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
+		free(marg->replacement_text);
+	}
+}
+
+/*
+ * Find the next substitution in the macro working backwards from
+ * body_pos until the beginning of the macro buffer.  next_match
+ * should be initialized to the beginning of the macro buffer prior
+ * to calling this routine.
+ */
+static void
+next_substitution(struct symbol *mac_symbol, const char *body_pos,
+		  const char **next_match, struct macro_arg **match_marg,
+		  regmatch_t *match)
+{
+	regmatch_t	  matches[2];
+	struct macro_arg *marg;
+	const char	 *search_pos;
+	int		  retval;
+
+	do {
+		search_pos = *next_match;
+
+		STAILQ_FOREACH(marg, &mac_symbol->info.macroinfo->args, links) {
+
+			retval = regexec(&marg->arg_regex, search_pos, 2,
+					 matches, 0);
+			if (retval == 0
+			 && (matches[1].rm_eo + search_pos) <= body_pos
+			 && (matches[1].rm_eo + search_pos) > *next_match) {
+				*match = matches[1];
+				*next_match = match->rm_eo + search_pos;
+				*match_marg = marg;
+			}
+		}
+	} while (search_pos != *next_match);
+}
+
+int
+yywrap()
+{
+	include_t *include;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER);
+	(void)fclose(yyin);
+	if (yyfilename != NULL)
+		free(yyfilename);
+	yyfilename = NULL;
+	include = include_stack.slh_first;
+	if (include != NULL) {
+		yy_switch_to_buffer(include->buffer);
+		yylineno = include->lineno;
+		yyfilename = include->filename;
+		SLIST_REMOVE_HEAD(&include_stack, links);
+		free(include);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
new file mode 100644
index 0000000..232aff1
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
@@ -0,0 +1,693 @@
+/*
+ * Aic7xxx SCSI host adapter firmware assembler symbol table implementation
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.c#24 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#ifdef __linux__
+#include "aicdb.h"
+#else
+#include <db.h>
+#endif
+#include <fcntl.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "aicasm_symbol.h"
+#include "aicasm.h"
+
+static DB *symtable;
+
+symbol_t *
+symbol_create(char *name)
+{
+	symbol_t *new_symbol;
+
+	new_symbol = (symbol_t *)malloc(sizeof(symbol_t));
+	if (new_symbol == NULL) {
+		perror("Unable to create new symbol");
+		exit(EX_SOFTWARE);
+	}
+	memset(new_symbol, 0, sizeof(*new_symbol));
+	new_symbol->name = strdup(name);
+	if (new_symbol->name == NULL)
+		 stop("Unable to strdup symbol name", EX_SOFTWARE);
+	new_symbol->type = UNINITIALIZED;
+	new_symbol->count = 1;
+	return (new_symbol);
+}
+
+void
+symbol_delete(symbol_t *symbol)
+{
+	if (symtable != NULL) {
+		DBT	 key;
+
+		key.data = symbol->name;
+		key.size = strlen(symbol->name);
+		symtable->del(symtable, &key, /*flags*/0);
+	}
+	switch(symbol->type) {
+	case SCBLOC:
+	case SRAMLOC:
+	case REGISTER:
+		if (symbol->info.rinfo != NULL)
+			free(symbol->info.rinfo);
+		break;
+	case ALIAS:
+		if (symbol->info.ainfo != NULL)
+			free(symbol->info.ainfo);
+		break;
+	case MASK:
+	case FIELD:
+	case ENUM:
+	case ENUM_ENTRY:
+		if (symbol->info.finfo != NULL) {
+			symlist_free(&symbol->info.finfo->symrefs);
+			free(symbol->info.finfo);
+		}
+		break;
+	case DOWNLOAD_CONST:
+	case CONST:
+		if (symbol->info.cinfo != NULL)
+			free(symbol->info.cinfo);
+		break;
+	case LABEL:
+		if (symbol->info.linfo != NULL)
+			free(symbol->info.linfo);
+		break;
+	case UNINITIALIZED:
+	default:
+		break;
+	}
+	free(symbol->name);
+	free(symbol);
+}
+
+void
+symtable_open()
+{
+	symtable = dbopen(/*filename*/NULL,
+			  O_CREAT | O_NONBLOCK | O_RDWR, /*mode*/0, DB_HASH,
+			  /*openinfo*/NULL);
+
+	if (symtable == NULL) {
+		perror("Symbol table creation failed");
+		exit(EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+}
+
+void
+symtable_close()
+{
+	if (symtable != NULL) {
+		DBT	 key;
+		DBT	 data;
+
+		while (symtable->seq(symtable, &key, &data, R_FIRST) == 0) {
+			symbol_t *stored_ptr;
+
+			memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+			symbol_delete(stored_ptr);
+		}
+		symtable->close(symtable);
+	}
+}
+
+/*
+ * The semantics of get is to return an uninitialized symbol entry
+ * if a lookup fails.
+ */
+symbol_t *
+symtable_get(char *name)
+{
+	symbol_t *stored_ptr;
+	DBT	  key;
+	DBT	  data;
+	int	  retval;
+
+	key.data = (void *)name;
+	key.size = strlen(name);
+
+	if ((retval = symtable->get(symtable, &key, &data, /*flags*/0)) != 0) {
+		if (retval == -1) {
+			perror("Symbol table get operation failed");
+			exit(EX_SOFTWARE);
+			/* NOTREACHED */
+		} else if (retval == 1) {
+			/* Symbol wasn't found, so create a new one */
+			symbol_t *new_symbol;
+
+			new_symbol = symbol_create(name);
+			data.data = &new_symbol;
+			data.size = sizeof(new_symbol);
+			if (symtable->put(symtable, &key, &data,
+					  /*flags*/0) !=0) {
+				perror("Symtable put failed");
+				exit(EX_SOFTWARE);
+			}
+			return (new_symbol);
+		} else {
+			perror("Unexpected return value from db get routine");
+			exit(EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+	}
+	memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+	stored_ptr->count++;
+	data.data = &stored_ptr;
+	if (symtable->put(symtable, &key, &data, /*flags*/0) !=0) {
+		perror("Symtable put failed");
+		exit(EX_SOFTWARE);
+	}
+	return (stored_ptr);
+}
+
+symbol_node_t *
+symlist_search(symlist_t *symlist, char *symname)
+{
+	symbol_node_t *curnode;
+
+	curnode = SLIST_FIRST(symlist);
+	while(curnode != NULL) {
+		if (strcmp(symname, curnode->symbol->name) == 0)
+			break;
+		curnode = SLIST_NEXT(curnode, links);
+	}
+	return (curnode);
+}
+
+void
+symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
+{
+	symbol_node_t *newnode;
+
+	newnode = (symbol_node_t *)malloc(sizeof(symbol_node_t));
+	if (newnode == NULL) {
+		stop("symlist_add: Unable to malloc symbol_node", EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+	newnode->symbol = symbol;
+	if (how == SYMLIST_SORT) {
+		symbol_node_t *curnode;
+		int field;
+
+		field = FALSE;
+		switch(symbol->type) {
+		case REGISTER:
+		case SCBLOC:
+		case SRAMLOC:
+			break;
+		case FIELD:
+		case MASK:
+		case ENUM:
+		case ENUM_ENTRY:
+			field = TRUE;
+			break;
+		default:
+			stop("symlist_add: Invalid symbol type for sorting",
+			     EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+
+		curnode = SLIST_FIRST(symlist);
+		if (curnode == NULL
+		 || (field
+		  && (curnode->symbol->type > newnode->symbol->type
+		   || (curnode->symbol->type == newnode->symbol->type
+		    && (curnode->symbol->info.finfo->value >
+			newnode->symbol->info.finfo->value))))
+		 || (!field && (curnode->symbol->info.rinfo->address >
+				newnode->symbol->info.rinfo->address))) {
+			SLIST_INSERT_HEAD(symlist, newnode, links);
+			return;
+		}
+
+		while (1) {
+			if (SLIST_NEXT(curnode, links) == NULL) {
+				SLIST_INSERT_AFTER(curnode, newnode,
+						   links);
+				break;
+			} else {
+				symbol_t *cursymbol;
+
+				cursymbol = SLIST_NEXT(curnode, links)->symbol;
+				if ((field
+				  && (cursymbol->type > symbol->type
+				   || (cursymbol->type == symbol->type
+				    && (cursymbol->info.finfo->value >
+					symbol->info.finfo->value))))
+				 || (!field
+				   && (cursymbol->info.rinfo->address >
+				       symbol->info.rinfo->address))) {
+					SLIST_INSERT_AFTER(curnode, newnode,
+							   links);
+					break;
+				}
+			}
+			curnode = SLIST_NEXT(curnode, links);
+		}
+	} else {
+		SLIST_INSERT_HEAD(symlist, newnode, links);
+	}
+}
+
+void
+symlist_free(symlist_t *symlist)
+{
+	symbol_node_t *node1, *node2;
+
+	node1 = SLIST_FIRST(symlist);
+	while (node1 != NULL) {
+		node2 = SLIST_NEXT(node1, links);
+		free(node1);
+		node1 = node2;
+	}
+	SLIST_INIT(symlist);
+}
+
+void
+symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+	      symlist_t *symlist_src2)
+{
+	symbol_node_t *node;
+
+	*symlist_dest = *symlist_src1;
+	while((node = SLIST_FIRST(symlist_src2)) != NULL) {
+		SLIST_REMOVE_HEAD(symlist_src2, links);
+		SLIST_INSERT_HEAD(symlist_dest, node, links);
+	}
+
+	/* These are now empty */
+	SLIST_INIT(symlist_src1);
+	SLIST_INIT(symlist_src2);
+}
+
+void
+aic_print_file_prologue(FILE *ofile)
+{
+
+	if (ofile == NULL)
+		return;
+
+	fprintf(ofile,
+"/*\n"
+" * DO NOT EDIT - This file is automatically generated\n"
+" *		 from the following source files:\n"
+" *\n"
+"%s */\n",
+		versions);
+}
+
+void
+aic_print_include(FILE *dfile, char *include_file)
+{
+
+	if (dfile == NULL)
+		return;
+	fprintf(dfile, "\n#include \"%s\"\n\n", include_file);
+}
+
+void
+aic_print_reg_dump_types(FILE *ofile)
+{
+	if (ofile == NULL)
+		return;
+
+	fprintf(ofile,
+"typedef int (%sreg_print_t)(u_int, u_int *, u_int);\n"
+"typedef struct %sreg_parse_entry {\n"
+"	char	*name;\n"
+"	uint8_t	 value;\n"
+"	uint8_t	 mask;\n"
+"} %sreg_parse_entry_t;\n"
+"\n",
+		prefix, prefix, prefix);
+}
+
+static void
+aic_print_reg_dump_start(FILE *dfile, symbol_node_t *regnode)
+{
+	if (dfile == NULL)
+		return;
+
+	fprintf(dfile,
+"static const %sreg_parse_entry_t %s_parse_table[] = {\n",
+		prefix,
+		regnode->symbol->name);
+}
+
+static void
+aic_print_reg_dump_end(FILE *ofile, FILE *dfile,
+		       symbol_node_t *regnode, u_int num_entries)
+{
+	char *lower_name;
+	char *letter;
+
+	lower_name = strdup(regnode->symbol->name);
+	if (lower_name == NULL)
+		 stop("Unable to strdup symbol name", EX_SOFTWARE);
+
+	for (letter = lower_name; *letter != '\0'; letter++)
+		*letter = tolower(*letter);
+
+	if (dfile != NULL) {
+		if (num_entries != 0)
+			fprintf(dfile,
+"\n"
+"};\n"
+"\n");
+
+		fprintf(dfile,
+"int\n"
+"%s%s_print(u_int regvalue, u_int *cur_col, u_int wrap)\n"
+"{\n"
+"	return (%sprint_register(%s%s, %d, \"%s\",\n"
+"	    0x%02x, regvalue, cur_col, wrap));\n"
+"}\n"
+"\n",
+			prefix,
+			lower_name,
+			prefix,
+			num_entries != 0 ? regnode->symbol->name : "NULL",
+			num_entries != 0 ? "_parse_table" : "",
+			num_entries,
+			regnode->symbol->name,
+			regnode->symbol->info.rinfo->address);
+	}
+
+	fprintf(ofile,
+"#if AIC_DEBUG_REGISTERS\n"
+"%sreg_print_t %s%s_print;\n"
+"#else\n"
+"#define %s%s_print(regvalue, cur_col, wrap) \\\n"
+"    %sprint_register(NULL, 0, \"%s\", 0x%02x, regvalue, cur_col, wrap)\n"
+"#endif\n"
+"\n",
+		prefix,
+		prefix,
+		lower_name,
+		prefix,
+		lower_name,
+		prefix,
+		regnode->symbol->name,
+		regnode->symbol->info.rinfo->address);
+}
+
+static void
+aic_print_reg_dump_entry(FILE *dfile, symbol_node_t *curnode)
+{
+	int num_tabs;
+
+	if (dfile == NULL)
+		return;
+
+	fprintf(dfile,
+"	{ \"%s\",",
+		curnode->symbol->name);
+
+	num_tabs = 3 - (strlen(curnode->symbol->name) + 5) / 8;
+
+	while (num_tabs-- > 0)
+		fputc('\t', dfile);
+	fprintf(dfile, "0x%02x, 0x%02x }",
+		curnode->symbol->info.finfo->value,
+		curnode->symbol->info.finfo->mask);
+}
+
+void
+symtable_dump(FILE *ofile, FILE *dfile)
+{
+	/*
+	 * Sort the registers by address with a simple insertion sort.
+	 * Put bitmasks next to the first register that defines them.
+	 * Put constants at the end.
+	 */
+	symlist_t	 registers;
+	symlist_t	 masks;
+	symlist_t	 constants;
+	symlist_t	 download_constants;
+	symlist_t	 aliases;
+	symlist_t	 exported_labels;
+	symbol_node_t	*curnode;
+	symbol_node_t	*regnode;
+	DBT		 key;
+	DBT		 data;
+	int		 flag;
+	int		 reg_count = 0, reg_used = 0;
+	u_int		 i;
+
+	if (symtable == NULL)
+		return;
+
+	SLIST_INIT(&registers);
+	SLIST_INIT(&masks);
+	SLIST_INIT(&constants);
+	SLIST_INIT(&download_constants);
+	SLIST_INIT(&aliases);
+	SLIST_INIT(&exported_labels);
+	flag = R_FIRST;
+	while (symtable->seq(symtable, &key, &data, flag) == 0) {
+		symbol_t *cursym;
+
+		memcpy(&cursym, data.data, sizeof(cursym));
+		switch(cursym->type) {
+		case REGISTER:
+		case SCBLOC:
+		case SRAMLOC:
+			symlist_add(&registers, cursym, SYMLIST_SORT);
+			break;
+		case MASK:
+		case FIELD:
+		case ENUM:
+		case ENUM_ENTRY:
+			symlist_add(&masks, cursym, SYMLIST_SORT);
+			break;
+		case CONST:
+			symlist_add(&constants, cursym,
+				    SYMLIST_INSERT_HEAD);
+			break;
+		case DOWNLOAD_CONST:
+			symlist_add(&download_constants, cursym,
+				    SYMLIST_INSERT_HEAD);
+			break;
+		case ALIAS:
+			symlist_add(&aliases, cursym,
+				    SYMLIST_INSERT_HEAD);
+			break;
+		case LABEL:
+			if (cursym->info.linfo->exported == 0)
+				break;
+			symlist_add(&exported_labels, cursym,
+				    SYMLIST_INSERT_HEAD);
+			break;
+		default:
+			break;
+		}
+		flag = R_NEXT;
+	}
+
+	/* Register dianostic functions/declarations first. */
+	aic_print_file_prologue(ofile);
+	aic_print_reg_dump_types(ofile);
+	aic_print_file_prologue(dfile);
+	aic_print_include(dfile, stock_include_file);
+	SLIST_FOREACH(curnode, &registers, links) {
+
+		if (curnode->symbol->dont_generate_debug_code)
+			continue;
+
+		switch(curnode->symbol->type) {
+		case REGISTER:
+		case SCBLOC:
+		case SRAMLOC:
+		{
+			symlist_t	*fields;
+			symbol_node_t	*fieldnode;
+			int		 num_entries;
+
+			num_entries = 0;
+			reg_count++;
+			if (curnode->symbol->count == 1)
+				break;
+			fields = &curnode->symbol->info.rinfo->fields;
+			SLIST_FOREACH(fieldnode, fields, links) {
+				if (num_entries == 0)
+					aic_print_reg_dump_start(dfile,
+								 curnode);
+				else if (dfile != NULL)
+					fputs(",\n", dfile);
+				num_entries++;
+				aic_print_reg_dump_entry(dfile, fieldnode);
+			}
+			aic_print_reg_dump_end(ofile, dfile,
+					       curnode, num_entries);
+			reg_used++;
+		}
+		default:
+			break;
+		}
+	}
+	fprintf(stderr, "%s: %d of %d register definitions used\n", appname,
+		reg_used, reg_count);
+
+	/* Fold in the masks and bits */
+	while (SLIST_FIRST(&masks) != NULL) {
+		char *regname;
+
+		curnode = SLIST_FIRST(&masks);
+		SLIST_REMOVE_HEAD(&masks, links);
+
+		regnode = SLIST_FIRST(&curnode->symbol->info.finfo->symrefs);
+		regname = regnode->symbol->name;
+		regnode = symlist_search(&registers, regname);
+		SLIST_INSERT_AFTER(regnode, curnode, links);
+	}
+
+	/* Add the aliases */
+	while (SLIST_FIRST(&aliases) != NULL) {
+		char *regname;
+
+		curnode = SLIST_FIRST(&aliases);
+		SLIST_REMOVE_HEAD(&aliases, links);
+
+		regname = curnode->symbol->info.ainfo->parent->name;
+		regnode = symlist_search(&registers, regname);
+		SLIST_INSERT_AFTER(regnode, curnode, links);
+	}
+
+	/* Output generated #defines. */
+	while (SLIST_FIRST(&registers) != NULL) {
+		symbol_node_t *curnode;
+		u_int value;
+		char *tab_str;
+		char *tab_str2;
+
+		curnode = SLIST_FIRST(&registers);
+		SLIST_REMOVE_HEAD(&registers, links);
+		switch(curnode->symbol->type) {
+		case REGISTER:
+		case SCBLOC:
+		case SRAMLOC:
+			fprintf(ofile, "\n");
+			value = curnode->symbol->info.rinfo->address;
+			tab_str = "\t";
+			tab_str2 = "\t\t";
+			break;
+		case ALIAS:
+		{
+			symbol_t *parent;
+
+			parent = curnode->symbol->info.ainfo->parent;
+			value = parent->info.rinfo->address;
+			tab_str = "\t";
+			tab_str2 = "\t\t";
+			break;
+		}
+		case MASK:
+		case FIELD:
+		case ENUM:
+		case ENUM_ENTRY:
+			value = curnode->symbol->info.finfo->value;
+			tab_str = "\t\t";
+			tab_str2 = "\t";
+			break;
+		default:
+			value = 0; /* Quiet compiler */
+			tab_str = NULL;
+			tab_str2 = NULL;
+			stop("symtable_dump: Invalid symbol type "
+			     "encountered", EX_SOFTWARE);
+			break;
+		}
+		fprintf(ofile, "#define%s%-16s%s0x%02x\n",
+			tab_str, curnode->symbol->name, tab_str2,
+			value);
+		free(curnode);
+	}
+	fprintf(ofile, "\n\n");
+
+	while (SLIST_FIRST(&constants) != NULL) {
+		symbol_node_t *curnode;
+
+		curnode = SLIST_FIRST(&constants);
+		SLIST_REMOVE_HEAD(&constants, links);
+		fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+			curnode->symbol->name,
+			curnode->symbol->info.cinfo->value);
+		free(curnode);
+	}
+
+	fprintf(ofile, "\n\n/* Downloaded Constant Definitions */\n");
+
+	for (i = 0; SLIST_FIRST(&download_constants) != NULL; i++) {
+		symbol_node_t *curnode;
+
+		curnode = SLIST_FIRST(&download_constants);
+		SLIST_REMOVE_HEAD(&download_constants, links);
+		fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+			curnode->symbol->name,
+			curnode->symbol->info.cinfo->value);
+		free(curnode);
+	}
+	fprintf(ofile, "#define\tDOWNLOAD_CONST_COUNT\t0x%02x\n", i);
+
+	fprintf(ofile, "\n\n/* Exported Labels */\n");
+
+	while (SLIST_FIRST(&exported_labels) != NULL) {
+		symbol_node_t *curnode;
+
+		curnode = SLIST_FIRST(&exported_labels);
+		SLIST_REMOVE_HEAD(&exported_labels, links);
+		fprintf(ofile, "#define\tLABEL_%-8s\t0x%02x\n",
+			curnode->symbol->name,
+			curnode->symbol->info.linfo->address);
+		free(curnode);
+	}
+}
+
diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
new file mode 100644
index 0000000..34bbcad
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
@@ -0,0 +1,209 @@
+/*
+ * Aic7xxx SCSI host adapter firmware assembler symbol table definitions
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2002 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.h#17 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+typedef enum {
+	UNINITIALIZED,
+	REGISTER,
+	ALIAS,
+	SCBLOC,
+	SRAMLOC,
+	ENUM_ENTRY,
+	FIELD,
+	MASK,
+	ENUM,
+	CONST,
+	DOWNLOAD_CONST,
+	LABEL,
+	CONDITIONAL,
+	MACRO
+} symtype;
+
+typedef enum {
+	RO = 0x01,
+	WO = 0x02,
+	RW = 0x03
+}amode_t;
+
+typedef SLIST_HEAD(symlist, symbol_node) symlist_t;
+
+struct reg_info {
+	u_int	  address;
+	int	  size;
+	amode_t	  mode;
+	symlist_t fields;
+	uint8_t	  valid_bitmask;
+	uint8_t	  modes;
+	int	  typecheck_masks;
+};
+
+struct field_info {
+	symlist_t symrefs;
+	uint8_t	  value;
+	uint8_t	  mask;
+};
+
+struct const_info {
+	u_int	value;
+	int	define;
+};
+
+struct alias_info {
+	struct symbol *parent;
+};
+
+struct label_info {
+	int	address;
+	int	exported;
+};
+
+struct cond_info {
+	int	func_num;
+};
+
+struct macro_arg {
+	STAILQ_ENTRY(macro_arg)	links;
+	regex_t	arg_regex;
+	char   *replacement_text;
+};
+STAILQ_HEAD(macro_arg_list, macro_arg) args;
+
+struct macro_info {
+	struct macro_arg_list args;
+	int   narg;
+	const char* body;
+};
+
+typedef struct expression_info {
+        symlist_t       referenced_syms;
+        int             value;
+} expression_t;
+
+typedef struct symbol {
+	char	*name;
+	symtype	type;
+	int	count;
+	union	{
+		struct reg_info	  *rinfo;
+		struct field_info *finfo;
+		struct const_info *cinfo;
+		struct alias_info *ainfo;
+		struct label_info *linfo;
+		struct cond_info  *condinfo;
+		struct macro_info *macroinfo;
+	} info;
+	int	dont_generate_debug_code;
+} symbol_t;
+
+typedef struct symbol_ref {
+	symbol_t *symbol;
+	int	 offset;
+} symbol_ref_t;
+
+typedef struct symbol_node {
+	SLIST_ENTRY(symbol_node) links;
+	symbol_t *symbol;
+} symbol_node_t;
+
+typedef struct critical_section {
+	TAILQ_ENTRY(critical_section) links;
+	int begin_addr;
+	int end_addr;
+} critical_section_t;
+
+typedef enum {
+	SCOPE_ROOT,
+	SCOPE_IF,
+	SCOPE_ELSE_IF,
+	SCOPE_ELSE
+} scope_type;
+
+typedef struct patch_info {
+	int skip_patch;
+	int skip_instr;
+} patch_info_t;
+
+typedef struct scope {
+	SLIST_ENTRY(scope) scope_stack_links;
+	TAILQ_ENTRY(scope) scope_links;
+	TAILQ_HEAD(, scope) inner_scope;
+	scope_type type;
+	int inner_scope_patches;
+	int begin_addr;
+        int end_addr;
+	patch_info_t patches[2];
+	int func_num;
+} scope_t;
+
+TAILQ_HEAD(cs_tailq, critical_section);
+SLIST_HEAD(scope_list, scope);
+TAILQ_HEAD(scope_tailq, scope);
+
+void	symbol_delete(symbol_t *symbol);
+
+void	symtable_open(void);
+
+void	symtable_close(void);
+
+symbol_t *
+	symtable_get(char *name);
+
+symbol_node_t *
+	symlist_search(symlist_t *symlist, char *symname);
+
+void
+	symlist_add(symlist_t *symlist, symbol_t *symbol, int how);
+#define SYMLIST_INSERT_HEAD	0x00
+#define SYMLIST_SORT		0x01
+
+void	symlist_free(symlist_t *symlist);
+
+void	symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+		      symlist_t *symlist_src2);
+void	symtable_dump(FILE *ofile, FILE *dfile);
diff --git a/drivers/scsi/aic7xxx/aiclib.c b/drivers/scsi/aic7xxx/aiclib.c
new file mode 100644
index 0000000..828ae3d
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aiclib.c
@@ -0,0 +1,34 @@
+/*
+ * Implementation of Utility functions for all SCSI device types.
+ *
+ * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Kenneth D. Merry.
+ * 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,
+ *    without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not 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.
+ *
+ * $FreeBSD: src/sys/cam/scsi/scsi_all.c,v 1.38 2002/09/23 04:56:35 mjacob Exp $
+ * $Id$
+ */
+
+#include "aiclib.h"
+
diff --git a/drivers/scsi/aic7xxx/aiclib.h b/drivers/scsi/aic7xxx/aiclib.h
new file mode 100644
index 0000000..f8fd198
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aiclib.h
@@ -0,0 +1,195 @@
+/*
+ * Largely written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ *
+ * $FreeBSD: src/sys/cam/scsi/scsi_all.h,v 1.21 2002/10/08 17:12:44 ken Exp $
+ *
+ * Copyright (c) 2003 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id$
+ */
+
+#ifndef	_AICLIB_H
+#define _AICLIB_H
+
+struct scsi_sense
+{
+	uint8_t opcode;
+	uint8_t byte2;
+	uint8_t unused[2];
+	uint8_t length;
+	uint8_t control;
+};
+
+#define		SCSI_REV_0		0
+#define		SCSI_REV_CCS		1
+#define		SCSI_REV_2		2
+#define		SCSI_REV_SPC		3
+#define		SCSI_REV_SPC2		4
+
+struct scsi_sense_data
+{
+	uint8_t error_code;
+#define	SSD_ERRCODE			0x7F
+#define		SSD_CURRENT_ERROR	0x70
+#define		SSD_DEFERRED_ERROR	0x71
+#define	SSD_ERRCODE_VALID	0x80	
+	uint8_t segment;
+	uint8_t flags;
+#define	SSD_KEY				0x0F
+#define		SSD_KEY_NO_SENSE	0x00
+#define		SSD_KEY_RECOVERED_ERROR	0x01
+#define		SSD_KEY_NOT_READY	0x02
+#define		SSD_KEY_MEDIUM_ERROR	0x03
+#define		SSD_KEY_HARDWARE_ERROR	0x04
+#define		SSD_KEY_ILLEGAL_REQUEST	0x05
+#define		SSD_KEY_UNIT_ATTENTION	0x06
+#define		SSD_KEY_DATA_PROTECT	0x07
+#define		SSD_KEY_BLANK_CHECK	0x08
+#define		SSD_KEY_Vendor_Specific	0x09
+#define		SSD_KEY_COPY_ABORTED	0x0a
+#define		SSD_KEY_ABORTED_COMMAND	0x0b		
+#define		SSD_KEY_EQUAL		0x0c
+#define		SSD_KEY_VOLUME_OVERFLOW	0x0d
+#define		SSD_KEY_MISCOMPARE	0x0e
+#define		SSD_KEY_RESERVED	0x0f			
+#define	SSD_ILI		0x20
+#define	SSD_EOM		0x40
+#define	SSD_FILEMARK	0x80
+	uint8_t info[4];
+	uint8_t extra_len;
+	uint8_t cmd_spec_info[4];
+	uint8_t add_sense_code;
+	uint8_t add_sense_code_qual;
+	uint8_t fru;
+	uint8_t sense_key_spec[3];
+#define	SSD_SCS_VALID		0x80
+#define SSD_FIELDPTR_CMD	0x40
+#define SSD_BITPTR_VALID	0x08
+#define SSD_BITPTR_VALUE	0x07
+#define SSD_MIN_SIZE 18
+	uint8_t extra_bytes[14];
+#define SSD_FULL_SIZE sizeof(struct scsi_sense_data)
+};
+
+/*
+ * Status Byte
+ */
+#define	SCSI_STATUS_OK			0x00
+#define	SCSI_STATUS_CHECK_COND		0x02
+#define	SCSI_STATUS_COND_MET		0x04
+#define	SCSI_STATUS_BUSY		0x08
+#define SCSI_STATUS_INTERMED		0x10
+#define SCSI_STATUS_INTERMED_COND_MET	0x14
+#define SCSI_STATUS_RESERV_CONFLICT	0x18
+#define SCSI_STATUS_CMD_TERMINATED	0x22	/* Obsolete in SAM-2 */
+#define SCSI_STATUS_QUEUE_FULL		0x28
+#define SCSI_STATUS_ACA_ACTIVE		0x30
+#define SCSI_STATUS_TASK_ABORTED	0x40
+
+/************************* Large Disk Handling ********************************/
+static inline int
+aic_sector_div(sector_t capacity, int heads, int sectors)
+{
+	/* ugly, ugly sector_div calling convention.. */
+	sector_div(capacity, (heads * sectors));
+	return (int)capacity;
+}
+
+static inline uint32_t
+scsi_4btoul(uint8_t *bytes)
+{
+	uint32_t rv;
+
+	rv = (bytes[0] << 24) |
+	     (bytes[1] << 16) |
+	     (bytes[2] << 8) |
+	     bytes[3];
+	return (rv);
+}
+
+/* Macros for generating the elements of the PCI ID tables. */
+
+#define GETID(v, s) (unsigned)(((v) >> (s)) & 0xFFFF ?: PCI_ANY_ID)
+
+#define ID_C(x, c)						\
+{								\
+	GETID(x,32), GETID(x,48), GETID(x,0), GETID(x,16),	\
+	(c) << 8, 0xFFFF00, 0					\
+}
+
+#define ID2C(x)                          \
+	ID_C(x, PCI_CLASS_STORAGE_SCSI), \
+	ID_C(x, PCI_CLASS_STORAGE_RAID)
+
+#define IDIROC(x)  ((x) | ~ID_ALL_IROC_MASK)
+
+/* Generate IDs for all 16 possibilites.
+ * The argument has already masked out
+ * the 4 least significant bits of the device id.
+ * (e.g., mask: ID_9005_GENERIC_MASK).
+ */
+#define ID16(x)                          \
+	ID(x),                           \
+	ID((x) | 0x0001000000000000ull), \
+	ID((x) | 0x0002000000000000ull), \
+	ID((x) | 0x0003000000000000ull), \
+	ID((x) | 0x0004000000000000ull), \
+	ID((x) | 0x0005000000000000ull), \
+	ID((x) | 0x0006000000000000ull), \
+	ID((x) | 0x0007000000000000ull), \
+	ID((x) | 0x0008000000000000ull), \
+	ID((x) | 0x0009000000000000ull), \
+	ID((x) | 0x000A000000000000ull), \
+	ID((x) | 0x000B000000000000ull), \
+	ID((x) | 0x000C000000000000ull), \
+	ID((x) | 0x000D000000000000ull), \
+	ID((x) | 0x000E000000000000ull), \
+	ID((x) | 0x000F000000000000ull)
+
+#endif /*_AICLIB_H */
diff --git a/drivers/scsi/aic7xxx/cam.h b/drivers/scsi/aic7xxx/cam.h
new file mode 100644
index 0000000..687aef6
--- /dev/null
+++ b/drivers/scsi/aic7xxx/cam.h
@@ -0,0 +1,111 @@
+/*
+ * Data structures and definitions for the CAM system.
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2000 Adaptec Inc.
+ * 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,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * 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.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/cam.h#15 $
+ */
+
+#ifndef _AIC7XXX_CAM_H
+#define _AIC7XXX_CAM_H 1
+
+#include <linux/types.h>
+
+#define	CAM_BUS_WILDCARD ((u_int)~0)
+#define	CAM_TARGET_WILDCARD ((u_int)~0)
+#define	CAM_LUN_WILDCARD ((u_int)~0)
+
+/* CAM Status field values */
+typedef enum {
+	CAM_REQ_INPROG,		/* CCB request is in progress */
+	CAM_REQ_CMP,		/* CCB request completed without error */
+	CAM_REQ_ABORTED,	/* CCB request aborted by the host */
+	CAM_UA_ABORT,		/* Unable to abort CCB request */
+	CAM_REQ_CMP_ERR,	/* CCB request completed with an error */
+	CAM_BUSY,		/* CAM subsystem is busy */
+	CAM_REQ_INVALID,	/* CCB request was invalid */
+	CAM_PATH_INVALID,	/* Supplied Path ID is invalid */
+	CAM_SEL_TIMEOUT,	/* Target Selection Timeout */
+	CAM_CMD_TIMEOUT,	/* Command timeout */
+	CAM_SCSI_STATUS_ERROR,	/* SCSI error, look at error code in CCB */
+	CAM_SCSI_BUS_RESET,	/* SCSI Bus Reset Sent/Received */
+	CAM_UNCOR_PARITY,	/* Uncorrectable parity error occurred */
+	CAM_AUTOSENSE_FAIL,	/* Autosense: request sense cmd fail */
+	CAM_NO_HBA,		/* No HBA Detected Error */
+	CAM_DATA_RUN_ERR,	/* Data Overrun error */
+	CAM_UNEXP_BUSFREE,	/* Unexpected Bus Free */
+	CAM_SEQUENCE_FAIL,	/* Protocol Violation */
+	CAM_CCB_LEN_ERR,	/* CCB length supplied is inadequate */
+	CAM_PROVIDE_FAIL,	/* Unable to provide requested capability */
+	CAM_BDR_SENT,		/* A SCSI BDR msg was sent to target */
+	CAM_REQ_TERMIO,		/* CCB request terminated by the host */
+	CAM_UNREC_HBA_ERROR,	/* Unrecoverable Host Bus Adapter Error */
+	CAM_REQ_TOO_BIG,	/* The request was too large for this host */
+	CAM_UA_TERMIO,		/* Unable to terminate I/O CCB request */
+	CAM_MSG_REJECT_REC,	/* Message Reject Received */
+	CAM_DEV_NOT_THERE,	/* SCSI Device Not Installed/there */
+	CAM_RESRC_UNAVAIL,	/* Resource Unavailable */
+	/*
+	 * This request should be requeued to preserve
+	 * transaction ordering.  This typically occurs
+	 * when the SIM recognizes an error that should
+	 * freeze the queue and must place additional
+	 * requests for the target at the sim level
+	 * back into the XPT queue.
+	 */
+	CAM_REQUEUE_REQ,
+	CAM_DEV_QFRZN		= 0x40,
+
+	CAM_STATUS_MASK		= 0x3F
+} cam_status;
+
+/*
+ * Definitions for the asynchronous callback CCB fields.
+ */
+typedef enum {
+	AC_GETDEV_CHANGED	= 0x800,/* Getdev info might have changed */
+	AC_INQ_CHANGED		= 0x400,/* Inquiry info might have changed */
+	AC_TRANSFER_NEG		= 0x200,/* New transfer settings in effect */
+	AC_LOST_DEVICE		= 0x100,/* A device went away */
+	AC_FOUND_DEVICE		= 0x080,/* A new device was found */
+	AC_PATH_DEREGISTERED	= 0x040,/* A path has de-registered */
+	AC_PATH_REGISTERED	= 0x020,/* A new path has been registered */
+	AC_SENT_BDR		= 0x010,/* A BDR message was sent to target */
+	AC_SCSI_AEN		= 0x008,/* A SCSI AEN has been received */
+	AC_UNSOL_RESEL		= 0x002,/* Unsolicited reselection occurred */
+	AC_BUS_RESET		= 0x001 /* A SCSI bus reset occurred */
+} ac_code;
+
+typedef enum {
+	CAM_DIR_IN		= DMA_FROM_DEVICE,
+	CAM_DIR_OUT		= DMA_TO_DEVICE,
+	CAM_DIR_NONE		= DMA_NONE,
+} ccb_flags;
+
+#endif /* _AIC7XXX_CAM_H */
diff --git a/drivers/scsi/aic7xxx/queue.h b/drivers/scsi/aic7xxx/queue.h
new file mode 100644
index 0000000..8adf800
--- /dev/null
+++ b/drivers/scsi/aic7xxx/queue.h
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.38 2000/05/26 02:06:56 jake Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *			SLIST	LIST	STAILQ	TAILQ	CIRCLEQ
+ * _HEAD		+	+	+	+	+
+ * _HEAD_INITIALIZER	+	+	+	+	+
+ * _ENTRY		+	+	+	+	+
+ * _INIT		+	+	+	+	+
+ * _EMPTY		+	+	+	+	+
+ * _FIRST		+	+	+	+	+
+ * _NEXT		+	+	+	+	+
+ * _PREV		-	-	-	+	+
+ * _LAST		-	-	+	+	+
+ * _FOREACH		+	+	+	+	+
+ * _FOREACH_REVERSE	-	-	-	+	+
+ * _INSERT_HEAD		+	+	+	+	+
+ * _INSERT_BEFORE	-	+	-	+	+
+ * _INSERT_AFTER	+	+	+	+	+
+ * _INSERT_TAIL		-	-	+	+	+
+ * _REMOVE_HEAD		+	-	+	-	-
+ * _REMOVE		+	+	+	+	+
+ *
+ */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define	SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+ 
+#define	SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+ 
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
+
+#define	SLIST_FIRST(head)	((head)->slh_first)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for ((var) = SLIST_FIRST((head));				\
+	    (var);							\
+	    (var) = SLIST_NEXT((var), field))
+
+#define	SLIST_INIT(head) do {						\
+	SLIST_FIRST((head)) = NULL;					\
+} while (0)
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);	\
+	SLIST_NEXT((slistelm), field) = (elm);				\
+} while (0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	SLIST_NEXT((elm), field) = SLIST_FIRST((head));			\
+	SLIST_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define	SLIST_REMOVE(head, elm, type, field) do {			\
+	if (SLIST_FIRST((head)) == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		struct type *curelm = SLIST_FIRST((head));		\
+		while (SLIST_NEXT(curelm, field) != (elm))		\
+			curelm = SLIST_NEXT(curelm, field);		\
+		SLIST_NEXT(curelm, field) =				\
+		    SLIST_NEXT(SLIST_NEXT(curelm, field), field);	\
+	}								\
+} while (0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);	\
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define	STAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *stqh_first;/* first element */			\
+	struct type **stqh_last;/* addr of last next element */		\
+}
+
+#define	STAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).stqh_first }
+
+#define	STAILQ_ENTRY(type)						\
+struct {								\
+	struct type *stqe_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)
+
+#define	STAILQ_FIRST(head)	((head)->stqh_first)
+
+#define	STAILQ_FOREACH(var, head, field)				\
+	for((var) = STAILQ_FIRST((head));				\
+	   (var);							\
+	   (var) = STAILQ_NEXT((var), field))
+
+#define	STAILQ_INIT(head) do {						\
+	STAILQ_FIRST((head)) = NULL;					\
+	(head)->stqh_last = &STAILQ_FIRST((head));			\
+} while (0)
+
+#define	STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {		\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_NEXT((tqelm), field) = (elm);				\
+} while (0)
+
+#define	STAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL)	\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	STAILQ_NEXT((elm), field) = NULL;				\
+	STAILQ_LAST((head)) = (elm);					\
+	(head)->stqh_last = &STAILQ_NEXT((elm), field);			\
+} while (0)
+
+#define	STAILQ_LAST(head)	(*(head)->stqh_last)
+
+#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
+
+#define	STAILQ_REMOVE(head, elm, type, field) do {			\
+	if (STAILQ_FIRST((head)) == (elm)) {				\
+		STAILQ_REMOVE_HEAD(head, field);			\
+	}								\
+	else {								\
+		struct type *curelm = STAILQ_FIRST((head));		\
+		while (STAILQ_NEXT(curelm, field) != (elm))		\
+			curelm = STAILQ_NEXT(curelm, field);		\
+		if ((STAILQ_NEXT(curelm, field) =			\
+		     STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+			(head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+	}								\
+} while (0)
+
+#define	STAILQ_REMOVE_HEAD(head, field) do {				\
+	if ((STAILQ_FIRST((head)) =					\
+	     STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)		\
+		(head)->stqh_last = &STAILQ_FIRST((head));		\
+} while (0)
+
+#define	STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do {			\
+	if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL)	\
+		(head)->stqh_last = &STAILQ_FIRST((head));		\
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define	LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define	LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+
+#define	LIST_EMPTY(head)	((head)->lh_first == NULL)
+
+#define	LIST_FIRST(head)	((head)->lh_first)
+
+#define	LIST_FOREACH(var, head, field)					\
+	for ((var) = LIST_FIRST((head));				\
+	    (var);							\
+	    (var) = LIST_NEXT((var), field))
+
+#define	LIST_INIT(head) do {						\
+	LIST_FIRST((head)) = NULL;					\
+} while (0)
+
+#define	LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+		LIST_NEXT((listelm), field)->field.le_prev =		\
+		    &LIST_NEXT((elm), field);				\
+	LIST_NEXT((listelm), field) = (elm);				\
+	(elm)->field.le_prev = &LIST_NEXT((listelm), field);		\
+} while (0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	LIST_NEXT((elm), field) = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &LIST_NEXT((elm), field);		\
+} while (0)
+
+#define	LIST_INSERT_HEAD(head, elm, field) do {				\
+	if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)	\
+		LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+	LIST_FIRST((head)) = (elm);					\
+	(elm)->field.le_prev = &LIST_FIRST((head));			\
+} while (0)
+
+#define	LIST_NEXT(elm, field)	((elm)->field.le_next)
+
+#define	LIST_REMOVE(elm, field) do {					\
+	if (LIST_NEXT((elm), field) != NULL)				\
+		LIST_NEXT((elm), field)->field.le_prev = 		\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = LIST_NEXT((elm), field);		\
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define	TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define	TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define	TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_EMPTY(head)	((head)->tqh_first == NULL)
+
+#define	TAILQ_FIRST(head)	((head)->tqh_first)
+
+#define	TAILQ_FOREACH(var, head, field)					\
+	for ((var) = TAILQ_FIRST((head));				\
+	    (var);							\
+	    (var) = TAILQ_NEXT((var), field))
+
+#define	TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for ((var) = TAILQ_LAST((head), headname);			\
+	    (var);							\
+	    (var) = TAILQ_PREV((var), headname, field))
+
+#define	TAILQ_INIT(head) do {						\
+	TAILQ_FIRST((head)) = NULL;					\
+	(head)->tqh_last = &TAILQ_FIRST((head));			\
+} while (0)
+
+#define	TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+		TAILQ_NEXT((elm), field)->field.tqe_prev = 		\
+		    &TAILQ_NEXT((elm), field);				\
+	else								\
+		(head)->tqh_last = &TAILQ_NEXT((elm), field);		\
+	TAILQ_NEXT((listelm), field) = (elm);				\
+	(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);		\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	TAILQ_NEXT((elm), field) = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);		\
+} while (0)
+
+#define	TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)	\
+		TAILQ_FIRST((head))->field.tqe_prev =			\
+		    &TAILQ_NEXT((elm), field);				\
+	else								\
+		(head)->tqh_last = &TAILQ_NEXT((elm), field);		\
+	TAILQ_FIRST((head)) = (elm);					\
+	(elm)->field.tqe_prev = &TAILQ_FIRST((head));			\
+} while (0)
+
+#define	TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	TAILQ_NEXT((elm), field) = NULL;				\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &TAILQ_NEXT((elm), field);			\
+} while (0)
+
+#define	TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define	TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define	TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define	TAILQ_REMOVE(head, elm, field) do {				\
+	if ((TAILQ_NEXT((elm), field)) != NULL)				\
+		TAILQ_NEXT((elm), field)->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);		\
+} while (0)
+
+/*
+ * Circular queue declarations.
+ */
+#define	CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define	CIRCLEQ_HEAD_INITIALIZER(head)					\
+	{ (void *)&(head), (void *)&(head) }
+
+#define	CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_EMPTY(head)	((head)->cqh_first == (void *)(head))
+
+#define	CIRCLEQ_FIRST(head)	((head)->cqh_first)
+
+#define	CIRCLEQ_FOREACH(var, head, field)				\
+	for ((var) = CIRCLEQ_FIRST((head));				\
+	    (var) != (void *)(head);					\
+	    (var) = CIRCLEQ_NEXT((var), field))
+
+#define	CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
+	for ((var) = CIRCLEQ_LAST((head));				\
+	    (var) != (void *)(head);					\
+	    (var) = CIRCLEQ_PREV((var), field))
+
+#define	CIRCLEQ_INIT(head) do {						\
+	CIRCLEQ_FIRST((head)) = (void *)(head);				\
+	CIRCLEQ_LAST((head)) = (void *)(head);				\
+} while (0)
+
+#define	CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field);	\
+	CIRCLEQ_PREV((elm), field) = (listelm);				\
+	if (CIRCLEQ_NEXT((listelm), field) == (void *)(head))		\
+		CIRCLEQ_LAST((head)) = (elm);				\
+	else								\
+		CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\
+	CIRCLEQ_NEXT((listelm), field) = (elm);				\
+} while (0)
+
+#define	CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
+	CIRCLEQ_NEXT((elm), field) = (listelm);				\
+	CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field);	\
+	if (CIRCLEQ_PREV((listelm), field) == (void *)(head))		\
+		CIRCLEQ_FIRST((head)) = (elm);				\
+	else								\
+		CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\
+	CIRCLEQ_PREV((listelm), field) = (elm);				\
+} while (0)
+
+#define	CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head));		\
+	CIRCLEQ_PREV((elm), field) = (void *)(head);			\
+	if (CIRCLEQ_LAST((head)) == (void *)(head))			\
+		CIRCLEQ_LAST((head)) = (elm);				\
+	else								\
+		CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm);	\
+	CIRCLEQ_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	CIRCLEQ_NEXT((elm), field) = (void *)(head);			\
+	CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head));		\
+	if (CIRCLEQ_FIRST((head)) == (void *)(head))			\
+		CIRCLEQ_FIRST((head)) = (elm);				\
+	else								\
+		CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm);	\
+	CIRCLEQ_LAST((head)) = (elm);					\
+} while (0)
+
+#define	CIRCLEQ_LAST(head)	((head)->cqh_last)
+
+#define	CIRCLEQ_NEXT(elm,field)	((elm)->field.cqe_next)
+
+#define	CIRCLEQ_PREV(elm,field)	((elm)->field.cqe_prev)
+
+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
+	if (CIRCLEQ_NEXT((elm), field) == (void *)(head))		\
+		CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field);	\
+	else								\
+		CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) =	\
+		    CIRCLEQ_PREV((elm), field);				\
+	if (CIRCLEQ_PREV((elm), field) == (void *)(head))		\
+		CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field);	\
+	else								\
+		CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) =	\
+		    CIRCLEQ_NEXT((elm), field);				\
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/drivers/scsi/aic7xxx/scsi_iu.h b/drivers/scsi/aic7xxx/scsi_iu.h
new file mode 100644
index 0000000..0eafd3c
--- /dev/null
+++ b/drivers/scsi/aic7xxx/scsi_iu.h
@@ -0,0 +1,39 @@
+/*
+ * This file is in the public domain.
+ */
+#ifndef	_SCSI_SCSI_IU_H
+#define _SCSI_SCSI_IU_H 1
+
+struct scsi_status_iu_header
+{
+	u_int8_t reserved[2];
+	u_int8_t flags;
+#define	SIU_SNSVALID 0x2
+#define	SIU_RSPVALID 0x1
+	u_int8_t status;
+	u_int8_t sense_length[4];
+	u_int8_t pkt_failures_length[4];
+	u_int8_t pkt_failures[1];
+};
+
+#define SIU_PKTFAIL_OFFSET(siu) 12
+#define SIU_PKTFAIL_CODE(siu) (scsi_4btoul((siu)->pkt_failures) & 0xFF)
+#define		SIU_PFC_NONE			0
+#define		SIU_PFC_CIU_FIELDS_INVALID	2
+#define		SIU_PFC_TMF_NOT_SUPPORTED	4
+#define		SIU_PFC_TMF_FAILED		5
+#define		SIU_PFC_INVALID_TYPE_CODE	6
+#define		SIU_PFC_ILLEGAL_REQUEST		7
+#define SIU_SENSE_OFFSET(siu)				\
+    (12 + (((siu)->flags & SIU_RSPVALID)		\
+	? scsi_4btoul((siu)->pkt_failures_length)	\
+	: 0))
+
+#define	SIU_TASKMGMT_NONE		0x00
+#define	SIU_TASKMGMT_ABORT_TASK		0x01
+#define	SIU_TASKMGMT_ABORT_TASK_SET	0x02
+#define	SIU_TASKMGMT_CLEAR_TASK_SET	0x04
+#define	SIU_TASKMGMT_LUN_RESET		0x08
+#define	SIU_TASKMGMT_TARGET_RESET	0x20
+#define	SIU_TASKMGMT_CLEAR_ACA		0x40
+#endif /*_SCSI_SCSI_IU_H*/
diff --git a/drivers/scsi/aic7xxx/scsi_message.h b/drivers/scsi/aic7xxx/scsi_message.h
new file mode 100644
index 0000000..75811e2
--- /dev/null
+++ b/drivers/scsi/aic7xxx/scsi_message.h
@@ -0,0 +1,70 @@
+/*
+ * This file is in the public domain.
+ * $FreeBSD: src/sys/cam/scsi/scsi_message.h,v 1.2 2000/05/01 20:21:29 peter Exp $
+ */
+
+/* Messages (1 byte) */		     /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE		0x00 /* M/M */
+#define MSG_TASK_COMPLETE	0x00 /* M/M */ /* SPI3 Terminology */
+#define MSG_EXTENDED		0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER	0x02 /* O/O */
+#define MSG_RESTOREPOINTERS	0x03 /* O/O */
+#define MSG_DISCONNECT		0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR	0x05 /* M/M */
+#define MSG_ABORT		0x06 /* O/M */
+#define MSG_ABORT_TASK_SET	0x06 /* O/M */ /* SPI3 Terminology */
+#define MSG_MESSAGE_REJECT	0x07 /* M/M */
+#define MSG_NOOP		0x08 /* M/M */
+#define MSG_PARITY_ERROR	0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE	0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF	0x0b /* O/O */
+#define MSG_BUS_DEV_RESET	0x0c /* O/M */
+#define MSG_TARGET_RESET	0x0c /* O/M */ /* SPI3 Terminology */
+#define MSG_ABORT_TAG		0x0d /* O/O */
+#define MSG_ABORT_TASK		0x0d /* O/O */ /* SPI3 Terminology */
+#define MSG_CLEAR_QUEUE		0x0e /* O/O */
+#define MSG_CLEAR_TASK_SET	0x0e /* O/O */ /* SPI3 Terminology */
+#define MSG_INIT_RECOVERY	0x0f /* O/O */ /* Deprecated in SPI3 */
+#define MSG_REL_RECOVERY	0x10 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_TERM_IO_PROC	0x11 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_CLEAR_ACA		0x16 /* O/O */ /* SPI3 */
+#define MSG_LOGICAL_UNIT_RESET	0x17 /* O/O */ /* SPI3 */
+#define MSG_QAS_REQUEST		0x55 /* O/O */ /* SPI3 */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG	0x20 /* O/O */
+#define MSG_SIMPLE_TASK		0x20 /* O/O */ /* SPI3 Terminology */
+#define MSG_HEAD_OF_Q_TAG	0x21 /* O/O */
+#define MSG_HEAD_OF_QUEUE_TASK	0x21 /* O/O */ /* SPI3 Terminology */
+#define MSG_ORDERED_Q_TAG	0x22 /* O/O */
+#define MSG_ORDERED_TASK	0x22 /* O/O */ /* SPI3 Terminology */
+#define MSG_IGN_WIDE_RESIDUE	0x23 /* O/O */
+#define MSG_ACA_TASK		0x24 /* 0/0 */ /* SPI3 */
+
+/* Identify message */		     /* M/M */	
+#define MSG_IDENTIFYFLAG	0x80 
+#define MSG_IDENTIFY_DISCFLAG	0x40 
+#define MSG_IDENTIFY(lun, disc)	(((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m)	((m) & MSG_IDENTIFYFLAG)
+#define MSG_IDENTIFY_LUNMASK	0x3F 
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR		0x01
+#define MSG_EXT_SDTR_LEN	0x03
+
+#define MSG_EXT_WDTR		0x03
+#define MSG_EXT_WDTR_LEN	0x02
+#define MSG_EXT_WDTR_BUS_8_BIT	0x00
+#define MSG_EXT_WDTR_BUS_16_BIT	0x01
+#define MSG_EXT_WDTR_BUS_32_BIT	0x02 /* Deprecated in SPI3 */
+
+#define MSG_EXT_PPR		0x04 /* SPI3 */
+#define MSG_EXT_PPR_LEN		0x06
+#define	MSG_EXT_PPR_PCOMP_EN	0x80
+#define	MSG_EXT_PPR_RTI		0x40
+#define	MSG_EXT_PPR_RD_STRM	0x20
+#define	MSG_EXT_PPR_WR_FLOW	0x10
+#define	MSG_EXT_PPR_HOLD_MCS	0x08
+#define	MSG_EXT_PPR_QAS_REQ	0x04
+#define	MSG_EXT_PPR_DT_REQ	0x02
+#define MSG_EXT_PPR_IU_REQ	0x01