summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic7xxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/scsi/aic7xxx
parentInitial commit. (diff)
downloadlinux-upstream.tar.xz
linux-upstream.zip
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--drivers/scsi/aic7xxx/.gitignore7
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic79xx86
-rw-r--r--drivers/scsi/aic7xxx/Kconfig.aic7xxx91
-rw-r--r--drivers/scsi/aic7xxx/Makefile87
-rw-r--r--drivers/scsi/aic7xxx/aic7770.c385
-rw-r--r--drivers/scsi/aic7xxx/aic7770_osm.c156
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.h1465
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.reg4281
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.seq2290
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c10724
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_inline.h172
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c2851
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.h658
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm_pci.c378
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_pci.c1004
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_pci.h72
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_proc.c316
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg.h_shipped1810
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_reg_print.c_shipped745
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_seq.h_shipped1189
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.h1273
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.reg1761
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.seq2399
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_93cx6.c318
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_93cx6.h102
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_core.c7901
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_inline.h97
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c2577
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.h674
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm_pci.c447
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_pci.c2460
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_pci.h125
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_proc.c343
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped912
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped413
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped1307
-rw-r--r--drivers/scsi/aic7xxx/aicasm/Makefile86
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.c843
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm.h91
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_gram.y1999
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h218
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y161
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l153
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_scan.l618
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c689
-rw-r--r--drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h205
-rw-r--r--drivers/scsi/aic7xxx/aiclib.h180
-rw-r--r--drivers/scsi/aic7xxx/cam.h111
-rw-r--r--drivers/scsi/aic7xxx/queue.h501
-rw-r--r--drivers/scsi/aic7xxx/scsi_iu.h39
-rw-r--r--drivers/scsi/aic7xxx/scsi_message.h40
51 files changed, 57810 insertions, 0 deletions
diff --git a/drivers/scsi/aic7xxx/.gitignore b/drivers/scsi/aic7xxx/.gitignore
new file mode 100644
index 000000000..9aa780221
--- /dev/null
+++ b/drivers/scsi/aic7xxx/.gitignore
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+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 000000000..a47dbd500
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# 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.rst 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 000000000..0cfd92ce7
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# 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"
+ 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.rst 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 000000000..e0188ecd8
--- /dev/null
+++ b/drivers/scsi/aic7xxx/Makefile
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# 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
+
+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$(srctree)/$(src) -r $(obj)/aic7xxx_reg.h \
+ $(aicasm-7xxx-opts-y) -o $(obj)/aic7xxx_seq.h \
+ $(srctree)/$(src)/aic7xxx.seq
+
+$(aic7xxx-gen-y): $(objtree)/$(obj)/aic7xxx_seq.h
+ @true
+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$(srctree)/$(src) -r $(obj)/aic79xx_reg.h \
+ $(aicasm-79xx-opts-y) -o $(obj)/aic79xx_seq.h \
+ $(srctree)/$(src)/aic79xx.seq
+
+$(aic79xx-gen-y): $(objtree)/$(obj)/aic79xx_seq.h
+ @true
+else
+$(obj)/aic79xx_reg_print.c: $(src)/aic79xx_reg_print.c_shipped
+endif
+
+$(obj)/aicasm/aicasm: $(srctree)/$(src)/aicasm/*.[chyl]
+ $(MAKE) -C $(srctree)/$(src)/aicasm OUTDIR=$(shell pwd)/$(obj)/aicasm/
diff --git a/drivers/scsi/aic7xxx/aic7770.c b/drivers/scsi/aic7xxx/aic7770.c
new file mode 100644
index 000000000..176704b24
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7770.c
@@ -0,0 +1,385 @@
+/*
+ * 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$
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+#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 000000000..bdd177e3d
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7770_osm.c
@@ -0,0 +1,156 @@
+/*
+ * 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);
+ ahc->dev = dev;
+ 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 000000000..59e932181
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx.h
@@ -0,0 +1,1465 @@
+/*
+ * 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;
+ 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);
+BSD_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;
+
+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];
+ 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
+ */
+ BSD_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.
+ */
+ struct timer_list 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 version 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 *);
+void __maybe_unused ahd_pci_suspend(struct ahd_softc *);
+void __maybe_unused ahd_pci_resume(struct ahd_softc *);
+
+/************************** 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);
+int __maybe_unused ahd_suspend(struct ahd_softc *ahd);
+void __maybe_unused ahd_resume(struct ahd_softc *ahd);
+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 000000000..7e12c31cc
--- /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 000000000..3a36d9362
--- /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 000000000..3e3100dbf
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -0,0 +1,10724 @@
+/*
+ * 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 $
+ */
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+
+/***************************** Lookup Tables **********************************/
+static const char *const ahd_chip_names[] =
+{
+ "NONE",
+ "aic7901",
+ "aic7902",
+ "aic7901A"
+};
+
+/*
+ * 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, NOP, "in Data-out phase" },
+ { P_DATAIN, INITIATOR_ERROR, "in Data-in phase" },
+ { P_DATAOUT_DT, NOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, INITIATOR_ERROR, "in DT Data-in phase" },
+ { P_COMMAND, NOP, "in Command phase" },
+ { P_MESGOUT, NOP, "in Message-out phase" },
+ { P_STATUS, INITIATOR_ERROR, "in Status phase" },
+ { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
+ { P_BUSFREE, NOP, "while idle" },
+ { 0, NOP, "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 void ahd_stat_timer(struct timer_list *t);
+#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, NOP); /* 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;
+
+ addr = ahd_le64toh(sg_list[i].addr);
+ 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;
+ 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);
+ 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] = 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] = TARGET_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);
+ fallthrough;
+ 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;
+ fallthrough;
+ 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 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);
+ 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 = INITIATOR_ERROR;
+ 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 NOP.
+ */
+ ahd->send_msg_perror = msg_out;
+ if (scb != NULL && msg_out == INITIATOR_ERROR)
+ 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, ABORT_TASK, TRUE)
+ || ahd_sent_msg(ahd, AHDMSG_1B, ABORT_TASK_SET, 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 == ABORT_TASK ? "" : " Tag");
+
+ if (sent_msg == ABORT_TASK)
+ 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,
+ TARGET_RESET, TRUE)) {
+ 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, EXTENDED_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, EXTENDED_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, EXTENDED_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,
+ INITIATOR_ERROR, 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,
+ 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] = 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];
+ 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_tmode_tstate *tstate;
+
+ ahd_scb_devinfo(ahd, &devinfo, pending_scb);
+ 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++] = NOP;
+ 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++] = TARGET_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++] = ABORT_TASK;
+ } else {
+ ahd->msgout_buf[ahd->msgout_index++] = ABORT_TASK_SET;
+ }
+ 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, NOP);
+ 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 == INITIATOR_ERROR)
+ 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] == EXTENDED_MESSAGE) {
+ 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] >= SIMPLE_QUEUE_TAG
+ && ahd->msgout_buf[index] <= IGNORE_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 DISCONNECT:
+ case SAVE_POINTERS:
+ case COMMAND_COMPLETE:
+ case RESTORE_POINTERS:
+ case IGNORE_WIDE_RESIDUE:
+ /*
+ * End our message loop as these are messages
+ * the sequencer handles on its own.
+ */
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MESSAGE_REJECT:
+ response = ahd_handle_msg_reject(ahd, devinfo);
+ fallthrough;
+ case NOP:
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ case EXTENDED_MESSAGE:
+ {
+ /* Wait for enough of the message to begin validation */
+ if (ahd->msgin_index < 2)
+ break;
+ switch (ahd->msgin_buf[2]) {
+ case EXTENDED_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, EXTENDED_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 EXTENDED_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, EXTENDED_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 EXTENDED_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, EXTENDED_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 TARGET_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 ABORT_TASK:
+ case ABORT_TASK_SET:
+ case CLEAR_TASK_SET:
+ {
+ int tag;
+
+ /* Target mode messages */
+ if (devinfo->role != ROLE_TARGET) {
+ reject = TRUE;
+ break;
+ }
+ tag = SCB_LIST_NULL;
+ if (ahd->msgin_buf[0] == ABORT_TASK)
+ 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 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 TERMINATE_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] = 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, EXTENDED_PPR, /*full*/FALSE)) {
+ if (ahd_sent_msg(ahd, AHDMSG_EXT, EXTENDED_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, EXTENDED_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, EXTENDED_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 & SIMPLE_QUEUE_TAG) != 0) {
+ int tag_type;
+ int mask;
+
+ tag_type = (scb->hscb->control & SIMPLE_QUEUE_TAG);
+
+ if (tag_type == SIMPLE_QUEUE_TAG) {
+ 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 == ORDERED_QUEUE_TAG
+ ? "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*/SIMPLE_QUEUE_TAG);
+ 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,
+ TARGET_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;
+
+ ahd = kzalloc(sizeof(*ahd), GFP_ATOMIC);
+ if (!ahd) {
+ printk("aic7xxx: cannot malloc softc!\n");
+ kfree(name);
+ return NULL;
+ }
+
+ ahd->seep_config = kmalloc(sizeof(*ahd->seep_config), GFP_ATOMIC);
+ if (ahd->seep_config == NULL) {
+ kfree(ahd);
+ 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;
+ timer_setup(&ahd->stat_timer, ahd_stat_timer, 0);
+ 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;
+
+#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
+ if (ahd_platform_alloc(ahd, platform_arg) != 0) {
+ ahd_free(ahd);
+ ahd = NULL;
+ }
+ 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)
+{
+ 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);
+ break;
+ case 1:
+ break;
+ case 0:
+ break;
+ }
+
+ 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
+ kfree(ahd->name);
+ kfree(ahd->seep_config);
+ kfree(ahd->saved_stack);
+ kfree(ahd);
+ return;
+}
+
+static void
+ahd_shutdown(void *arg)
+{
+ struct ahd_softc *ahd;
+
+ ahd = (struct ahd_softc *)arg;
+
+ /*
+ * Stop periodic timer callbacks.
+ */
+ del_timer_sync(&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. Disable
+ * 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;
+
+ 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;
+ 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 *******************************/
+static void
+ahd_timer_reset(struct timer_list *timer, int usec)
+{
+ del_timer(timer);
+ timer->expires = jiffies + (usec * HZ)/1000000;
+ 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_array(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;
+
+ 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++;
+ fallthrough;
+ 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);
+ 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;
+}
+
+int __maybe_unused
+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 __maybe_unused
+ahd_resume(struct ahd_softc *ahd)
+{
+ ahd_reset(ahd, /*reinit*/TRUE);
+ ahd_intr_enable(ahd, TRUE);
+ ahd_restart(ahd);
+}
+
+/************************** 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);
+ fallthrough;
+ 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(struct timer_list *t)
+{
+ struct ahd_softc *ahd = from_timer(ahd, t, stat_timer);
+ 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_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");
+ break;
+ default:
+ break;
+ }
+ }
+ if (siu->status == SAM_STAT_GOOD)
+ 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 SAM_STAT_COMMAND_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
+ {
+ 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 SAM_STAT_GOOD:
+ 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 == TARGET_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 = kmemdup(cs_table, cs_count, GFP_ATOMIC);
+ if (ahd->critical_sections == NULL)
+ panic("ahd_loadseq: Could not malloc");
+ }
+ 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 000000000..09335a3c8
--- /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 000000000..f2f3405cd
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -0,0 +1,2851 @@
+/*
+ * 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 struct ahd_linux_iocell_opts aic79xx_iocell_info[] __ro_after_init =
+{
+ 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)
+{
+ 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->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)
+{
+ 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[])
+{
+ int heads;
+ int sectors;
+ int cylinders;
+ int extended;
+ struct ahd_softc *ahd;
+
+ ahd = *((struct ahd_softc **)sdev->host->hostdata);
+
+ if (scsi_partsize(bdev, capacity, geom))
+ return 0;
+
+ 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)
+{
+ return ahd_linux_queue_abort_cmd(cmd);
+}
+
+/*
+ * 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;
+ struct ahd_initiator_tinfo *tinfo;
+ struct ahd_tmode_tstate *tstate;
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ reset_scb = NULL;
+
+ 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,
+ .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 = dma_alloc_coherent(&ahd->dev_softc->dev, dmat->maxsize, mapp,
+ GFP_ATOMIC);
+ 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)
+{
+ dma_free_coherent(&ahd->dev_softc->dev, 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 |= ORDERED_QUEUE_TAG;
+ dev->commands_since_idle_or_otag = 0;
+ } else {
+ hscb->control |= SIMPLE_QUEUE_TAG;
+ }
+ }
+
+ 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) {
+#ifdef AHD_REPORT_UNDERFLOWS
+ uint32_t amount_xferred;
+
+ amount_xferred =
+ ahd_get_transfer_length(scb) - ahd_get_residual(scb);
+#endif
+ 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) != SAM_STAT_TASK_SET_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 SAM_STAT_CHECK_CONDITION:
+ case SAM_STAT_COMMAND_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);
+ set_status_byte(cmd, SAM_STAT_CHECK_CONDITION);
+
+#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 SAM_STAT_TASK_SET_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, SAM_STAT_GOOD);
+ 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, SAM_STAT_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;
+ struct scsi_sense_data *sense;
+
+ /*
+ * 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 SAM_STAT_COMMAND_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
+ 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);
+
+ 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 cdb_byte;
+ int retval = SUCCESS;
+ 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");
+ goto done;
+ }
+
+ /*
+ * 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 done;
+ }
+
+ 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 done;
+ }
+
+ 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);
+ 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.
+ */
+ 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;
+ }
+
+
+ ahd_restore_modes(ahd, saved_modes);
+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 000000000..793fe1999
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.h
@@ -0,0 +1,658 @@
+/*
+ * 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
+
+#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 dma_alloc_coherent() 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)
+
+/********************************** Includes **********************************/
+#ifdef CONFIG_AIC79XX_REG_PRETTY_PRINT
+#define AIC_DEBUG_REGISTERS 1
+#else
+#define AIC_DEBUG_REGISTERS 0
+#endif
+#include "aic79xx.h"
+
+/***************************** 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
+
+/****************************** 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 *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 000000000..b92e2e3c3
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
@@ -0,0 +1,378 @@
+/*
+ * 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);
+
+static int __maybe_unused
+ahd_linux_pci_dev_suspend(struct device *dev)
+{
+ struct ahd_softc *ahd = dev_get_drvdata(dev);
+ int rc;
+
+ if ((rc = ahd_suspend(ahd)))
+ return rc;
+
+ ahd_pci_suspend(ahd);
+
+ return rc;
+}
+
+static int __maybe_unused
+ahd_linux_pci_dev_resume(struct device *dev)
+{
+ struct ahd_softc *ahd = dev_get_drvdata(dev);
+
+ ahd_pci_resume(ahd);
+
+ ahd_resume(ahd);
+
+ return 0;
+}
+
+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 SIMPLE_DEV_PM_OPS(ahd_linux_pci_dev_pm_ops,
+ ahd_linux_pci_dev_suspend,
+ ahd_linux_pci_dev_resume);
+
+static struct pci_driver aic79xx_pci_driver = {
+ .name = "aic79xx",
+ .probe = ahd_linux_pci_dev_probe,
+ .driver.pm = &ahd_linux_pci_dev_pm_ops,
+ .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(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 000000000..5fad41b1a
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_pci.c
@@ -0,0 +1,1004 @@
+/*
+ * 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 $
+ */
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#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, PCI_SUBSYSTEM_VENDOR_ID, /*bytes*/2);
+ subdevice = ahd_pci_read_config(pci, PCI_SUBSYSTEM_ID, /*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)
+{
+ u_int command;
+ uint32_t devconfig;
+ uint16_t subvendor;
+ int error;
+
+ ahd->description = entry->name;
+ /*
+ * Record if this is an HP board.
+ */
+ subvendor = ahd_pci_read_config(ahd->dev_softc,
+ PCI_SUBSYSTEM_VENDOR_ID, /*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);
+}
+
+void __maybe_unused
+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 __maybe_unused
+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);
+}
+
+/*
+ * 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 000000000..16b7c70a6
--- /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 000000000..746d0ca2a
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_proc.c
@@ -0,0 +1,316 @@
+/*
+ * 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 000000000..ddcd5a770
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_reg.h_shipped
@@ -0,0 +1,1810 @@
+/*
+ * 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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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
+
+
+#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 CLRERR 0x04
+#define CLRCIOPARERR 0x80
+#define CLRCIOACCESFAIL 0x40
+#define CLRMPARERR 0x20
+#define CLRDPARERR 0x10
+#define CLRSQPARERR 0x08
+#define CLRILLOPCODE 0x04
+#define CLRDSCTMOUT 0x02
+
+#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 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 ARBCTL 0x1b
+#define RESET_HARB 0x80
+#define RETRY_SWEN 0x08
+#define USE_TIME 0x07
+
+#define SG_CACHE_SHADOW 0x1b
+#define ODD_SEG 0x04
+#define LAST_SEG 0x02
+#define LAST_SEG_DONE 0x01
+
+#define SG_CACHE_PRE 0x1b
+
+#define TYPEPTR 0x20
+
+#define LQIN 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 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 SCSBIST0 0x39
+#define GSBISTERR 0x40
+#define GSBISTDONE 0x20
+#define GSBISTRUN 0x10
+#define OSBISTERR 0x04
+#define OSBISTDONE 0x02
+#define OSBISTRUN 0x01
+
+#define SCSISEQ0 0x3a
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENARBO 0x20
+#define FORCEBUSFREE 0x10
+#define SCSIRSTO 0x01
+
+#define SCSBIST1 0x3a
+#define NTBISTERR 0x04
+#define NTBISTDONE 0x02
+#define NTBISTRUN 0x01
+
+#define SCSISEQ1 0x3b
+
+#define BUSINITID 0x3c
+
+#define SXFRCTL0 0x3c
+#define DFON 0x80
+#define DFPEXP 0x40
+#define BIOSCANCELEN 0x10
+#define SPIOEN 0x08
+
+#define DLCOUNT 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 MULTARGID 0x40
+
+#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 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 SIMODE0 0x4b
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENIOERR 0x08
+#define ENOVERRUN 0x04
+#define ENSPIORDY 0x02
+#define ENARBDO 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 CLRSINT0 0x4b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRIOERR 0x08
+#define CLROVERRUN 0x04
+#define CLRSPIORDY 0x02
+#define CLRARBDO 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 CLRSINT1 0x4c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRSTRB2FAST 0x02
+#define CLRREQINIT 0x01
+
+#define SIMODE2 0x4d
+#define ENWIDE_RES 0x04
+#define ENSDONE 0x02
+#define ENDMADONE 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 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 LQOSTATE 0x4f
+
+#define SOFFCNT 0x4f
+
+#define LQISTAT0 0x50
+#define LQIATNQAS 0x20
+#define LQICRCT1 0x10
+#define LQICRCT2 0x08
+#define LQIBADLQT 0x04
+#define LQIATNLQ 0x02
+#define LQIATNCMD 0x01
+
+#define LQIMODE0 0x50
+#define ENLQIATNQASK 0x20
+#define ENLQICRCT1 0x10
+#define ENLQICRCT2 0x08
+#define ENLQIBADLQT 0x04
+#define ENLQIATNLQ 0x02
+#define ENLQIATNCMD 0x01
+
+#define CLRLQIINT0 0x50
+#define CLRLQIATNQAS 0x20
+#define CLRLQICRCT1 0x10
+#define CLRLQICRCT2 0x08
+#define CLRLQIBADLQT 0x04
+#define CLRLQIATNLQ 0x02
+#define CLRLQIATNCMD 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 SIMODE3 0x53
+#define ENNTRAMPERR 0x02
+#define ENOSRAMPERR 0x01
+
+#define SSTAT3 0x53
+#define NTRAMPERR 0x02
+#define OSRAMPERR 0x01
+
+#define CLRSINT3 0x53
+#define CLRNTRAMPERR 0x02
+#define CLROSRAMPERR 0x01
+
+#define CLRLQOINT0 0x54
+#define CLRLQOTARGSCBPERR 0x10
+#define CLRLQOSTOPT2 0x08
+#define CLRLQOATNLQ 0x04
+#define CLRLQOATNPKT 0x02
+#define CLRLQOTCRC 0x01
+
+#define LQOSTAT0 0x54
+#define LQOTARGSCBPERR 0x10
+#define LQOSTOPT2 0x08
+#define LQOATNLQ 0x04
+#define LQOATNPKT 0x02
+#define LQOTCRC 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 CLRLQOINT1 0x55
+#define CLRLQOINITSCBPERR 0x10
+#define CLRLQOSTOPI2 0x08
+#define CLRLQOBADQAS 0x04
+#define CLRLQOBUSFREE 0x02
+#define CLRLQOPHACHGINPKT 0x01
+
+#define LQOSTAT1 0x55
+#define LQOINITSCBPERR 0x10
+#define LQOSTOPI2 0x08
+#define LQOBADQAS 0x04
+#define LQOBUSFREE 0x02
+#define LQOPHACHGINPKT 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 SEQIMODE 0x5c
+#define ENCTXTDONE 0x40
+#define ENSAVEPTRS 0x20
+#define ENCFG4DATA 0x10
+#define ENCFG4ISTAT 0x08
+#define ENCFG4TSTAT 0x04
+#define ENCFG4ICMD 0x02
+#define ENCFG4TCMD 0x01
+
+#define CURRSCB 0x5c
+
+#define CRCCONTROL 0x5d
+#define CRCVALCHKEN 0x40
+
+#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 DFFTAG 0x5e
+
+#define SCSITEST 0x5e
+#define CNTRTEST 0x08
+#define SEL_TXPLL_DEBUG 0x04
+
+#define LASTSCB 0x5e
+
+#define IOPDNCTL 0x5f
+#define DISABLE_OE 0x80
+#define PDN_IDIST 0x04
+#define PDN_DIFFSENSE 0x01
+
+#define DGRPCRCI 0x60
+
+#define NEGOADDR 0x60
+
+#define SHADDR 0x60
+
+#define NEGPERIOD 0x61
+
+#define NEGOFFSET 0x62
+
+#define PACKCRCI 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 PLL960CTL1 0x69
+
+#define TOWNID 0x69
+
+#define PLL960CNT0 0x6a
+
+#define XSIG 0x6a
+
+#define SELOID 0x6b
+
+#define FAIRNESS 0x6c
+
+#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 PLL400CTL1 0x6d
+#define PLL_CNTEN 0x80
+#define PLL_CNTCLR 0x40
+#define PLL_RST 0x01
+
+#define UNFAIRNESS 0x6e
+
+#define PLL400CNT0 0x6e
+
+#define HADDR 0x70
+
+#define HODMAADR 0x70
+
+#define PLLDELAY 0x70
+#define SPLIT_DROP_REQ 0x80
+
+#define HCNT 0x78
+
+#define HODMACNT 0x78
+
+#define HODMAEN 0x7a
+
+#define SGHADDR 0x7c
+
+#define SCBHADDR 0x7c
+
+#define SGHCNT 0x84
+
+#define SCBHCNT 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 OVLYRXMSG0 0x90
+
+#define DCHRXMSG0 0x90
+
+#define ROENABLE 0x90
+#define MSIROEN 0x20
+#define OVLYROEN 0x10
+#define CMCROEN 0x08
+#define SGROEN 0x04
+#define DCH1ROEN 0x02
+#define DCH0ROEN 0x01
+
+#define OVLYRXMSG1 0x91
+
+#define CMCRXMSG1 0x91
+
+#define DCHRXMSG1 0x91
+
+#define NSENABLE 0x91
+#define MSINSEN 0x20
+#define OVLYNSEN 0x10
+#define CMCNSEN 0x08
+#define SGNSEN 0x04
+#define DCH1NSEN 0x02
+#define DCH0NSEN 0x01
+
+#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 CMCSEQBCNT 0x94
+
+#define OVLYSEQBCNT 0x94
+
+#define DCHSEQBCNT 0x94
+
+#define DCHSPLTSTAT0 0x96
+
+#define OVLYSPLTSTAT0 0x96
+
+#define CMCSPLTSTAT0 0x96
+
+#define OVLYSPLTSTAT1 0x97
+
+#define DCHSPLTSTAT1 0x97
+
+#define CMCSPLTSTAT1 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 SLVSPLTOUTATTR0 0x9c
+#define LOWER_BCNT 0xff
+
+#define SGSEQBCNT 0x9c
+
+#define SLVSPLTOUTATTR1 0x9d
+#define CMPLT_DNUM 0xf8
+#define CMPLT_FNUM 0x07
+
+#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 SLVSPLTOUTATTR2 0x9e
+#define CMPLT_BNUM 0xff
+
+#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 DATA_COUNT_ODD 0xa7
+
+#define TARGPCISTAT 0xa7
+#define DPE 0x80
+#define SSE 0x40
+#define STA 0x08
+#define TWATERR 0x02
+
+#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 CCSCBADDR 0xac
+
+#define CCSCBADR_BK 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_EWDS 0x40
+#define SEEOP_WALL 0x40
+#define SEEOP_EWEN 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 DSPFLTRCTL 0xc0
+#define FLTRDISABLE 0x20
+#define EDGESENSE 0x10
+#define DSPFCNTSEL 0x0f
+
+#define DFWADDR 0xc0
+
+#define DSPDATACTL 0xc1
+#define BYPASSENAB 0x80
+#define DESQDIS 0x10
+#define RCVROFFSTDIS 0x04
+#define XMITOFFSTDIS 0x02
+
+#define DSPREQCTL 0xc2
+#define MANREQCTL 0xc0
+#define MANREQDLY 0x3f
+
+#define DFRADDR 0xc2
+
+#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 DFPTRS 0xc8
+
+#define RCVRBIASCALC 0xc8
+
+#define DFBKPTR 0xc9
+
+#define SKEWCALC 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 AHD_PCI_CONFIG_BASE 0x100
+
+#define SRAM_BASE 0x100
+
+#define WAITING_SCB_TAILS 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 SDMAENACK 0x10
+#define SDMAEN 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_RESIDUAL_DATACNT 0x180
+#define SCB_CDB_STORE 0x180
+#define SCB_HOST_CDB_PTR 0x180
+
+#define SCB_BASE 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 STIMESEL_SHIFT 0x03
+#define STIMESEL_MIN 0x18
+#define INVALID_ADDR 0x80
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define AHD_PRECOMP_MASK 0x07
+#define TARGET_DATA_IN 0x01
+#define SEEOP_EWEN_ADDR 0xc0
+#define NUMDSPS 0x14
+#define DST_MODE_SHIFT 0x04
+#define CCSCBADDR_MAX 0x80
+#define AHD_ANNEXCOL_PER_DEV0 0x04
+#define TARGET_CMD_CMPLT 0xfe
+#define SEEOP_WRAL_ADDR 0x40
+#define BUS_8_BIT 0x00
+#define AHD_TIMER_MAX_US 0x18ffe7
+#define AHD_TIMER_MAX_TICKS 0xffff
+#define AHD_SENSE_BUFSIZE 0x100
+#define AHD_PRECOMP_SHIFT 0x00
+#define AHD_PRECOMP_CUTBACK_37 0x07
+#define AHD_ANNEXCOL_PRECOMP_SLEW 0x04
+#define AHD_AMPLITUDE_DEF 0x07
+#define WRTBIASCTL_HP_DEFAULT 0x00
+#define TID_SHIFT 0x04
+#define STATUS_QUEUE_FULL 0x28
+#define STATUS_BUSY 0x08
+#define SEEOP_EWDS_ADDR 0x00
+#define SCB_TRANSFER_SIZE_FULL_LUN 0x38
+#define MK_MESSAGE_BIT_OFFSET 0x04
+#define MAX_OFFSET_PACED 0xfe
+#define MAX_OFFSET_NON_PACED 0x7f
+#define LUNLEN_SINGLE_LEVEL_LUN 0x0f
+#define CCSGADDR_MAX 0x80
+#define B_CURRFIFO_0 0x02
+#define BUS_32_BIT 0x02
+#define AHD_TIMER_US_PER_TICK 0x19
+#define AHD_SLEWRATE_SHIFT 0x03
+#define AHD_SLEWRATE_MASK 0x78
+#define AHD_SLEWRATE_DEF_REVA 0x08
+#define AHD_PRECOMP_CUTBACK_29 0x06
+#define AHD_NUM_PER_DEV_ANNEXCOLS 0x04
+#define AHD_ANNEXCOL_AMPLITUDE 0x06
+#define AHD_AMPLITUDE_SHIFT 0x00
+#define AHD_AMPLITUDE_MASK 0x07
+#define STIMESEL_BUG_ADJ 0x08
+#define STATUS_PKT_SENSE 0xff
+#define SRC_MODE_SHIFT 0x00
+#define SEEOP_ERAL_ADDR 0x80
+#define NVRAM_SCB_OFFSET 0x2c
+#define MAX_OFFSET_PACED_BUG 0x7f
+#define CCSGRAM_MAXSEGS 0x10
+#define AHD_SLEWRATE_DEF_REVB 0x08
+#define AHD_PRECOMP_CUTBACK_17 0x04
+#define SCB_TRANSFER_SIZE_1BYTE_LUN 0x30
+#define PKT_OVERRUN_BUFSIZE 0x200
+#define MAX_OFFSET 0xfe
+#define HOST_MSG 0xff
+#define BUS_16_BIT 0x01
+
+
+/* Downloaded Constant Definitions */
+#define SG_SIZEOF 0x04
+#define SG_PREFETCH_ALIGN_MASK 0x02
+#define SG_PREFETCH_CNT_LIMIT 0x01
+#define CACHELINE_MASK 0x07
+#define SCB_TRANSFER_SIZE 0x06
+#define PKT_OVERRUN_BUFOFFSET 0x05
+#define SG_PREFETCH_ADDR_MASK 0x03
+#define SG_PREFETCH_CNT 0x00
+#define DOWNLOAD_CONST_COUNT 0x08
+
+
+/* Exported Labels */
+#define LABEL_timer_isr 0x28b
+#define LABEL_seq_isr 0x28f
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 000000000..2e0c58905
--- /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 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 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 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 000000000..fd64a950e
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic79xx_seq.h_shipped
@@ -0,0 +1,1189 @@
+/*
+ * 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 }
+};
+
+#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(critical_sections)
diff --git a/drivers/scsi/aic7xxx/aic7xxx.h b/drivers/scsi/aic7xxx/aic7xxx.h
new file mode 100644
index 000000000..9bc755a0a
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx.h
@@ -0,0 +1,1273 @@
+/*
+ * 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;
+ 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 void ahc_callback_t (void *);
+
+struct ahc_softc {
+ bus_space_tag_t tag;
+ bus_space_handle_t bsh;
+ struct scb_data *scb_data;
+
+ struct scb *next_queued_scb;
+
+ /*
+ * SCBs that have been sent to the controller
+ */
+ BSD_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;
+ struct device *dev;
+
+ /*
+ * 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 *);
+void __maybe_unused ahc_pci_resume(struct ahc_softc *ahc);
+
+/*************************** 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);
+int __maybe_unused ahc_suspend(struct ahc_softc *ahc);
+int __maybe_unused ahc_resume(struct ahc_softc *ahc);
+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 000000000..00fde2243
--- /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 configuration 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 000000000..e60041e8f
--- /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 000000000..11ddffbcc
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
@@ -0,0 +1,318 @@
+/*
+ * 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.
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+/*
+ * 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 000000000..859c43ccd
--- /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 000000000..a396f048a
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_core.c
@@ -0,0 +1,7901 @@
+/*
+ * 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 $
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+
+/***************************** 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"
+};
+
+/*
+ * 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, NOP, "in Data-out phase" },
+ { P_DATAIN, INITIATOR_ERROR, "in Data-in phase" },
+ { P_DATAOUT_DT, NOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, INITIATOR_ERROR, "in DT Data-in phase" },
+ { P_COMMAND, NOP, "in Command phase" },
+ { P_MESGOUT, NOP, "in Message-out phase" },
+ { P_STATUS, INITIATOR_ERROR, "in Status phase" },
+ { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
+ { P_BUSFREE, NOP, "while idle" },
+ { 0, NOP, "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)
+ | (((uint64_t)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, NOP); /* 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 SAM_STAT_GOOD:
+ printk("%s: Interrupted for status of 0???\n",
+ ahc_name(ahc));
+ break;
+ case SAM_STAT_COMMAND_TERMINATED:
+ case SAM_STAT_CHECK_CONDITION:
+ {
+ 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] = TARGET_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 "
+ "received\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 = INITIATOR_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.
+ */
+ if (mesg_out != NOP) {
+ 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, ABORT_TASK, TRUE)
+ || ahc_sent_msg(ahc, AHCMSG_1B, ABORT_TASK_SET, TRUE)) {
+ if (ahc->msgout_buf[ahc->msgout_index - 1]
+ == ABORT_TASK)
+ 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,
+ TARGET_RESET, TRUE)) {
+ 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,
+ EXTENDED_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,
+ EXTENDED_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,
+ EXTENDED_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];
+ 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.
+ */
+static 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++] = TARGET_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++] = ABORT_TASK;
+ else
+ ahc->msgout_buf[ahc->msgout_index++] = ABORT_TASK_SET;
+ 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, NOP);
+ 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] = 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] == EXTENDED_MESSAGE) {
+ 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] >= SIMPLE_QUEUE_TAG
+ && ahc->msgout_buf[index] <= IGNORE_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 DISCONNECT:
+ case SAVE_POINTERS:
+ case COMMAND_COMPLETE:
+ case RESTORE_POINTERS:
+ case IGNORE_WIDE_RESIDUE:
+ /*
+ * End our message loop as these are messages
+ * the sequencer handles on its own.
+ */
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MESSAGE_REJECT:
+ response = ahc_handle_msg_reject(ahc, devinfo);
+ fallthrough;
+ case NOP:
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ case EXTENDED_MESSAGE:
+ {
+ /* Wait for enough of the message to begin validation */
+ if (ahc->msgin_index < 2)
+ break;
+ switch (ahc->msgin_buf[2]) {
+ case EXTENDED_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, EXTENDED_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 EXTENDED_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, EXTENDED_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 EXTENDED_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, EXTENDED_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 TARGET_RESET:
+ ahc_handle_devreset(ahc, devinfo,
+ CAM_BDR_SENT,
+ "Bus Device Reset Received",
+ /*verbose_level*/0);
+ ahc_restart(ahc);
+ done = MSGLOOP_TERMINATED;
+ break;
+ case ABORT_TASK:
+ case ABORT_TASK_SET:
+ case CLEAR_QUEUE_TASK_SET:
+ {
+ int tag;
+
+ /* Target mode messages */
+ if (devinfo->role != ROLE_TARGET) {
+ reject = TRUE;
+ break;
+ }
+ tag = SCB_LIST_NULL;
+ if (ahc->msgin_buf[0] == ABORT_TASK)
+ 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 TERMINATE_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] = 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, EXTENDED_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, EXTENDED_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, EXTENDED_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 & SIMPLE_QUEUE_TAG) != 0) {
+ int tag_type;
+ int mask;
+
+ tag_type = (scb->hscb->control & SIMPLE_QUEUE_TAG);
+
+ if (tag_type == SIMPLE_QUEUE_TAG) {
+ 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 == ORDERED_QUEUE_TAG
+ ? "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*/SIMPLE_QUEUE_TAG);
+ 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,
+ TARGET_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;
+
+ ahc = kzalloc(sizeof(*ahc), GFP_ATOMIC);
+ if (!ahc) {
+ printk("aic7xxx: cannot malloc softc!\n");
+ kfree(name);
+ return NULL;
+ }
+
+ ahc->seep_config = kmalloc(sizeof(*ahc->seep_config), GFP_ATOMIC);
+ if (ahc->seep_config == NULL) {
+ kfree(ahc);
+ 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)
+{
+ 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);
+ fallthrough;
+ case 1:
+ break;
+ case 0:
+ break;
+ }
+
+ 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
+ kfree(ahc->name);
+ kfree(ahc->seep_config);
+ kfree(ahc);
+ 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 = kcalloc(AHC_SCB_MAX_ALLOC, sizeof(struct scb),
+ 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);
+ }
+ fallthrough;
+ case 6:
+ ahc_dmamap_unload(ahc, scb_data->sense_dmat,
+ scb_data->sense_dmamap);
+ fallthrough;
+ 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);
+ fallthrough;
+ case 4:
+ ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
+ fallthrough;
+ case 3:
+ ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
+ scb_data->hscb_dmamap);
+ fallthrough;
+ 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);
+ fallthrough;
+ case 1:
+ ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
+ break;
+ case 0:
+ break;
+ }
+ 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;
+
+ 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;
+ 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, NOP);
+
+ /*
+ * 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;
+
+ 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;
+}
+
+int __maybe_unused
+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 __maybe_unused
+ahc_resume(struct ahc_softc *ahc)
+{
+
+ ahc_reset(ahc, /*reinit*/TRUE);
+ ahc_intr_enable(ahc, TRUE);
+ ahc_restart(ahc);
+ return (0);
+}
+/************************** 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 == TARGET_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 = kmemdup(cs_table, cs_count, GFP_ATOMIC);
+ if (ahc->critical_sections == NULL)
+ panic("ahc_loadseq: Could not malloc");
+ }
+ 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");
+ }
+
+ 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 000000000..0b57b783e
--- /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 000000000..d3b108265
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -0,0 +1,2577 @@
+
+/*
+ * 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)
+{
+ 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->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;
+ 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);
+ }
+ 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)
+{
+ 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[])
+{
+ int heads;
+ int sectors;
+ int cylinders;
+ int extended;
+ struct ahc_softc *ahc;
+ u_int channel;
+
+ ahc = *((struct ahc_softc **)sdev->host->hostdata);
+ channel = sdev_channel(sdev);
+
+ if (scsi_partsize(bdev, capacity, geom))
+ return 0;
+
+ 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 != SUCCESS)
+ 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 != SUCCESS)
+ 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,
+ .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)
+{
+ /* XXX: check if we really need the GFP_ATOMIC and unwind this mess! */
+ *vaddr = dma_alloc_coherent(ahc->dev, dmat->maxsize, mapp, GFP_ATOMIC);
+ 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)
+{
+ dma_free_coherent(ahc->dev, 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);
+ 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.
+ */
+static 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 |= ORDERED_QUEUE_TAG;
+ dev->commands_since_idle_or_otag = 0;
+ } else {
+ hscb->control |= SIMPLE_QUEUE_TAG;
+ }
+ }
+
+ 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_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;
+
+ 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) {
+#ifdef AHC_REPORT_UNDERFLOWS
+ uint32_t amount_xferred;
+
+ amount_xferred =
+ ahc_get_transfer_length(scb) - ahc_get_residual(scb);
+#endif
+ 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) != SAM_STAT_TASK_SET_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 SAM_STAT_CHECK_CONDITION:
+ case SAM_STAT_COMMAND_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);
+#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 SAM_STAT_TASK_SET_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, SAM_STAT_GOOD);
+ 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, SAM_STAT_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);
+ }
+
+ 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);
+}
+
+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 000000000..51d9f4de0
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h
@@ -0,0 +1,674 @@
+/*
+ * 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
+
+#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 dma_alloc_coherent() 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
+
+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 *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 *);
+
+#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 000000000..a07e94fac
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
@@ -0,0 +1,447 @@
+/*
+ * 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);
+
+static int __maybe_unused
+ahc_linux_pci_dev_suspend(struct device *dev)
+{
+ struct ahc_softc *ahc = dev_get_drvdata(dev);
+
+ return ahc_suspend(ahc);
+}
+
+static int __maybe_unused
+ahc_linux_pci_dev_resume(struct device *dev)
+{
+ struct ahc_softc *ahc = dev_get_drvdata(dev);
+
+ ahc_pci_resume(ahc);
+
+ return (ahc_resume(ahc));
+}
+
+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;
+ ahc->dev = &pci->dev;
+ 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 SIMPLE_DEV_PM_OPS(ahc_linux_pci_dev_pm_ops,
+ ahc_linux_pci_dev_suspend,
+ ahc_linux_pci_dev_resume);
+
+static struct pci_driver aic7xxx_pci_driver = {
+ .name = "aic7xxx",
+ .probe = ahc_linux_pci_dev_probe,
+ .driver.pm = &ahc_linux_pci_dev_pm_ops,
+ .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(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 000000000..2d4c85426
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c
@@ -0,0 +1,2460 @@
+/*
+ * 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 $
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#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 device, uint16_t vendor,
+ uint16_t subdevice, uint16_t subvendor);
+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, PCI_SUBSYSTEM_VENDOR_ID, /*bytes*/2);
+ subdevice = ahc_pci_read_config(pci, PCI_SUBSYSTEM_ID, /*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));
+}
+
+void __maybe_unused
+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);
+ }
+}
+
+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 000000000..263f85da4
--- /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 000000000..4bc9e2dfc
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c
@@ -0,0 +1,343 @@
+/*
+ * 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 000000000..473039df0
--- /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 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 STCNT 0x08
+
+#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 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 TARGID 0x1b
+
+#define BRDCTL 0x1d
+#define BRDDAT7 0x80
+#define BRDDAT6 0x40
+#define BRDDAT5 0x20
+#define BRDSTB 0x10
+#define BRDDAT4 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 SELBUSB 0x08
+#define ENAB40 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_CDB_STORE 0xa0
+#define SCB_RESIDUAL_DATACNT 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 TARGET_SCB 0x80
+#define STATUS_RCVD 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 TARGET_CMD_CMPLT 0xfe
+#define MAX_OFFSET_ULTRA2 0x7f
+#define MAX_OFFSET_16BIT 0x08
+#define BUS_8_BIT 0x00
+#define TID_SHIFT 0x04
+#define STATUS_QUEUE_FULL 0x28
+#define STATUS_BUSY 0x08
+#define SCB_DOWNLOAD_SIZE_64 0x30
+#define MAX_OFFSET_8BIT 0x0f
+#define HOST_MAILBOX_SHIFT 0x04
+#define CCSGADDR_MAX 0x80
+#define BUS_32_BIT 0x02
+#define SG_SIZEOF 0x08
+#define SEQ_MAILBOX_SHIFT 0x00
+#define SCB_LIST_NULL 0xff
+#define SCB_DOWNLOAD_SIZE 0x20
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define CCSGRAM_MAXSEGS 0x10
+#define TARGET_DATA_IN 0x01
+#define STACK_SIZE 0x04
+#define SCB_UPLOAD_SIZE 0x20
+#define MAX_OFFSET 0x7f
+#define HOST_MSG 0xff
+#define BUS_16_BIT 0x01
+
+
+/* Downloaded Constant Definitions */
+#define INVERTED_CACHESIZE_MASK 0x03
+#define SG_PREFETCH_ALIGN_MASK 0x05
+#define SG_PREFETCH_ADDR_MASK 0x06
+#define QOUTFIFO_OFFSET 0x00
+#define SG_PREFETCH_CNT 0x04
+#define QINFIFO_OFFSET 0x01
+#define CACHESIZE_MASK 0x02
+#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 000000000..9f9b88047
--- /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 000000000..f37362bc8
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped
@@ -0,0 +1,1307 @@
+/*
+ * 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 }
+};
+
+#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(critical_sections)
diff --git a/drivers/scsi/aic7xxx/aicasm/Makefile b/drivers/scsi/aic7xxx/aicasm/Makefile
new file mode 100644
index 000000000..243adb0a3
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/Makefile
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0
+PROG= aicasm
+
+OUTDIR ?= ./
+
+.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= $(addprefix ${OUTDIR}/,aicdb.h $(YSRCS:.y=.h))
+GENSRCS= $(addprefix ${OUTDIR}/,$(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. -I$(OUTDIR)
+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): $(OUTDIR) ${GENHDRS} $(SRCS)
+ $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(OUTDIR)/$(PROG) $(LIBS)
+
+$(OUTDIR):
+ mkdir -p $(OUTDIR)
+
+$(OUTDIR)/aicdb.h:
+ @if [ -e "/usr/include/db4/db_185.h" ]; then \
+ echo "#include <db4/db_185.h>" > $@; \
+ elif [ -e "/usr/include/db3/db_185.h" ]; then \
+ echo "#include <db3/db_185.h>" > $@; \
+ elif [ -e "/usr/include/db2/db_185.h" ]; then \
+ echo "#include <db2/db_185.h>" > $@; \
+ elif [ -e "/usr/include/db1/db_185.h" ]; then \
+ echo "#include <db1/db_185.h>" > $@; \
+ elif [ -e "/usr/include/db/db_185.h" ]; then \
+ echo "#include <db/db_185.h>" > $@; \
+ elif [ -e "/usr/include/db_185.h" ]; then \
+ echo "#include <db_185.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.
+$(OUTDIR)/aicasm_gram.c: $(OUTDIR)/aicasm_gram.h
+$(OUTDIR)/aicasm_gram.c $(OUTDIR)/aicasm_gram.h: aicasm_gram.y
+ $(YACC) $(YFLAGS) -b $(<:.y=) $<
+ mv $(<:.y=).tab.c $(OUTDIR)/$(<:.y=.c)
+ mv $(<:.y=).tab.h $(OUTDIR)/$(<:.y=.h)
+
+# Create a dependency chain in generated files
+# to avoid concurrent invocations of the single
+# rule that builds them all.
+$(OUTDIR)/aicasm_macro_gram.c: $(OUTDIR)/aicasm_macro_gram.h
+$(OUTDIR)/aicasm_macro_gram.c $(OUTDIR)/aicasm_macro_gram.h: aicasm_macro_gram.y
+ $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $<
+ mv $(<:.y=).tab.c $(OUTDIR)/$(<:.y=.c)
+ mv $(<:.y=).tab.h $(OUTDIR)/$(<:.y=.h)
+
+$(OUTDIR)/aicasm_scan.c: aicasm_scan.l
+ $(LEX) $(LFLAGS) -o $@ $<
+
+$(OUTDIR)/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 000000000..cd692a4c5
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.c
@@ -0,0 +1,843 @@
+/*
+ * 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 specified\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
+ * traversal, 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,
+ "#define NUM_CRITICAL_SECTIONS ARRAY_SIZE(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 000000000..716a2aefc
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm.h
@@ -0,0 +1,91 @@
+/*
+ * 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$
+ */
+
+#include "../queue.h"
+
+#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 000000000..65182ad9c
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
@@ -0,0 +1,1999 @@
+%{
+/*
+ * 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>
+
+#include "../queue.h"
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+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 000000000..8373447bd
--- /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 000000000..8c0479865
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
@@ -0,0 +1,161 @@
+%{
+/*
+ * 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>
+
+#include "../queue.h"
+
+#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 000000000..98e9959c6
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
@@ -0,0 +1,153 @@
+%{
+/*
+ * 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>
+#include "../queue.h"
+
+#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 000000000..c78d4f68e
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
@@ -0,0 +1,618 @@
+%{
+/*
+ * 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>
+#include "../queue.h"
+
+#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 000000000..975fcfcc0
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
@@ -0,0 +1,689 @@
+/*
+ * 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>
+
+#include "aicdb.h"
+#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 000000000..ed3bdd43c
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
@@ -0,0 +1,205 @@
+/*
+ * 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$
+ */
+
+#include "../queue.h"
+
+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);
+
+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.h b/drivers/scsi/aic7xxx/aiclib.h
new file mode 100644
index 000000000..ba08eb3c4
--- /dev/null
+++ b/drivers/scsi/aic7xxx/aiclib.h
@@ -0,0 +1,180 @@
+/*
+ * 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)
+};
+
+/************************* 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 000000000..687aef6ef
--- /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 000000000..ba602981f
--- /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 BSD_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 000000000..0eafd3c17
--- /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 000000000..53343a6d8
--- /dev/null
+++ b/drivers/scsi/aic7xxx/scsi_message.h
@@ -0,0 +1,40 @@
+/*
+ * 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_SAVEDATAPOINTER 0x02 /* O/O */
+#define MSG_RESTOREPOINTERS 0x03 /* O/O */
+#define MSG_DISCONNECT 0x04 /* O/O */
+#define MSG_MESSAGE_REJECT 0x07 /* M/M */
+#define MSG_NOOP 0x08 /* M/M */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
+#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
+
+/* 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_LEN 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_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