aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin T. Gibbs <gibbs@FreeBSD.org>1996-10-07 02:07:07 +0000
committerJustin T. Gibbs <gibbs@FreeBSD.org>1996-10-07 02:07:07 +0000
commit0a42ab83792022a1217f7269bbda94d153e2b399 (patch)
tree7361912c7b272d7c8f03eee6a1f6560309fd76b0
parentb109ceda2fbf1a7a1a8b10d0d6af8307970c7b9d (diff)
downloadsrc-0a42ab83792022a1217f7269bbda94d153e2b399.tar.gz
src-0a42ab83792022a1217f7269bbda94d153e2b399.zip
Advanced Systems Inc. SCSI Controller driver and ISA/VL front end.
I have only tested the ABP5140 card and only with a single CDROM drive but it seems to work fine. This driver relies on features found only in the SCSI branch so will not work in -current until those changes are brought in. It also doesn't have any error handling code *yet*. The goal is to use this driver as the development platform for the new generic SCSI layer error recovery/handling code. PCI and EISA front ends will show up as soon as I get my hands on the cards. There are also a few issues in the driver that I need to clear up with AdvanSys before I can suggest sticking one of these cards in your server. 8-) Thanks to AdvanSys for releasing this code under a suitable copyright. Obtained from: Ported from the Linux driver writen by bobf@advansys.com (Bob Frey).
Notes
Notes: svn path=/cvs2svn/branches/GIBBS/; revision=18781
-rw-r--r--sys/dev/advansys/adv_isa.c236
-rw-r--r--sys/dev/advansys/advlib.c1654
-rw-r--r--sys/dev/advansys/advlib.h741
-rw-r--r--sys/dev/advansys/advmcode.c166
-rw-r--r--sys/dev/advansys/advmcode.h19
-rw-r--r--sys/i386/isa/adv_isa.c236
-rw-r--r--sys/i386/scsi/advansys.c798
-rw-r--r--sys/i386/scsi/advansys.h50
8 files changed, 3900 insertions, 0 deletions
diff --git a/sys/dev/advansys/adv_isa.c b/sys/dev/advansys/adv_isa.c
new file mode 100644
index 000000000000..e7c89151f762
--- /dev/null
+++ b/sys/dev/advansys/adv_isa.c
@@ -0,0 +1,236 @@
+/*
+ * Device probe and attach routines for the following
+ * Advanced Systems Inc. SCSI controllers:
+ *
+ * Connectivity Products:
+ * ABP5140 - Bus-Master PnP ISA 16 CDB
+ *
+ * Single Channel Products:
+ * ABP542 - Bus-Master ISA 240 CDB
+ * ABP5150 - Bus-Master ISA 240 CDB (shipped by HP with the 4020i CD-R drive)
+ * ABP842 - Bus-Master VL 240 CDB
+ *
+ * Dual Channel Products:
+ * ABP852 - Dual Channel Bus-Master VL 240 CDB Per Channel
+ *
+ * Copyright (c) 1996 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 immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+
+#include <i386/scsi/advansys.h>
+
+#define ADV_ISA_MAX_DMA_ADDR (0x00FFFFFFL)
+#define ADV_ISA_MAX_DMA_COUNT (0x00FFFFFFL)
+
+#define ADV_VL_MAX_DMA_ADDR (0x07FFFFFFL)
+#define ADV_VL_MAX_DMA_COUNT (0x07FFFFFFL)
+
+/* Possible port addresses an ISA or VL adapter can live at */
+u_int16_t adv_isa_ioports[] =
+{
+ 0x100,
+ 0x110, /* First selection in BIOS setup */
+ 0x120,
+ 0x130, /* Second selection in BIOS setup */
+ 0x140,
+ 0x150, /* Third selection in BIOS setup */
+ 0x190, /* Fourth selection in BIOS setup */
+ 0x210, /* Fifth selection in BIOS setup */
+ 0x230, /* Sixth selection in BIOS setup */
+ 0x250, /* Seventh selection in BIOS setup */
+ 0x330 /* Eighth and default selection in BIOS setup */
+};
+
+#define MAX_ISA_IOPORT_INDEX (sizeof(adv_isa_ioports)/sizeof(u_short) - 1)
+
+static int advisaprobe __P((struct isa_device *id));
+static int advisaattach __P((struct isa_device *id));
+static void adv_set_isapnp_wait_for_key __P((void));
+static int adv_find_signature __P((u_int16_t iobase));
+
+void adv_isa_intr __P((int unit));
+
+struct isa_driver advdriver =
+{
+ advisaprobe,
+ advisaattach,
+ "adv"
+};
+
+static int
+advisaprobe(id)
+ struct isa_device *id;
+{
+ int port_index;
+ int max_port_index;
+
+ /*
+ * Default to scanning all possible device locations.
+ */
+ port_index = 0;
+ max_port_index = MAX_ISA_IOPORT_INDEX;
+
+ if (id->id_iobase > 0) {
+ for (;port_index <= max_port_index; port_index++)
+ if (id->id_iobase >= adv_isa_ioports[port_index])
+ break;
+ if ((port_index > max_port_index)
+ || (id->id_iobase != adv_isa_ioports[port_index])) {
+ printf("adv%d: Invalid baseport of 0x%x specified. "
+ "Neerest valid baseport is 0x%x. Failing "
+ "probe.\n", id->id_unit, id->id_iobase,
+ (port_index <= max_port_index) ?
+ adv_isa_ioports[port_index] :
+ adv_isa_ioports[max_port_index]);
+ return 0;
+ }
+ max_port_index = port_index;
+ }
+
+ /* Perform the actual probing */
+ adv_set_isapnp_wait_for_key();
+ for (;port_index <= max_port_index; port_index++) {
+ u_int16_t port_addr = adv_isa_ioports[port_index];
+ if (port_addr == 0)
+ /* Already been attached */
+ continue;
+ if (adv_find_signature(port_addr)) {
+ /*
+ * Got one. Now allocate our softc
+ * and see if we can initialize the card.
+ */
+ struct adv_softc *adv;
+ adv = adv_alloc(id->id_unit, port_addr);
+ if (adv == NULL)
+ return (0);
+
+ id->id_iobase = adv->iobase;
+ /*
+ * Determine the chip version.
+ */
+ adv->chip_version = ADV_INB(adv,
+ ADV_NONEISA_CHIP_REVISION);
+
+ if (adv_init(adv) != 0) {
+ adv_free(adv);
+ return (0);
+ }
+ switch (adv->type) {
+ case ADV_ISAPNP:
+ if (adv->chip_version == ADV_CHIP_VER_ASYN_BUG)
+ adv->needs_async_bug_fix = TARGET_BIT_VECTOR_SET;
+ /* Fall Through */
+ case ADV_ISA:
+ adv->max_dma_count = ADV_ISA_MAX_DMA_COUNT;
+ break;
+
+ case ADV_VL:
+ adv->max_dma_count = ADV_VL_MAX_DMA_COUNT;
+ break;
+ }
+
+ if ((adv->type & ADV_ISAPNP) == ADV_ISAPNP) {
+ }
+
+ /* Determine our IRQ */
+ if (id->id_irq == 0 /* irq ? */)
+ id->id_irq = 1 << adv_get_chip_irq(adv);
+ else
+ adv_set_chip_irq(adv, ffs(id->id_irq) - 1);
+
+ /* Mark as probed */
+ adv_isa_ioports[port_index] = 0;
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static int
+advisaattach(id)
+ struct isa_device *id;
+{
+ struct adv_softc *adv;
+
+ adv = advsoftcs[id->id_unit];
+ return (adv_attach(adv));
+}
+
+static void
+adv_set_isapnp_wait_for_key(void)
+{
+ static int isapnp_wait_set = 0;
+ if (isapnp_wait_set == 0) {
+ outb(ADV_ISA_PNP_PORT_ADDR, 0x02);
+ outb(ADV_ISA_PNP_PORT_WRITE, 0x02);
+ isapnp_wait_set++;
+ }
+ return;
+}
+
+/*
+ * Determine if there is a board at "iobase" by looking
+ * for the AdvanSys signatures. Return 1 if a board is
+ * found, 0 otherwise.
+ */
+static int
+adv_find_signature(iobase)
+ u_int16_t iobase;
+{
+ u_int16_t signature;
+
+ if (inb(iobase + ADV_SIGNATURE_BYTE) == ADV_1000_ID1B) {
+ signature = inw(iobase + ADV_SIGNATURE_WORD );
+ if ((signature == ADV_1000_ID0W)
+ || (signature == ADV_1000_ID0W_FIX))
+ return (1);
+ }
+ return (0);
+}
+
+
+/*
+ * Handle an ISA interrupt.
+ * XXX should go away as soon as ISA interrupt handlers
+ * take a (void *) arg.
+ */
+void
+adv_isa_intr(unit)
+ int unit;
+{
+ struct adv_softc *arg = advsoftcs[unit];
+ adv_intr((void *)arg);
+}
diff --git a/sys/dev/advansys/advlib.c b/sys/dev/advansys/advlib.c
new file mode 100644
index 000000000000..365f3432c1ba
--- /dev/null
+++ b/sys/dev/advansys/advlib.c
@@ -0,0 +1,1654 @@
+/*
+ * Low level routines for the Advanced Systems Inc. SCSI controllers chips
+ *
+ * Copyright (c) 1996 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 immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+/*
+ * Ported from:
+ * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-1996 Advanced System Products, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <machine/clock.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsi_disk.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <dev/advansys/advlib.h>
+#include <dev/advansys/advmcode.h>
+
+/*
+ * Allowable periods in ns
+ */
+u_int8_t adv_sdtr_period_tbl[] =
+{
+ 25,
+ 30,
+ 35,
+ 40,
+ 50,
+ 60,
+ 70,
+ 85
+};
+
+struct sdtr_xmsg {
+ u_int8_t msg_type;
+ u_int8_t msg_len;
+ u_int8_t msg_req;
+ u_int8_t xfer_period;
+ u_int8_t req_ack_offset;
+ u_int8_t res;
+};
+
+/*
+ * Some of the early PCI adapters have problems with
+ * async transfers. Instead try to use an offset of
+ * 1.
+ */
+#define ASYN_SDTR_DATA_FIX 0x41
+
+/* LRAM routines */
+static void adv_read_lram_16_multi __P((struct adv_softc *adv, u_int16_t s_addr,
+ u_int16_t *buffer, int count));
+static void adv_write_lram_16_multi __P((struct adv_softc *adv,
+ u_int16_t s_addr, u_int16_t *buffer,
+ int count));
+static void adv_mset_lram_16 __P((struct adv_softc *adv,
+ u_int16_t s_addr, u_int16_t set_value,
+ int count));
+static u_int32_t adv_msum_lram_16 __P((struct adv_softc *adv, u_int16_t s_addr, int count));
+
+static int adv_write_and_verify_lram_16 __P((struct adv_softc *adv,
+ u_int16_t addr, u_int16_t value));
+static u_int32_t adv_read_lram_32 __P((struct adv_softc *adv, u_int16_t addr));
+
+
+static void adv_write_lram_32 __P((struct adv_softc *adv, u_int16_t addr,
+ u_int32_t value));
+static void adv_write_lram_32_multi __P((struct adv_softc *adv, u_int16_t s_addr,
+ u_int32_t *buffer, int count));
+
+/* EEPROM routines */
+static u_int16_t adv_read_eeprom_16 __P((struct adv_softc *adv, u_int8_t addr));
+static u_int16_t adv_write_eeprom_16 __P((struct adv_softc *adv, u_int8_t addr, u_int16_t value));
+static int adv_write_eeprom_cmd_reg __P((struct adv_softc *adv, u_int8_t cmd_reg));
+static int adv_set_eeprom_config_once __P((struct adv_softc *adv,
+ struct adv_eeprom_config *eeprom_config));
+
+/* Initialization */
+static u_int32_t adv_load_microcode __P((struct adv_softc *adv, u_int16_t s_addr,
+ u_int16_t *mcode_buf, u_int16_t mcode_size));
+static void adv_init_lram __P((struct adv_softc *adv));
+static int adv_init_microcode_var __P((struct adv_softc *adv));
+static void adv_init_qlink_var __P((struct adv_softc *adv));
+
+/* Interrupts */
+static void adv_disable_interrupt __P((struct adv_softc *adv));
+static void adv_enable_interrupt __P((struct adv_softc *adv));
+static void adv_toggle_irq_act __P((struct adv_softc *adv));
+
+/* Chip Control */
+#if UNUSED
+static void adv_start_execution __P((struct adv_softc *adv));
+#endif
+static int adv_start_chip __P((struct adv_softc *adv));
+static int adv_stop_chip __P((struct adv_softc *adv));
+static void adv_set_chip_ih __P((struct adv_softc *adv, u_int16_t ins_code));
+static void adv_set_bank __P((struct adv_softc *adv, u_int8_t bank));
+#if UNUSED
+static u_int8_t adv_get_chip_scsi_ctrl __P((struct adv_softc *adv));
+#endif
+
+/* Queue handling and execution */
+static int adv_sgcount_to_qcount __P((int sgcount));
+static void adv_get_q_info __P((struct adv_softc *adv, u_int16_t s_addr, u_int16_t *inbuf,
+ int words));
+static u_int adv_get_num_free_queues __P((struct adv_softc *adv, u_int8_t n_qs));
+static u_int8_t adv_alloc_free_queues __P((struct adv_softc *adv, u_int8_t free_q_head,
+ u_int8_t n_free_q));
+static u_int8_t adv_alloc_free_queue __P((struct adv_softc *adv, u_int8_t free_q_head));
+static int adv_send_scsi_queue __P((struct adv_softc *adv, struct adv_scsi_q *scsiq,
+ u_int8_t n_q_required));
+static void adv_put_ready_sg_list_queue __P((struct adv_softc *adv, struct adv_scsi_q *scsiq,
+ u_int8_t q_no));
+static void adv_put_ready_queue __P((struct adv_softc *adv, struct adv_scsi_q *scsiq, u_int8_t q_no));
+static void adv_put_scsiq __P((struct adv_softc *adv, u_int16_t s_addr, u_int16_t *buffer, int words));
+
+/* SDTR */
+static u_int8_t adv_msgout_sdtr __P((struct adv_softc *adv, u_int8_t sdtr_period, u_int8_t sdtr_offset));
+static u_int8_t adv_get_card_sync_setting __P((u_int8_t period, u_int8_t offset));
+static void adv_set_chip_sdtr __P((struct adv_softc *adv, u_int8_t sdtr_data,
+ u_int8_t tid_no));
+
+
+/* Exported Function first */
+
+u_int8_t
+adv_read_lram_8(adv, addr)
+ struct adv_softc *adv;
+ u_int16_t addr;
+
+{
+ u_int8_t byte_data;
+ u_int16_t word_data;
+
+ /*
+ * LRAM is accessed on 16bit boundaries.
+ */
+ ADV_OUTW(adv, ADV_LRAM_ADDR, addr & 0xFFFE);
+ word_data = ADV_INW(adv, ADV_LRAM_DATA);
+ if (addr & 1) {
+#if BYTE_ORDER == BIG_ENDIAN
+ byte_data = (u_int8_t)(word_data & 0xFF);
+#else
+ byte_data = (u_int8_t)((word_data >> 8) & 0xFF);
+#endif
+ } else {
+#if BYTE_ORDER == BIG_ENDIAN
+ byte_data = (u_int8_t)((word_data >> 8) & 0xFF);
+#else
+ byte_data = (u_int8_t)(word_data & 0xFF);
+#endif
+ }
+ return (byte_data);
+}
+
+void
+adv_write_lram_8(adv, addr, value)
+ struct adv_softc *adv;
+ u_int16_t addr;
+ u_int8_t value;
+{
+ u_int16_t word_data;
+
+ word_data = adv_read_lram_16(adv, addr & 0xFFFE);
+ if (addr & 1) {
+ word_data &= 0x00FF;
+ word_data |= (((u_int8_t)value << 8) & 0xFF00);
+ } else {
+ word_data &= 0xFF00;
+ word_data |= ((u_int8_t)value & 0x00FF);
+ }
+ adv_write_lram_16(adv, addr & 0xFFFE, word_data);
+}
+
+
+u_int16_t
+adv_read_lram_16(adv, addr)
+ struct adv_softc *adv;
+ u_int16_t addr;
+{
+ ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
+ return (ADV_INW(adv, ADV_LRAM_DATA));
+}
+
+void
+adv_write_lram_16(adv, addr, value)
+ struct adv_softc *adv;
+ u_int16_t addr;
+ u_int16_t value;
+{
+ ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
+ ADV_OUTW(adv, ADV_LRAM_DATA, value);
+}
+
+
+/*
+ * Return the fully qualified board type for the adapter.
+ * The chip_revision must be set before this function is called.
+ */
+void
+adv_get_board_type(adv)
+ struct adv_softc *adv;
+{
+ if ((adv->chip_version >= ADV_CHIP_MIN_VER_VL) &&
+ (adv->chip_version <= ADV_CHIP_MAX_VER_VL)) {
+ if (((adv->iobase & 0x0C30) == 0x0C30) ||
+ ((adv->iobase & 0x0C50) == 0x0C50)) {
+ adv->type = ADV_EISA;
+ } else
+ adv->type = ADV_VL;
+ } else if ((adv->chip_version >= ADV_CHIP_MIN_VER_ISA) &&
+ (adv->chip_version <= ADV_CHIP_MAX_VER_ISA)) {
+ if (adv->chip_version >= ADV_CHIP_MIN_VER_ISA_PNP) {
+ adv->type = ADV_ISAPNP;
+ } else
+ adv->type = ADV_ISA;
+ } else if ((adv->chip_version >= ADV_CHIP_MIN_VER_PCI) &&
+ (adv->chip_version <= ADV_CHIP_MAX_VER_PCI)) {
+ adv->type = ADV_PCI;
+ } else
+ panic("adv_get_board_type: Unknown board type encountered");
+}
+
+u_int16_t
+adv_get_eeprom_config(adv, eeprom_config)
+ struct adv_softc *adv;
+ struct adv_eeprom_config *eeprom_config;
+{
+ u_int16_t sum;
+ u_int16_t *wbuf;
+ u_int8_t cfg_beg;
+ u_int8_t cfg_end;
+ u_int8_t s_addr;
+
+ wbuf = (u_int16_t *)eeprom_config;
+ sum = 0;
+
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ *wbuf = adv_read_eeprom_16(adv, s_addr);
+ sum += *wbuf;
+ }
+
+ if (adv->type & ADV_VL) {
+ cfg_beg = ADV_EEPROM_CFG_BEG_VL;
+ cfg_end = ADV_EEPROM_MAX_ADDR_VL;
+ } else {
+ cfg_beg = ADV_EEPROM_CFG_BEG;
+ cfg_end = ADV_EEPROM_MAX_ADDR;
+ }
+
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1); s_addr++, wbuf++) {
+ *wbuf = adv_read_eeprom_16(adv, s_addr);
+ sum += *wbuf;
+#if ADV_DEBUG_EEPROM
+ printf("Addr 0x%x: 0x%04x\n", s_addr, *wbuf);
+#endif
+ }
+ *wbuf = adv_read_eeprom_16(adv, s_addr);
+ return (sum);
+}
+
+int
+adv_set_eeprom_config(adv, eeprom_config)
+ struct adv_softc *adv;
+ struct adv_eeprom_config *eeprom_config;
+{
+ int retry;
+
+ retry = 0;
+ while (1) {
+ if (adv_set_eeprom_config_once(adv, eeprom_config) == 0) {
+ break;
+ }
+ if (++retry > ADV_EEPROM_MAX_RETRY) {
+ break;
+ }
+ }
+ return (retry > ADV_EEPROM_MAX_RETRY);
+}
+
+int
+adv_reset_chip_and_scsi_bus(adv)
+ struct adv_softc *adv;
+{
+ adv_stop_chip(adv);
+ ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_CHIP_RESET | ADV_CC_SCSI_RESET | ADV_CC_HALT);
+ DELAY(200 * 1000);
+
+ adv_set_chip_ih(adv, ADV_INS_RFLAG_WTM);
+ adv_set_chip_ih(adv, ADV_INS_HALT);
+
+ ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_CHIP_RESET | ADV_CC_HALT);
+ ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_HALT);
+ DELAY(200 * 1000);
+ return (adv_is_chip_halted(adv));
+}
+
+int
+adv_test_external_lram(adv)
+ struct adv_softc* adv;
+{
+ u_int16_t q_addr;
+ u_int16_t saved_value;
+ int success;
+
+ success = 0;
+
+ /* XXX Why 241? */
+ q_addr = ADV_QNO_TO_QADDR(241);
+ saved_value = adv_read_lram_16(adv, q_addr);
+ if (adv_write_and_verify_lram_16(adv, q_addr, 0x55AA) == 0) {
+ success = 1;
+ adv_write_lram_16(adv, q_addr, saved_value);
+ }
+ return (success);
+}
+
+
+int
+adv_init_lram_and_mcode(adv)
+ struct adv_softc *adv;
+{
+ u_int32_t retval;
+ adv_disable_interrupt(adv);
+
+ adv_init_lram(adv);
+
+ retval = adv_load_microcode(adv, 0, (u_int16_t *)adv_mcode, adv_mcode_size);
+ if (retval != adv_mcode_chksum) {
+ printf("adv%d: Microcode download failed checksum!\n",
+ adv->unit);
+ return (1);
+ }
+
+ if (adv_init_microcode_var(adv) != 0)
+ return (1);
+
+ adv_enable_interrupt(adv);
+ return (0);
+}
+
+u_int8_t
+adv_get_chip_irq(adv)
+ struct adv_softc *adv;
+{
+ u_int16_t cfg_lsw;
+ u_int8_t chip_irq;
+
+ cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW);
+
+ if ((adv->type & ADV_VL) != 0) {
+ chip_irq = (u_int8_t)(((cfg_lsw >> 2) & 0x07));
+ if ((chip_irq == 0) ||
+ (chip_irq == 4) ||
+ (chip_irq == 7)) {
+ return (0);
+ }
+ return (chip_irq + (ADV_MIN_IRQ_NO - 1));
+ }
+ chip_irq = (u_int8_t)(((cfg_lsw >> 2) & 0x03));
+ if (chip_irq == 3)
+ chip_irq += 2;
+ return (chip_irq + ADV_MIN_IRQ_NO);
+}
+
+u_int8_t
+adv_set_chip_irq(adv, irq_no)
+ struct adv_softc *adv;
+ u_int8_t irq_no;
+{
+ u_int16_t cfg_lsw;
+
+ if ((adv->type & ADV_VL) != 0) {
+ if (irq_no != 0) {
+ if ((irq_no < ADV_MIN_IRQ_NO) || (irq_no > ADV_MAX_IRQ_NO)) {
+ irq_no = 0;
+ } else {
+ irq_no -= ADV_MIN_IRQ_NO - 1;
+ }
+ }
+ cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW) & 0xFFE3;
+ cfg_lsw |= 0x0010;
+ ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
+ adv_toggle_irq_act(adv);
+
+ cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW) & 0xFFE0;
+ cfg_lsw |= (irq_no & 0x07) << 2;
+ ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
+ adv_toggle_irq_act(adv);
+ } else if ((adv->type & ADV_ISA) != 0) {
+ if (irq_no == 15)
+ irq_no -= 2;
+ irq_no -= ADV_MIN_IRQ_NO;
+ cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW) & 0xFFF3;
+ cfg_lsw |= (irq_no & 0x03) << 2;
+ ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
+ }
+ return (adv_get_chip_irq(adv));
+}
+
+int
+adv_execute_scsi_queue(adv, scsiq)
+ struct adv_softc *adv;
+ struct adv_scsi_q *scsiq;
+{
+ int retval;
+ u_int n_q_required;
+ int s;
+ u_int32_t addr;
+ u_int8_t sg_entry_cnt;
+ u_int8_t target_ix;
+ u_int8_t sg_entry_cnt_minus_one;
+ u_int8_t tid_no;
+ u_int8_t sdtr_data;
+ u_int32_t *p_data_addr;
+ u_int32_t *p_data_bcount;
+
+ scsiq->q1.q_no = 0;
+ retval = 1; /* Default to error case */
+ target_ix = scsiq->q2.target_ix;
+ tid_no = ADV_TIX_TO_TID(target_ix);
+
+ n_q_required = 1;
+
+ s = splbio();
+ if (scsiq->cdbptr->opcode == REQUEST_SENSE) {
+ if (((adv->initiate_sdtr & scsiq->q1.target_id) != 0)
+ && ((adv->sdtr_done & scsiq->q1.target_id) != 0)) {
+ int sdtr_index;
+
+ sdtr_data = adv_read_lram_8(adv, ADVV_SDTR_DATA_BEG + tid_no);
+ sdtr_index = (sdtr_data >> 4);
+ adv_msgout_sdtr(adv, adv_sdtr_period_tbl[sdtr_index],
+ (sdtr_data & ADV_SYN_MAX_OFFSET));
+ scsiq->q1.cntl |= (QC_MSG_OUT | QC_URGENT);
+ }
+ }
+
+ if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
+ sg_entry_cnt = scsiq->sg_head->entry_cnt;
+ sg_entry_cnt_minus_one = sg_entry_cnt - 1;
+
+#ifdef DIAGNOSTIC
+ if (sg_entry_cnt <= 1)
+ panic("adv_execute_scsi_queue: Queue with QC_SG_HEAD set but %d segs.", sg_entry_cnt);
+
+ if (sg_entry_cnt > ADV_MAX_SG_LIST)
+ panic("adv_execute_scsi_queue: Queue with too many segs.");
+
+ if (adv->type & (ADV_ISA | ADV_VL | ADV_EISA)) {
+ for (i = 0; i < sg_entry_cnt_minus_one; i++) {
+ addr = scsiq->sg_head->sg_list[i].addr +
+ scsiq->sg_head->sg_list[i].bytes;
+
+ if ((addr & 0x0003) != 0)
+ panic("adv_execute_scsi_queue: SG with odd address or byte count");
+ }
+ }
+#endif
+ p_data_addr = &scsiq->sg_head->sg_list[sg_entry_cnt_minus_one].addr;
+ p_data_bcount = &scsiq->sg_head->sg_list[sg_entry_cnt_minus_one].bytes;
+
+ n_q_required = adv_sgcount_to_qcount(sg_entry_cnt);
+ scsiq->sg_head->queue_cnt = n_q_required - 1;
+ } else {
+ p_data_addr = &scsiq->q1.data_addr;
+ p_data_bcount = &scsiq->q1.data_cnt;
+ n_q_required = 1;
+ }
+
+ if (adv->bug_fix_control & ADV_BUG_FIX_ADD_ONE_BYTE) {
+ addr = *p_data_addr + *p_data_bcount;
+ if ((addr & 0x0003) != 0) {
+ /*
+ * XXX Is this extra test (the one on data_cnt) really only supposed to apply
+ * to the non SG case or was it a bug due to code duplication?
+ */
+ if ((scsiq->q1.cntl & QC_SG_HEAD) != 0 || (scsiq->q1.data_cnt & 0x01FF) == 0) {
+ if ((scsiq->cdbptr->opcode == READ_COMMAND) ||
+ (scsiq->cdbptr->opcode == READ_BIG)) {
+ if ((scsiq->q2.tag_code & ADV_TAG_FLAG_ADD_ONE_BYTE) == 0) {
+ (*p_data_bcount)++;
+ scsiq->q2.tag_code |= ADV_TAG_FLAG_ADD_ONE_BYTE;
+ }
+ }
+
+ }
+ }
+ }
+
+ if ((adv_get_num_free_queues(adv, n_q_required) >= n_q_required)
+ || ((scsiq->q1.cntl & QC_URGENT) != 0))
+ retval = adv_send_scsi_queue(adv, scsiq, n_q_required);
+
+ splx(s);
+ return (retval);
+}
+
+
+u_int8_t
+adv_copy_lram_doneq(adv, q_addr, scsiq, max_dma_count)
+ struct adv_softc *adv;
+ u_int16_t q_addr;
+ struct adv_q_done_info *scsiq;
+ u_int32_t max_dma_count;
+{
+ u_int16_t val;
+ u_int8_t sg_queue_cnt;
+
+ adv_get_q_info(adv, q_addr + ADV_SCSIQ_DONE_INFO_BEG,
+ (u_int16_t *)scsiq,
+ (sizeof(scsiq->d2) + sizeof(scsiq->d3)) / 2);
+
+#if BYTE_ORDER == BIG_ENDIAN
+ adv_adj_endian_qdone_info(scsiq);
+#endif
+
+ val = adv_read_lram_16(adv, q_addr + ADV_SCSIQ_B_STATUS);
+ scsiq->q_status = val & 0xFF;
+ scsiq->q_no = (val >> 8) & 0XFF;
+
+ val = adv_read_lram_16(adv, q_addr + ADV_SCSIQ_B_CNTL);
+ scsiq->cntl = val & 0xFF;
+ sg_queue_cnt = (val >> 8) & 0xFF;
+
+ val = adv_read_lram_16(adv,q_addr + ADV_SCSIQ_B_SENSE_LEN);
+ scsiq->sense_len = val & 0xFF;
+ scsiq->user_def = (val >> 8) & 0xFF;
+
+ scsiq->remain_bytes = adv_read_lram_32(adv,
+ q_addr + ADV_SCSIQ_DW_REMAIN_XFER_CNT);
+ /*
+ * XXX Is this just a safeguard or will the counter really
+ * have bogus upper bits?
+ */
+ scsiq->remain_bytes &= max_dma_count;
+
+ return (sg_queue_cnt);
+}
+
+int
+adv_stop_execution(adv)
+ struct adv_softc *adv;
+{
+ int count;
+
+ count = 0;
+ if (adv_read_lram_8(adv, ADV_STOP_CODE_B) == 0) {
+ adv_write_lram_8(adv, ADV_STOP_CODE_B,
+ ADV_STOP_REQ_RISC_STOP);
+ do {
+ if (adv_read_lram_8(adv, ADV_STOP_CODE_B) &
+ ADV_STOP_ACK_RISC_STOP) {
+ return (1);
+ }
+ DELAY(1000);
+ } while (count++ < 20);
+ }
+ return (0);
+}
+
+int
+adv_is_chip_halted(adv)
+ struct adv_softc *adv;
+{
+ if ((ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_HALTED) != 0) {
+ if ((ADV_INB(adv, ADV_CHIP_CTRL) & ADV_CC_HALT) != 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * XXX The numeric constants and the loops in this routine
+ * need to be documented.
+ */
+void
+adv_ack_interrupt(adv)
+ struct adv_softc *adv;
+{
+ u_int8_t host_flag;
+ u_int8_t risc_flag;
+ int loop;
+
+ loop = 0;
+ do {
+ risc_flag = adv_read_lram_8(adv, ADVV_RISC_FLAG_B);
+ if (loop++ > 0x7FFF) {
+ break;
+ }
+ } while ((risc_flag & ADV_RISC_FLAG_GEN_INT) != 0);
+
+ host_flag = adv_read_lram_8(adv, ADVV_HOST_FLAG_B);
+ adv_write_lram_8(adv, ADVV_HOST_FLAG_B,
+ host_flag | ADV_HOST_FLAG_ACK_INT);
+
+ ADV_OUTW(adv, ADV_CHIP_STATUS, ADV_CIW_INT_ACK);
+ loop = 0;
+ while (ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_INT_PENDING) {
+ ADV_OUTW(adv, ADV_CHIP_STATUS, ADV_CIW_INT_ACK);
+ if (loop++ > 3) {
+ break;
+ }
+ }
+
+ adv_write_lram_8(adv, ADVV_HOST_FLAG_B, host_flag);
+}
+
+/*
+ * Handle all conditions that may halt the chip waiting
+ * for us to intervene.
+ */
+void
+adv_isr_chip_halted(adv)
+ struct adv_softc *adv;
+{
+ u_int16_t int_halt_code;
+ u_int8_t halt_qp;
+ u_int16_t halt_q_addr;
+ u_int8_t target_ix;
+ u_int8_t q_cntl;
+ u_int8_t tid_no;
+ target_bit_vector target_id;
+ target_bit_vector scsi_busy;
+ u_int8_t asyn_sdtr;
+ u_int8_t sdtr_data;
+
+ int_halt_code = adv_read_lram_16(adv, ADVV_HALTCODE_W);
+ halt_qp = adv_read_lram_8(adv, ADVV_CURCDB_B);
+ halt_q_addr = ADV_QNO_TO_QADDR(halt_qp);
+ target_ix = adv_read_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_TARGET_IX);
+ q_cntl = adv_read_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL);
+ tid_no = ADV_TIX_TO_TID(target_ix);
+ target_id = ADV_TID_TO_TARGET_ID(tid_no);
+ if (adv->needs_async_bug_fix & target_id)
+ asyn_sdtr = ASYN_SDTR_DATA_FIX;
+ else
+ asyn_sdtr = 0;
+ if (int_halt_code == ADV_HALT_EXTMSG_IN) {
+ struct sdtr_xmsg sdtr_xmsg;
+ int sdtr_accept;
+
+ adv_read_lram_16_multi(adv, ADVV_MSGIN_BEG,
+ (u_int16_t *) &sdtr_xmsg,
+ sizeof(sdtr_xmsg) >> 1);
+ if ((sdtr_xmsg.msg_type == MSG_EXTENDED) &&
+ (sdtr_xmsg.msg_len == MSG_EXT_SDTR_LEN)) {
+ sdtr_accept = TRUE;
+ if (sdtr_xmsg.msg_req == MSG_EXT_SDTR) {
+ if (sdtr_xmsg.req_ack_offset > ADV_SYN_MAX_OFFSET) {
+
+ sdtr_accept = FALSE;
+ sdtr_xmsg.req_ack_offset = ADV_SYN_MAX_OFFSET;
+ }
+ sdtr_data = adv_get_card_sync_setting(sdtr_xmsg.xfer_period,
+ sdtr_xmsg.req_ack_offset);
+ if (sdtr_xmsg.req_ack_offset == 0) {
+ q_cntl &= ~QC_MSG_OUT;
+ adv->initiate_sdtr &= ~target_id;
+ adv->sdtr_done &= ~target_id;
+ adv_set_chip_sdtr(adv, asyn_sdtr, tid_no);
+ } else if (sdtr_data == 0) {
+ q_cntl |= QC_MSG_OUT;
+ adv->initiate_sdtr &= ~target_id;
+ adv->sdtr_done &= ~target_id;
+ adv_set_chip_sdtr(adv, asyn_sdtr, tid_no);
+ } else {
+ if (sdtr_accept && (q_cntl & QC_MSG_OUT)) {
+ q_cntl &= ~QC_MSG_OUT;
+ adv->sdtr_done |= target_id;
+ adv->initiate_sdtr |= target_id;
+ adv->needs_async_bug_fix &= ~target_id;
+ adv_set_chip_sdtr(adv, sdtr_data, tid_no);
+ } else {
+
+ q_cntl |= QC_MSG_OUT;
+
+ adv_msgout_sdtr(adv,
+ sdtr_xmsg.xfer_period,
+ sdtr_xmsg.req_ack_offset);
+ adv->needs_async_bug_fix &= ~target_id;
+ adv_set_chip_sdtr(adv, sdtr_data, tid_no);
+ adv->sdtr_done |= target_id;
+ adv->initiate_sdtr |= target_id;
+ }
+ }
+
+ adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL, q_cntl);
+ }
+ }
+ /*
+ * XXX Hey, shouldn't we be rejecting any messages we don't understand?
+ * The old code also did not un-halt the processor if it recieved
+ * an extended message that it didn't understand. That didn't
+ * seem right, so I changed this routine to always un-halt the
+ * processor at the end.
+ */
+ } else if (int_halt_code == ADV_HALT_CHK_CONDITION) {
+ u_int8_t tag_code;
+ u_int8_t q_status;
+
+ q_cntl |= QC_REQ_SENSE;
+ if (((adv->initiate_sdtr & target_id) != 0) &&
+ ((adv->sdtr_done & target_id) != 0)) {
+
+ sdtr_data = adv_read_lram_8(adv, ADVV_SDTR_DATA_BEG + tid_no);
+ /* XXX Macrotize the extraction of the index from sdtr_data ??? */
+ adv_msgout_sdtr(adv, adv_sdtr_period_tbl[(sdtr_data >> 4) & 0x0F],
+ sdtr_data & ADV_SYN_MAX_OFFSET);
+ q_cntl |= QC_MSG_OUT;
+ }
+ adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL, q_cntl);
+
+ /* Don't tag request sense commands */
+ tag_code = adv_read_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_TAG_CODE);
+ tag_code &= ~(MSG_SIMPLE_Q_TAG|MSG_HEAD_OF_Q_TAG|MSG_ORDERED_Q_TAG);
+ adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_TAG_CODE, tag_code);
+
+ q_status = adv_read_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_STATUS);
+ q_status |= (QS_READY | QS_BUSY);
+ adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_STATUS, q_status);
+
+ scsi_busy = adv_read_lram_8(adv, ADVV_SCSIBUSY_B);
+ scsi_busy &= ~target_id;
+ adv_write_lram_8(adv, ADVV_SCSIBUSY_B, scsi_busy);
+ } else if (int_halt_code == ADV_HALT_SDTR_REJECTED) {
+ struct sdtr_xmsg out_msg;
+
+ adv_read_lram_16_multi(adv, ADVV_MSGOUT_BEG,
+ (u_int16_t *) &out_msg,
+ sizeof(out_msg)/2);
+
+ if ((out_msg.msg_type == MSG_EXTENDED) &&
+ (out_msg.msg_len == MSG_EXT_SDTR_LEN) &&
+ (out_msg.msg_req == MSG_EXT_SDTR)) {
+
+ adv->initiate_sdtr &= ~target_id;
+ adv->sdtr_done &= ~target_id;
+ adv_set_chip_sdtr(adv, asyn_sdtr, tid_no);
+ }
+ q_cntl &= ~QC_MSG_OUT;
+ adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL, q_cntl);
+ } else if (int_halt_code == ADV_HALT_SS_QUEUE_FULL) {
+ u_int8_t cur_dvc_qng;
+ u_int8_t scsi_status;
+
+ /*
+ * XXX It would be nice if we could push the responsibility for handling
+ * this situation onto the generic SCSI layer as other drivers do.
+ * This would be done by completing the command with the status byte
+ * set to QUEUE_FULL, whereupon it will request that any transactions
+ * pending on the target that where scheduled after this one be aborted
+ * (so as to maintain queue ordering) and the number of requests the
+ * upper level will attempt to send this target will be reduced.
+ *
+ * With this current strategy, am I guaranteed that once I unbusy the
+ * target the queued up transactions will be sent in the order they
+ * were queued? If the ASC chip does a round-robin on all queued
+ * transactions looking for queues to run, the order is not guaranteed.
+ */
+ scsi_status = adv_read_lram_8(adv, halt_q_addr + ADV_SCSIQ_SCSI_STATUS);
+ cur_dvc_qng = adv_read_lram_8(adv, ADV_QADR_BEG + target_ix);
+ printf("adv%d: Queue full - target %d, active transactions %d\n", adv->unit,
+ tid_no, cur_dvc_qng);
+#if 0
+ /* XXX FIX LATER */
+ if ((cur_dvc_qng > 0) && (adv->cur_dvc_qng[tid_no] > 0)) {
+ scsi_busy = adv_read_lram_8(adv, ADVV_SCSIBUSY_B);
+ scsi_busy |= target_id;
+ adv_write_lram_8(adv, ADVV_SCSIBUSY_B, scsi_busy);
+ asc_dvc->queue_full_or_busy |= target_id;
+
+ if (scsi_status == SS_QUEUE_FULL) {
+ if (cur_dvc_qng > ASC_MIN_TAGGED_CMD) {
+ cur_dvc_qng -= 1;
+ asc_dvc->max_dvc_qng[tid_no] = cur_dvc_qng;
+
+ adv_write_lram_8(adv, ADVV_MAX_DVC_QNG_BEG + tid_no,
+ cur_dvc_qng);
+ }
+ }
+ }
+#endif
+ }
+ adv_write_lram_16(adv, ADVV_HALTCODE_W, 0);
+}
+
+/* Internal Routines */
+
+static void
+adv_read_lram_16_multi(adv, s_addr, buffer, count)
+ struct adv_softc *adv;
+ u_int16_t s_addr;
+ u_int16_t *buffer;
+ int count;
+{
+ ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
+ ADV_INSW(adv, ADV_LRAM_DATA, buffer, count);
+}
+
+static void
+adv_write_lram_16_multi(adv, s_addr, buffer, count)
+ struct adv_softc *adv;
+ u_int16_t s_addr;
+ u_int16_t *buffer;
+ int count;
+{
+ ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
+ ADV_OUTSW(adv, ADV_LRAM_DATA, buffer, count);
+}
+
+static void
+adv_mset_lram_16(adv, s_addr, set_value, count)
+ struct adv_softc *adv;
+ u_int16_t s_addr;
+ u_int16_t set_value;
+ int count;
+{
+ int i;
+
+ ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
+ for (i = 0; i < count; i++)
+ ADV_OUTW(adv, ADV_LRAM_DATA, set_value);
+}
+
+static u_int32_t
+adv_msum_lram_16(adv, s_addr, count)
+ struct adv_softc *adv;
+ u_int16_t s_addr;
+ int count;
+{
+ u_int32_t sum;
+ int i;
+
+ sum = 0;
+ for (i = 0; i < count; i++, s_addr += 2)
+ sum += adv_read_lram_16(adv, s_addr);
+ return (sum);
+}
+
+static int
+adv_write_and_verify_lram_16(adv, addr, value)
+ struct adv_softc *adv;
+ u_int16_t addr;
+ u_int16_t value;
+{
+ int retval;
+
+ retval = 0;
+ ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
+ ADV_OUTW(adv, ADV_LRAM_DATA, value);
+ ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
+ if (value != ADV_INW(adv, ADV_LRAM_DATA))
+ retval = 1;
+ return (retval);
+}
+
+static u_int32_t
+adv_read_lram_32(adv, addr)
+ struct adv_softc *adv;
+ u_int16_t addr;
+{
+ u_int16_t val_low, val_high;
+
+ ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
+
+#if BYTE_ORDER == BIG_ENDIAN
+ val_high = ADV_INW(adv, ADV_LRAM_DATA);
+ val_low = ADV_INW(adv, ADV_LRAM_DATA);
+#else
+ val_low = ADV_INW(adv, ADV_LRAM_DATA);
+ val_high = ADV_INW(adv, ADV_LRAM_DATA);
+#endif
+
+ return (((u_int32_t)val_high << 16) | (u_int32_t)val_low);
+}
+
+static void
+adv_write_lram_32(adv, addr, value)
+ struct adv_softc *adv;
+ u_int16_t addr;
+ u_int32_t value;
+{
+ ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
+
+#if BYTE_ORDER == BIG_ENDIAN
+ ADV_OUTW(adv, ADV_LRAM_DATA, (u_int16_t)((value >> 16) & 0xFFFF));
+ ADV_OUTW(adv, ADV_LRAM_DATA, (u_int16_t)(value & 0xFFFF));
+#else
+ ADV_OUTW(adv, ADV_LRAM_DATA, (u_int16_t)(value & 0xFFFF));
+ ADV_OUTW(adv, ADV_LRAM_DATA, (u_int16_t)((value >> 16) & 0xFFFF));
+#endif
+}
+
+static void
+adv_write_lram_32_multi(adv, s_addr, buffer, count)
+ struct adv_softc *adv;
+ u_int16_t s_addr;
+ u_int32_t *buffer;
+ int count;
+{
+ ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
+ ADV_OUTSW(adv, ADV_LRAM_DATA, buffer, count * 2);
+}
+
+static u_int16_t
+adv_read_eeprom_16(adv, addr)
+ struct adv_softc *adv;
+ u_int8_t addr;
+{
+ u_int16_t read_wval;
+ u_int8_t cmd_reg;
+
+ adv_write_eeprom_cmd_reg(adv, ADV_EEPROM_CMD_WRITE_DISABLE);
+ DELAY(1000);
+ cmd_reg = addr | ADV_EEPROM_CMD_READ;
+ adv_write_eeprom_cmd_reg(adv, cmd_reg);
+ DELAY(1000);
+ read_wval = ADV_INW(adv, ADV_EEPROM_DATA);
+ DELAY(1000);
+ return (read_wval);
+}
+
+static u_int16_t
+adv_write_eeprom_16(adv, addr, value)
+ struct adv_softc *adv;
+ u_int8_t addr;
+ u_int16_t value;
+{
+ u_int16_t read_value;
+
+ read_value = adv_read_eeprom_16(adv, addr);
+ if (read_value != value) {
+ adv_write_eeprom_cmd_reg(adv, ADV_EEPROM_CMD_WRITE_ENABLE);
+ DELAY(1000);
+
+ ADV_OUTW(adv, ADV_EEPROM_DATA, value);
+ DELAY(1000);
+
+ adv_write_eeprom_cmd_reg(adv, ADV_EEPROM_CMD_WRITE | addr);
+ DELAY(20 * 1000);
+
+ adv_write_eeprom_cmd_reg(adv, ADV_EEPROM_CMD_WRITE_DISABLE);
+ DELAY(1000);
+ read_value = adv_read_eeprom_16(adv, addr);
+ }
+ return (read_value);
+}
+
+static int
+adv_write_eeprom_cmd_reg(adv, cmd_reg)
+ struct adv_softc *adv;
+ u_int8_t cmd_reg;
+{
+ u_int8_t read_back;
+ int retry;
+
+ retry = 0;
+ while (1) {
+ ADV_OUTB(adv, ADV_EEPROM_CMD, cmd_reg);
+ DELAY(1000);
+ read_back = ADV_INB(adv, ADV_EEPROM_CMD);
+ if (read_back == cmd_reg) {
+ return (1);
+ }
+ if (retry++ > ADV_EEPROM_MAX_RETRY) {
+ return (0);
+ }
+ }
+}
+
+static int
+adv_set_eeprom_config_once(adv, eeprom_config)
+ struct adv_softc *adv;
+ struct adv_eeprom_config *eeprom_config;
+{
+ int n_error;
+ u_int16_t *wbuf;
+ u_int16_t sum;
+ u_int8_t s_addr;
+ u_int8_t cfg_beg;
+ u_int8_t cfg_end;
+
+ wbuf = (u_int16_t *)eeprom_config;
+ n_error = 0;
+ sum = 0;
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ sum += *wbuf;
+ if (*wbuf != adv_write_eeprom_16(adv, s_addr, *wbuf)) {
+ n_error++;
+ }
+ }
+ if (adv->type & ADV_VL) {
+ cfg_beg = ADV_EEPROM_CFG_BEG_VL;
+ cfg_end = ADV_EEPROM_MAX_ADDR_VL;
+ } else {
+ cfg_beg = ADV_EEPROM_CFG_BEG;
+ cfg_end = ADV_EEPROM_MAX_ADDR;
+ }
+
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1); s_addr++, wbuf++) {
+ sum += *wbuf;
+ if (*wbuf != adv_write_eeprom_16(adv, s_addr, *wbuf)) {
+ n_error++;
+ }
+ }
+ *wbuf = sum;
+ if (sum != adv_write_eeprom_16(adv, s_addr, sum)) {
+ n_error++;
+ }
+ wbuf = (u_int16_t *)eeprom_config;
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ if (*wbuf != adv_read_eeprom_16(adv, s_addr)) {
+ n_error++;
+ }
+ }
+ for (s_addr = cfg_beg; s_addr <= cfg_end; s_addr++, wbuf++) {
+ if (*wbuf != adv_read_eeprom_16(adv, s_addr)) {
+ n_error++;
+ }
+ }
+ return (n_error);
+}
+
+static u_int32_t
+adv_load_microcode(adv, s_addr, mcode_buf, mcode_size)
+ struct adv_softc *adv;
+ u_int16_t s_addr;
+ u_int16_t *mcode_buf;
+ u_int16_t mcode_size;
+{
+ u_int32_t chksum;
+ u_int16_t mcode_lram_size;
+ u_int16_t mcode_chksum;
+
+ mcode_lram_size = mcode_size >> 1;
+ /* XXX Why zero the memory just before you write the whole thing?? */
+ /* adv_mset_lram_16(adv, s_addr, 0, mcode_lram_size);*/
+ adv_write_lram_16_multi(adv, s_addr, mcode_buf, mcode_lram_size);
+
+ chksum = adv_msum_lram_16(adv, s_addr, mcode_lram_size);
+ mcode_chksum = (u_int16_t)adv_msum_lram_16(adv, ADV_CODE_SEC_BEG,
+ ((mcode_size - s_addr - ADV_CODE_SEC_BEG) >> 1));
+ adv_write_lram_16(adv, ADVV_MCODE_CHKSUM_W, mcode_chksum);
+ adv_write_lram_16(adv, ADVV_MCODE_SIZE_W, mcode_size);
+ return (chksum);
+}
+
+static void
+adv_init_lram(adv)
+ struct adv_softc *adv;
+{
+ u_int8_t i;
+ u_int16_t s_addr;
+
+ adv_mset_lram_16(adv, ADV_QADR_BEG, 0,
+ (u_int16_t)((((int)adv->max_openings + 2 + 1) * 64) >> 1));
+
+ i = ADV_MIN_ACTIVE_QNO;
+ s_addr = ADV_QADR_BEG + ADV_QBLK_SIZE;
+
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_FWD, i + 1);
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_BWD, adv->max_openings);
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_QNO, i);
+ i++;
+ s_addr += ADV_QBLK_SIZE;
+ for (; i < adv->max_openings; i++, s_addr += ADV_QBLK_SIZE) {
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_FWD, i + 1);
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_BWD, i - 1);
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_QNO, i);
+ }
+
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_FWD, ADV_QLINK_END);
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_BWD, adv->max_openings - 1);
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_QNO, adv->max_openings);
+ i++;
+ s_addr += ADV_QBLK_SIZE;
+
+ for (; i <= adv->max_openings + 3; i++, s_addr += ADV_QBLK_SIZE) {
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_FWD, i);
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_BWD, i);
+ adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_QNO, i);
+ }
+}
+
+static int
+adv_init_microcode_var(adv)
+ struct adv_softc *adv;
+{
+ int i;
+
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ adv_write_lram_8(adv, ADVV_SDTR_DATA_BEG + i,
+ adv->sdtr_data[i]);
+ }
+
+ adv_init_qlink_var(adv);
+
+ /* XXX Again, what about wide busses??? */
+ adv_write_lram_8(adv, ADVV_DISC_ENABLE_B, adv->disc_enable);
+ adv_write_lram_8(adv, ADVV_HOSTSCSI_ID_B, 0x01 << adv->scsi_id);
+
+ /* What are the extra 8 bytes for?? */
+ adv_write_lram_32(adv, ADVV_OVERRUN_PADDR_D, vtophys(&(adv->overrun_buf[0])) + 8);
+
+ adv_write_lram_32(adv, ADVV_OVERRUN_BSIZE_D, ADV_OVERRUN_BSIZE - 8);
+
+#if 0
+ /* If we're going to print anything, RCS ids are more meaningful */
+ mcode_date = adv_read_lram_16(adv, ADVV_MC_DATE_W);
+ mcode_version = adv_read_lram_16(adv, ADVV_MC_VER_W);
+#endif
+ ADV_OUTW(adv, ADV_REG_PROG_COUNTER, ADV_MCODE_START_ADDR);
+ if (ADV_INW(adv, ADV_REG_PROG_COUNTER) != ADV_MCODE_START_ADDR) {
+ printf("adv%d: Unable to set program counter. Aborting.\n", adv->unit);
+ return (1);
+ }
+ if (adv_start_chip(adv) != 1) {
+ printf("adv%d: Unable to start on board processor. Aborting.\n",
+ adv->unit);
+ return (1);
+ }
+ return (0);
+}
+
+static void
+adv_init_qlink_var(adv)
+ struct adv_softc *adv;
+{
+ int i;
+ u_int16_t lram_addr;
+
+ adv_write_lram_8(adv, ADVV_NEXTRDY_B, 1);
+ adv_write_lram_8(adv, ADVV_DONENEXT_B, adv->max_openings);
+
+ adv_write_lram_16(adv, ADVV_FREE_Q_HEAD_W, 1);
+ adv_write_lram_16(adv, ADVV_DONE_Q_TAIL_W, adv->max_openings);
+
+ adv_write_lram_8(adv, ADVV_BUSY_QHEAD_B,
+ (u_int8_t)((int) adv->max_openings + 1));
+ adv_write_lram_8(adv, ADVV_DISC1_QHEAD_B,
+ (u_int8_t)((int) adv->max_openings + 2));
+
+ adv_write_lram_8(adv, ADVV_TOTAL_READY_Q_B, adv->max_openings);
+
+ adv_write_lram_16(adv, ADVV_ASCDVC_ERR_CODE_W, 0);
+ adv_write_lram_16(adv, ADVV_HALTCODE_W, 0);
+ adv_write_lram_8(adv, ADVV_STOP_CODE_B, 0);
+ adv_write_lram_8(adv, ADVV_SCSIBUSY_B, 0);
+ adv_write_lram_8(adv, ADVV_WTM_FLAG_B, 0);
+
+ adv_write_lram_8(adv, ADVV_CDBCNT_B, 0);
+
+ lram_addr = ADV_QADR_BEG;
+ for (i = 0; i < 32; i++, lram_addr += 2)
+ adv_write_lram_16(adv, lram_addr, 0);
+}
+static void
+adv_disable_interrupt(adv)
+ struct adv_softc *adv;
+{
+ u_int16_t cfg;
+
+ cfg = ADV_INW(adv, ADV_CONFIG_LSW);
+ ADV_OUTW(adv, ADV_CONFIG_LSW, cfg & ~ADV_CFG_LSW_HOST_INT_ON);
+}
+
+static void
+adv_enable_interrupt(adv)
+ struct adv_softc *adv;
+{
+ u_int16_t cfg;
+
+ cfg = ADV_INW(adv, ADV_CONFIG_LSW);
+ ADV_OUTW(adv, ADV_CONFIG_LSW, cfg | ADV_CFG_LSW_HOST_INT_ON);
+}
+
+static void
+adv_toggle_irq_act(adv)
+ struct adv_softc *adv;
+{
+ ADV_OUTW(adv, ADV_CHIP_STATUS, ADV_CIW_IRQ_ACT);
+ ADV_OUTW(adv, ADV_CHIP_STATUS, 0);
+}
+
+#if UNUSED
+static void
+adv_start_execution(adv)
+ struct adv_softc *adv;
+{
+ if (adv_read_lram_8(adv, ADV_STOP_CODE_B) != 0) {
+ adv_write_lram_8(adv, ADV_STOP_CODE_B, 0);
+ }
+}
+#endif
+
+static int
+adv_start_chip(adv)
+ struct adv_softc *adv;
+{
+ ADV_OUTB(adv, ADV_CHIP_CTRL, 0);
+ if ((ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_HALTED) != 0)
+ return (0);
+ return (1);
+}
+
+static int
+adv_stop_chip(adv)
+ struct adv_softc *adv;
+{
+ u_int8_t cc_val;
+
+ cc_val = ADV_INB(adv, ADV_CHIP_CTRL)
+ & (~(ADV_CC_SINGLE_STEP | ADV_CC_TEST | ADV_CC_DIAG));
+ ADV_OUTB(adv, ADV_CHIP_CTRL, cc_val | ADV_CC_HALT);
+ adv_set_chip_ih(adv, ADV_INS_HALT);
+ adv_set_chip_ih(adv, ADV_INS_RFLAG_WTM);
+ if ((ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_HALTED) == 0) {
+ return (0);
+ }
+ return (1);
+}
+
+static void
+adv_set_chip_ih(adv, ins_code)
+ struct adv_softc *adv;
+ u_int16_t ins_code;
+{
+ adv_set_bank(adv, 1);
+ ADV_OUTW(adv, ADV_REG_IH, ins_code);
+ adv_set_bank(adv, 0);
+}
+
+static void
+adv_set_bank(adv, bank)
+ struct adv_softc *adv;
+ u_int8_t bank;
+{
+ u_int8_t control;
+
+ /*
+ * Start out with the bank reset to 0
+ */
+ control = ADV_INB(adv, ADV_CHIP_CTRL)
+ & (~(ADV_CC_SINGLE_STEP | ADV_CC_TEST
+ | ADV_CC_DIAG | ADV_CC_SCSI_RESET
+ | ADV_CC_CHIP_RESET | ADV_CC_BANK_ONE));
+ if (bank == 1) {
+ control |= ADV_CC_BANK_ONE;
+ } else if (bank == 2) {
+ control |= ADV_CC_DIAG | ADV_CC_BANK_ONE;
+ }
+ ADV_OUTB(adv, ADV_CHIP_CTRL, control);
+}
+
+#if UNUSED
+static u_int8_t
+adv_get_chip_scsi_ctrl(adv)
+ struct adv_softc *adv;
+{
+ u_int8_t scsi_ctrl;
+
+ adv_set_bank(adv, 1);
+ scsi_ctrl = ADV_INB(adv, ADV_REG_SC);
+ adv_set_bank(adv, 0);
+ return (scsi_ctrl);
+}
+#endif
+
+static int
+adv_sgcount_to_qcount(sgcount)
+ int sgcount;
+{
+ int n_sg_list_qs;
+
+ n_sg_list_qs = ((sgcount - 1) / ADV_SG_LIST_PER_Q);
+ if (((sgcount - 1) % ADV_SG_LIST_PER_Q) != 0)
+ n_sg_list_qs++;
+ return (n_sg_list_qs + 1);
+}
+
+/*
+ * XXX Looks like more padding issues in this routine as well.
+ * There has to be a way to turn this into an insw.
+ */
+static void
+adv_get_q_info(adv, s_addr, inbuf, words)
+ struct adv_softc *adv;
+ u_int16_t s_addr;
+ u_int16_t *inbuf;
+ int words;
+{
+ int i;
+
+ ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
+ for (i = 0; i < words; i++, inbuf++) {
+ if (i == 5) {
+ continue;
+ }
+ *inbuf = ADV_INW(adv, ADV_LRAM_DATA);
+ }
+}
+
+static u_int
+adv_get_num_free_queues(adv, n_qs)
+ struct adv_softc *adv;
+ u_int8_t n_qs;
+{
+ u_int cur_used_qs;
+ u_int cur_free_qs;
+
+ if (n_qs == 1)
+ cur_used_qs = adv->cur_active +
+ adv->openings_needed +
+ ADV_MIN_FREE_Q;
+ else
+ cur_used_qs = adv->cur_active +
+ ADV_MIN_FREE_Q;
+
+ if ((cur_used_qs + n_qs) <= adv->max_openings) {
+ cur_free_qs = adv->max_openings - cur_used_qs;
+ return (cur_free_qs);
+ }
+ if (n_qs > 1)
+ if (n_qs > adv->openings_needed)
+ adv->openings_needed = n_qs;
+ return (0);
+}
+
+static u_int8_t
+adv_alloc_free_queues(adv, free_q_head, n_free_q)
+ struct adv_softc *adv;
+ u_int8_t free_q_head;
+ u_int8_t n_free_q;
+{
+ int i;
+
+ for (i = 0; i < n_free_q; i++) {
+ free_q_head = adv_alloc_free_queue(adv, free_q_head);
+ if (free_q_head == ADV_QLINK_END)
+ break;
+ }
+ return (free_q_head);
+}
+
+static u_int8_t
+adv_alloc_free_queue(adv, free_q_head)
+ struct adv_softc *adv;
+ u_int8_t free_q_head;
+{
+ u_int16_t q_addr;
+ u_int8_t next_qp;
+ u_int8_t q_status;
+
+ next_qp = ADV_QLINK_END;
+ q_addr = ADV_QNO_TO_QADDR(free_q_head);
+ q_status = adv_read_lram_8(adv, q_addr + ADV_SCSIQ_B_STATUS);
+
+ if ((q_status & QS_READY) == 0)
+ next_qp = adv_read_lram_8(adv, q_addr + ADV_SCSIQ_B_FWD);
+
+ return (next_qp);
+}
+
+static int
+adv_send_scsi_queue(adv, scsiq, n_q_required)
+ struct adv_softc *adv;
+ struct adv_scsi_q *scsiq;
+ u_int8_t n_q_required;
+{
+ u_int8_t free_q_head;
+ u_int8_t next_qp;
+ u_int8_t tid_no;
+ u_int8_t target_ix;
+ int retval;
+
+ retval = 1;
+ target_ix = scsiq->q2.target_ix;
+ tid_no = ADV_TIX_TO_TID(target_ix);
+ free_q_head = adv_read_lram_16(adv, ADVV_FREE_Q_HEAD_W) & 0xFF;
+ if ((next_qp = adv_alloc_free_queues(adv, free_q_head, n_q_required))
+ != ADV_QLINK_END) {
+ if (n_q_required > 1) {
+ /*
+ * Only reset the shortage value when processing
+ * a "normal" request and not error recovery or
+ * other requests that dip into our reserved queues.
+ * Generally speaking, a normal request will need more
+ * than one queue.
+ */
+ adv->openings_needed = 0;
+ }
+ scsiq->q1.q_no = free_q_head;
+
+ /*
+ * Now that we know our Q number, point our sense
+ * buffer pointer to an area below 16M if we are
+ * an ISA adapter.
+ */
+ if (adv->sense_buffers != NULL)
+ scsiq->q1.sense_addr = (u_int32_t)vtophys(&(adv->sense_buffers[free_q_head]));
+ adv_put_ready_sg_list_queue(adv, scsiq, free_q_head);
+ adv_write_lram_16(adv, ADVV_FREE_Q_HEAD_W, next_qp);
+ adv->cur_active += n_q_required;
+ retval = 0;
+ }
+ return (retval);
+}
+
+
+static void
+adv_put_ready_sg_list_queue(adv, scsiq, q_no)
+ struct adv_softc *adv;
+ struct adv_scsi_q *scsiq;
+ u_int8_t q_no;
+{
+ u_int8_t sg_list_dwords;
+ u_int8_t sg_index, i;
+ u_int8_t sg_entry_cnt;
+ u_int8_t next_qp;
+ u_int16_t q_addr;
+ struct adv_sg_head *sg_head;
+ struct adv_sg_list_q scsi_sg_q;
+
+ sg_head = scsiq->sg_head;
+
+ if (sg_head) {
+ sg_entry_cnt = sg_head->entry_cnt - 1;
+#ifdef DIAGNOSTIC
+ if (sg_entry_cnt == 0)
+ panic("adv_put_ready_sg_list_queue: ScsiQ with a SG list but only one element");
+ if ((scsiq->q1.cntl & QC_SG_HEAD) == 0)
+ panic("adv_put_ready_sg_list_queue: ScsiQ with a SG list but QC_SG_HEAD not set");
+#endif
+ q_addr = ADV_QNO_TO_QADDR(q_no);
+ sg_index = 1;
+ scsiq->q1.sg_queue_cnt = sg_head->queue_cnt;
+ scsi_sg_q.sg_head_qp = q_no;
+ scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
+ for (i = 0; i < sg_head->queue_cnt; i++) {
+ u_int8_t segs_this_q;
+
+ if (sg_entry_cnt > ADV_SG_LIST_PER_Q)
+ segs_this_q = ADV_SG_LIST_PER_Q;
+ else {
+ /* This will be the last segment then */
+ segs_this_q = sg_entry_cnt;
+ scsi_sg_q.cntl |= QCSG_SG_XFER_END;
+ }
+ scsi_sg_q.seq_no = i + 1;
+ sg_list_dwords = segs_this_q * 2;
+ if (i == 0) {
+ scsi_sg_q.sg_list_cnt = segs_this_q;
+ scsi_sg_q.sg_cur_list_cnt = segs_this_q;
+ } else {
+ scsi_sg_q.sg_list_cnt = segs_this_q - 1;
+ scsi_sg_q.sg_cur_list_cnt = segs_this_q - 1;
+ }
+ next_qp = adv_read_lram_8(adv, q_addr + ADV_SCSIQ_B_FWD);
+ scsi_sg_q.q_no = next_qp;
+ q_addr = ADV_QNO_TO_QADDR(next_qp);
+
+ adv_write_lram_16_multi(adv, q_addr + ADV_SCSIQ_SGHD_CPY_BEG,
+ (u_int16_t *)&scsi_sg_q,
+ sizeof(scsi_sg_q) >> 1);
+ adv_write_lram_32_multi(adv, q_addr + ADV_SGQ_LIST_BEG,
+ (u_int32_t *)&sg_head->sg_list[sg_index],
+ sg_list_dwords);
+ sg_entry_cnt -= segs_this_q;
+ sg_index += ADV_SG_LIST_PER_Q;
+ }
+ }
+ adv_put_ready_queue(adv, scsiq, q_no);
+}
+
+static void
+adv_put_ready_queue(adv, scsiq, q_no)
+ struct adv_softc *adv;
+ struct adv_scsi_q *scsiq;
+ u_int8_t q_no;
+{
+ u_int16_t q_addr;
+ u_int8_t tid_no;
+ u_int8_t sdtr_data;
+ u_int8_t syn_period_ix;
+ u_int8_t syn_offset;
+
+ if (((adv->initiate_sdtr & scsiq->q1.target_id) != 0) &&
+ ((adv->sdtr_done & scsiq->q1.target_id) == 0)) {
+
+ tid_no = ADV_TIX_TO_TID(scsiq->q2.target_ix);
+
+ sdtr_data = adv_read_lram_8(adv, ADVV_SDTR_DATA_BEG + tid_no);
+ syn_period_ix = (sdtr_data >> 4) & (ADV_SYN_XFER_NO - 1);
+ syn_offset = sdtr_data & ADV_SYN_MAX_OFFSET;
+ adv_msgout_sdtr(adv, adv_sdtr_period_tbl[syn_period_ix],
+ syn_offset);
+
+ scsiq->q1.cntl |= QC_MSG_OUT;
+ }
+ q_addr = ADV_QNO_TO_QADDR(q_no);
+
+ scsiq->q1.status = QS_FREE;
+
+ adv_write_lram_16_multi(adv, q_addr + ADV_SCSIQ_CDB_BEG,
+ (u_int16_t *)scsiq->cdbptr,
+ scsiq->q2.cdb_len >> 1);
+
+#if BYTE_ORDER == BIG_ENDIAN
+ adv_adj_scsiq_endian(scsiq);
+#endif
+
+ adv_put_scsiq(adv, q_addr + ADV_SCSIQ_CPY_BEG,
+ (u_int16_t *) &scsiq->q1.cntl,
+ ((sizeof(scsiq->q1) + sizeof(scsiq->q2)) / 2) - 1);
+
+#if CC_WRITE_IO_COUNT
+ adv_write_lram_16(adv, q_addr + ADV_SCSIQ_W_REQ_COUNT,
+ adv->req_count);
+#endif
+
+#if CC_CLEAR_DMA_REMAIN
+
+ adv_write_lram_32(adv, q_addr + ADV_SCSIQ_DW_REMAIN_XFER_ADDR, 0);
+ adv_write_lram_32(adv, q_addr + ADV_SCSIQ_DW_REMAIN_XFER_CNT, 0);
+#endif
+
+ adv_write_lram_16(adv, q_addr + ADV_SCSIQ_B_STATUS,
+ (scsiq->q1.q_no << 8) | QS_READY);
+}
+
+static void
+adv_put_scsiq(adv, s_addr, buffer, words)
+ struct adv_softc *adv;
+ u_int16_t s_addr;
+ u_int16_t *buffer;
+ int words;
+{
+ int i;
+
+ /*
+ * XXX This routine makes *gross* assumptions
+ * about padding in the data structures.
+ * Either the data structures should have explicit
+ * padding members added, or they should have padding
+ * turned off via compiler attributes depending on
+ * which yields better overall performance. My hunch
+ * would be that turning off padding would be the
+ * faster approach as an outsw is much faster than
+ * this crude loop and accessing un-aligned data
+ * members isn't *that* expensive. The other choice
+ * would be to modify the ASC script so that the
+ * the adv_scsiq_1 structure can be re-arranged so
+ * padding isn't required.
+ */
+ ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
+ for (i = 0; i < words; i++, buffer++) {
+ if (i == 2 || i == 10) {
+ continue;
+ }
+ ADV_OUTW(adv, ADV_LRAM_DATA, *buffer);
+ }
+}
+
+static u_int8_t
+adv_msgout_sdtr(adv, sdtr_period, sdtr_offset)
+ struct adv_softc *adv;
+ u_int8_t sdtr_period;
+ u_int8_t sdtr_offset;
+{
+ struct sdtr_xmsg sdtr_buf;
+
+ sdtr_buf.msg_type = MSG_EXTENDED;
+ sdtr_buf.msg_len = MSG_EXT_SDTR_LEN;
+ sdtr_buf.msg_req = MSG_EXT_SDTR;
+ sdtr_buf.xfer_period = sdtr_period;
+ sdtr_offset &= ADV_SYN_MAX_OFFSET;
+ sdtr_buf.req_ack_offset = sdtr_offset;
+ adv_write_lram_16_multi(adv, ADVV_MSGOUT_BEG,
+ (u_int16_t *) &sdtr_buf,
+ sizeof(sdtr_buf) / 2);
+
+ return (adv_get_card_sync_setting(sdtr_period, sdtr_offset));
+}
+
+static u_int8_t
+adv_get_card_sync_setting(period, offset)
+ u_int8_t period;
+ u_int8_t offset;
+{
+ u_int i;
+
+ if (period >= adv_sdtr_period_tbl[0]) {
+ for (i = 0; i < sizeof(adv_sdtr_period_tbl); i++) {
+ if (period <= adv_sdtr_period_tbl[i])
+ return ((adv_sdtr_period_tbl[i] << 4) | offset);
+ }
+ }
+ return (0);
+}
+
+static void
+adv_set_chip_sdtr(adv, sdtr_data, tid_no)
+ struct adv_softc *adv;
+ u_int8_t sdtr_data;
+ u_int8_t tid_no;
+{
+ ADV_OUTB(adv, ADV_SYN_OFFSET, sdtr_data);
+ adv_write_lram_8(adv, ADVV_SDTR_DONE_BEG + tid_no, sdtr_data);
+}
diff --git a/sys/dev/advansys/advlib.h b/sys/dev/advansys/advlib.h
new file mode 100644
index 000000000000..9e18137c3368
--- /dev/null
+++ b/sys/dev/advansys/advlib.h
@@ -0,0 +1,741 @@
+/*
+ * Definitions for low level routines and data structures
+ * for the Advanced Systems Inc. SCSI controllers chips.
+ *
+ * Copyright (c) 1996 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 immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+/*
+ * Ported from:
+ * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-1996 Advanced System Products, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ */
+
+typedef u_int8_t target_bit_vector;
+#define TARGET_BIT_VECTOR_SET -1
+#define ADV_SCSI_ID_BITS 3
+#define ADV_MAX_TID 7
+
+/* Enumeration of board types */
+typedef enum {
+ ADV_NONE = 0x000,
+ ADV_ISA = 0x001,
+ ADV_ISAPNP = 0x003,
+ ADV_VL = 0x004,
+ ADV_EISA = 0x008,
+ ADV_PCI = 0x010
+}adv_btype;
+
+typedef enum {
+ ADV_STATE_NONE = 0x000
+}adv_state;
+
+#define ADV_SYN_XFER_NO 8
+#define ADV_SYN_MAX_OFFSET 0x0F
+#define ADV_DEF_SDTR_OFFSET 0x0F
+#define ADV_DEF_SDTR_INDEX 0x00
+#define ADV_OVERRUN_BSIZE 0x00000048
+#define ADV_MAX_CDB_LEN 12
+#define ADV_MAX_SENSE_LEN 32
+#define ADV_MIN_SENSE_LEN 14
+
+#define ADV_TIDLUN_TO_IX(tid, lun) ((tid) | ((lun) << ADV_SCSI_ID_BITS) )
+#define ADV_TID_TO_TARGET_ID(tid) (0x01 << (tid))
+#define ADV_TIX_TO_TARGET_ID(tix) (0x01 << ((tix) & ADV_MAX_TID))
+#define ADV_TIX_TO_TID(tix) ((tix) & ADV_MAX_TID)
+#define ADV_TID_TO_TIX(tid) ((tid) & ADV_MAX_TID)
+#define ADV_TIX_TO_LUN(tix) (((tix) >> ADV_SCSI_ID_BITS) & ADV_MAX_LUN )
+
+#define ADV_INB(adv, offset) \
+ inb((adv)->iobase + (offset))
+#define ADV_INW(adv, offset) \
+ inw((adv)->iobase + (offset))
+#define ADV_INSB(adv, offset, valp, size) \
+ insb((adv)->iobase + (offset), (valp), (size))
+#define ADV_INSW(adv, offset, valp, size) \
+ insw((adv)->iobase + (offset), (valp), (size))
+#define ADV_INSL(adv, offset, valp, size) \
+ insl((adv)->iobase + (offset), (valp), (size))
+#define ADV_OUTB(adv, offset, val) \
+ outb((adv)->iobase + (offset), (val))
+#define ADV_OUTW(adv, offset, val) \
+ outw((adv)->iobase + (offset), (val))
+#define ADV_OUTSB(adv, offset, valp, size) \
+ outsb((adv)->iobase + (offset), (valp), (size))
+#define ADV_OUTSW(adv, offset, valp, size) \
+ outsw((adv)->iobase + (offset), (valp), (size))
+#define ADV_OUTSL(adv, offset, valp, size) \
+ outsl((adv)->iobase + (offset), (valp), (size))
+
+/*
+ * XXX
+ * PnP port addresses
+ * I believe that these are standard PnP address and should be replaced
+ * by the values in a central ISA PnP header file when we get one.
+ */
+#define ADV_ISA_PNP_PORT_ADDR (0x279)
+#define ADV_ISA_PNP_PORT_WRITE (ADV_ISA_PNP_PORT_ADDR+0x800)
+
+/*
+ * Board Signatures
+ */
+#define ADV_SIGNATURE_WORD 0x0000
+#define ADV_1000_ID0W 0x04C1
+#define ADV_1000_ID0W_FIX 0x00C1
+
+#define ADV_SIGNATURE_BYTE 0x0001
+#define ADV_1000_ID1B 0x25
+
+/*
+ * Chip Revision Number
+ */
+#define ADV_NONEISA_CHIP_REVISION 0x0003
+#define ADV_CHIP_MIN_VER_VL 0x01
+#define ADV_CHIP_MAX_VER_VL 0x07
+
+#define ADV_CHIP_MIN_VER_PCI 0x09
+#define ADV_CHIP_MAX_VER_PCI 0x0F
+#define ADV_CHIP_VER_PCI_BIT 0x08
+
+#define ADV_CHIP_MIN_VER_ISA 0x11
+#define ADV_CHIP_MIN_VER_ISA_PNP 0x21
+#define ADV_CHIP_MAX_VER_ISA 0x27
+#define ADV_CHIP_VER_ISA_BIT 0x30
+#define ADV_CHIP_VER_ISAPNP_BIT 0x20
+
+#define ADV_CHIP_VER_ASYN_BUG 0x21
+
+#define ADV_CHIP_MIN_VER_EISA 0x41
+#define ADV_CHIP_MAX_VER_EISA 0x47
+#define ADV_CHIP_VER_EISA_BIT 0x40
+
+#define ADV_HALTCODE_W 0x0040
+#define ADV_STOP_CODE_B 0x0034
+#define ADV_STOP_REQ_RISC_STOP 0x01
+#define ADV_STOP_ACK_RISC_STOP 0x03
+
+#define ADV_CHIP_CTRL 0x000F
+#define ADV_CC_CHIP_RESET 0x80
+#define ADV_CC_SCSI_RESET 0x40
+#define ADV_CC_HALT 0x20
+#define ADV_CC_SINGLE_STEP 0x10
+#define ADV_CC_TEST 0x04
+#define ADV_CC_BANK_ONE 0x02
+#define ADV_CC_DIAG 0x01
+
+#define ADV_CHIP_STATUS 0x000E
+#define ADV_CSW_TEST1 0x8000
+#define ADV_CSW_AUTO_CONFIG 0x4000
+#define ADV_CSW_RESERVED1 0x2000
+#define ADV_CSW_IRQ_WRITTEN 0x1000
+#define ADV_CSW_33MHZ_SELECTED 0x0800
+#define ADV_CSW_TEST2 0x0400
+#define ADV_CSW_TEST3 0x0200
+#define ADV_CSW_RESERVED2 0x0100
+#define ADV_CSW_DMA_DONE 0x0080
+#define ADV_CSW_FIFO_RDY 0x0040
+#define ADV_CSW_EEP_READ_DONE 0x0020
+#define ADV_CSW_HALTED 0x0010
+#define ADV_CSW_SCSI_RESET_ACTIVE 0x0008
+#define ADV_CSW_PARITY_ERR 0x0004
+#define ADV_CSW_SCSI_RESET_LATCH 0x0002
+#define ADV_CSW_INT_PENDING 0x0001
+/*
+ * XXX I don't understand the relevence of the naming
+ * convention change here. What does CIW stand for?
+ * Perhaps this is to differentiate read and write
+ * values?
+ */
+#define ADV_CIW_INT_ACK 0x0100
+#define ADV_CIW_TEST1 0x0200
+#define ADV_CIW_TEST2 0x0400
+#define ADV_CIW_SEL_33MHZ 0x0800
+#define ADV_CIW_IRQ_ACT 0x1000
+
+#define ADV_REG_IH 0x0002
+#define ADV_INS_HALTINT 0x6281
+#define ADV_INS_HALT 0x6280
+#define ADV_INS_SINT 0x6200
+#define ADV_INS_RFLAG_WTM 0x7380
+
+
+#define ADV_REG_SC 0x0009
+
+#define ADV_REG_PROG_COUNTER 0x000C
+#define ADV_MCODE_START_ADDR 0x0080
+
+#define ADV_CONFIG_LSW 0x0002
+#define ADV_CFG_LSW_HOST_INT_ON 0x0020
+#define ADV_CFG_LSW_BIOS_ON 0x0040
+#define ADV_CFG_LSW_VERA_BURST_ON 0x0080
+#define ADV_CFG_LSW_SCSI_PARITY_ON 0x0800
+
+#define ADV_CONFIG_MSW 0x0004
+#define ADV_CFG_MSW_SCSI_TARGET_ON 0x0080
+#define ADV_CFG_MSW__LRAM_8BITS_ON 0x0800
+#define ADV_CFG_MSW_CLR_MASK 0xF0C0
+
+
+#define ADV_EEPROM_DATA 0x0006
+
+#define ADV_EEPROM_CMD 0x0007
+#define ADV_EEPROM_CMD_READ 0x80
+#define ADV_EEPROM_CMD_WRITE 0x40
+#define ADV_EEPROM_CMD_WRITE_ENABLE 0x30
+#define ADV_EEPROM_CMD_WRITE_DISABLE 0x00
+
+/*
+ * EEPROM routine constants
+ * XXX What about wide controllers?
+ * Surely they have space for 8 more targets.
+ */
+#define ADV_EEPROM_CFG_BEG_VL 2
+#define ADV_EEPROM_MAX_ADDR_VL 15
+#define ADV_EEPROM_CFG_BEG 32
+#define ADV_EEPROM_MAX_ADDR 45
+#define ADV_EEPROM_MAX_RETRY 20
+
+struct adv_eeprom_config {
+ u_int16_t cfg_lsw;
+
+ u_int16_t cfg_msw;
+
+ u_int8_t init_sdtr;
+ u_int8_t disc_enable;
+
+ u_int8_t use_cmd_qng;
+ u_int8_t start_motor;
+
+ u_int8_t max_total_qng;
+ u_int8_t max_tag_qng;
+
+ u_int8_t bios_scan;
+ u_int8_t power_up_wait;
+
+ u_int8_t no_scam;
+ u_int8_t scsi_id_dma_speed;
+#define EEPROM_SCSI_ID_MASK 0x0F
+#define EEPROM_DMA_SPEED_MASK 0xF0
+#define EEPROM_DMA_SPEED(ep) (((ep).scsi_id_dma_speed & EEPROM_DMA_SPEED_MASK) >> 8)
+#define EEPROM_SCSIID(ep) ((ep).scsi_id_dma_speed & EEPROM_SCSI_ID_MASK)
+#define EEPROM_SET_SCSIID(ep, id) ((ep).scsi_id_dma_speed &= EEPROM_SCSI_ID_MASK; \
+ (ep).scsi_id_dma_speed |= ((id) & EEPROM_SCSI_ID_MASK))
+ /* XXX What about wide controllers??? */
+ u_int8_t sdtr_data[8];
+ u_int8_t adapter_info[6];
+
+ u_int16_t cntl;
+
+ u_int16_t chksum;
+};
+
+/*
+ * Instruction data and code segment addresses,
+ * and transaction address translation (queues).
+ * All addresses refer to on board LRAM.
+ */
+#define ADV_DATA_SEC_BEG 0x0080
+#define ADV_DATA_SEC_END 0x0080
+#define ADV_CODE_SEC_BEG 0x0080
+#define ADV_CODE_SEC_END 0x0080
+#define ADV_QADR_BEG 0x4000
+#define ADV_QADR_END 0x7FFF
+#define ADV_QLAST_ADR 0x7FC0
+#define ADV_QBLK_SIZE 0x40
+#define ADV_BIOS_DATA_QBEG 0xF8
+#define ADV_MAX_QNO 0xF8
+#define ADV_QADR_USED (ADV_MAX_QNO * 64)
+#define ADV_QNO_TO_QADDR(q_no) ((ADV_QADR_BEG) + ((u_int16_t)(q_no) << 6))
+
+#define ADV_MIN_ACTIVE_QNO 0x01
+#define ADV_QLINK_END 0xFF
+
+#define ADV_MAX_SG_QUEUE 5
+#define ADV_SG_LIST_PER_Q 7
+#define ADV_MAX_SG_LIST (1 + ((ADV_SG_LIST_PER_Q) * (ADV_MAX_SG_QUEUE)))
+
+#define ADV_MIN_REMAIN_Q 0x02
+#define ADV_DEF_MAX_TOTAL_QNG 0x40
+#define ADV_MIN_TAG_Q_PER_DVC 0x04
+#define ADV_DEF_TAG_Q_PER_DVC 0x04
+#define ADV_MIN_FREE_Q ADV_MIN_REMAIN_Q
+#define ADV_MIN_TOTAL_QNG ((ADV_MAX_SG_QUEUE)+(ADV_MIN_FREE_Q))
+#define ADV_MAX_TOTAL_QNG 240
+#define ADV_MAX_INRAM_TAG_QNG 16
+#define ADV_MAX_PCI_INRAM_TOTAL_QNG 20
+
+#define ADV_DEF_IRQ_NO 10
+#define ADV_MAX_IRQ_NO 15
+#define ADV_MIN_IRQ_NO 10
+
+#define ADV_SCSIQ_CPY_BEG 4
+#define ADV_SCSIQ_SGHD_CPY_BEG 2
+
+#define ADV_SCSIQ_B_FWD 0
+#define ADV_SCSIQ_B_BWD 1
+
+#define ADV_SCSIQ_B_STATUS 2
+#define ADV_SCSIQ_B_QNO 3
+
+#define ADV_SCSIQ_B_CNTL 4
+#define ADV_SCSIQ_B_SG_QUEUE_CNT 5
+
+#define ADV_LRAM_ADDR 0x000A
+#define ADV_LRAM_DATA 0x0008
+
+#define ADV_SYN_OFFSET 0x000B
+
+/* LRAM Offsets */
+#define ADVV_MSGOUT_BEG 0x0000
+#define ADVV_MSGOUT_SDTR_PERIOD (ADVV_MSGOUT_BEG+3)
+#define ADVV_MSGOUT_SDTR_OFFSET (ADVV_MSGOUT_BEG+4)
+
+#define ADVV_MSGIN_BEG (ADVV_MSGOUT_BEG+8)
+#define ADVV_MSGIN_SDTR_PERIOD (ADVV_MSGIN_BEG+3)
+#define ADVV_MSGIN_SDTR_OFFSET (ADVV_MSGIN_BEG+4)
+
+#define ADVV_SDTR_DATA_BEG (ADVV_MSGIN_BEG+8)
+#define ADVV_SDTR_DONE_BEG (ADVV_SDTR_DATA_BEG+8)
+#define ADVV_MAX_DVC_QNG_BEG 0x0020
+
+#define ADVV_ASCDVC_ERR_CODE_W 0x0030
+#define ADVV_MCODE_CHKSUM_W 0x0032
+#define ADVV_MCODE_SIZE_W 0x0034
+#define ADVV_STOP_CODE_B 0x0036
+#define ADVV_DVC_ERR_CODE_B 0x0037
+
+#define ADVV_OVERRUN_PADDR_D 0x0038
+#define ADVV_OVERRUN_BSIZE_D 0x003C
+
+#define ADVV_HALTCODE_W 0x0040
+#define ADV_HALT_EXTMSG_IN 0x8000
+#define ADV_HALT_CHK_CONDITION 0x8100
+#define ADV_HALT_SS_QUEUE_FULL 0x8200
+#define ADV_HALT_SDTR_REJECTED 0x4000
+
+#define ADVV_CHKSUM_W 0x0042
+#define ADVV_MC_DATE_W 0x0044
+#define ADVV_MC_VER_W 0x0046
+#define ADVV_NEXTRDY_B 0x0048
+#define ADVV_DONENEXT_B 0x0049
+#define ADVV_USE_TAGGED_QNG_B 0x004A
+#define ADVV_SCSIBUSY_B 0x004B
+#define ADVV_CDBCNT_B 0x004C
+#define ADVV_CURCDB_B 0x004D
+#define ADVV_RCLUN_B 0x004E
+#define ADVV_BUSY_QHEAD_B 0x004F
+#define ADVV_DISC1_QHEAD_B 0x0050
+
+#define ADVV_DISC_ENABLE_B 0x0052
+#define ADVV_CAN_TAGGED_QNG_B 0x0053
+#define ADVV_HOSTSCSI_ID_B 0x0055
+#define ADVV_MCODE_CNTL_B 0x0056
+#define ADVV_NULL_TARGET_B 0x0057
+
+#define ADVV_FREE_Q_HEAD_W 0x0058
+#define ADVV_DONE_Q_TAIL_W 0x005A
+#define ADVV_FREE_Q_HEAD_B (ADVV_FREE_Q_HEAD_W+1)
+#define ADVV_DONE_Q_TAIL_B (ADVV_DONE_Q_TAIL_W+1)
+
+#define ADVV_HOST_FLAG_B 0x005D
+#define ADV_HOST_FLAG_IN_ISR 0x01
+#define ADV_HOST_FLAG_ACK_INT 0x02
+
+
+#define ADVV_TOTAL_READY_Q_B 0x0064
+#define ADVV_VER_SERIAL_B 0x0065
+#define ADVV_HALTCODE_SAVED_W 0x0066
+#define ADVV_WTM_FLAG_B 0x0068
+#define ADVV_RISC_FLAG_B 0x006A
+#define ADV_RISC_FLAG_GEN_INT 0x01
+#define ADV_RISC_FLAG_REQ_SG_LIST 0x02
+
+#define ADVV_REQ_SG_LIST_QP 0x006B
+struct adv_softc
+{
+ /* The overrun buffer must be quad word aligned */
+ u_int8_t overrun_buf[ADV_OVERRUN_BSIZE];
+ int unit;
+ u_int32_t iobase;
+ adv_btype type;
+ target_bit_vector needs_async_bug_fix;
+ target_bit_vector initiate_sdtr;
+ target_bit_vector sdtr_done;
+ target_bit_vector cmd_qng_enabled;
+ target_bit_vector unit_not_ready;
+ target_bit_vector start_motor;
+ target_bit_vector no_scam;
+ target_bit_vector disc_enable;
+ u_int16_t control;
+#define ADV_CNTL_INITIATOR 0x0001
+#define ADV_CNTL_BIOS_GT_1GB 0x0002
+#define ADV_CNTL_BIOS_GT_2_DISK 0x0004
+#define ADV_CNTL_BIOS_REMOVABLE 0x0008
+#define ADV_CNTL_NO_SCAM 0x0010
+#define ADV_CNTL_NO_PCI_FIX_ASYN_XFER 0x0020
+#define ADV_CNTL_INT_MULTI_Q 0x0080
+#define ADV_CNTL_NO_LUN_SUPPORT 0x0040
+#define ADV_CNTL_NO_VERIFY_COPY 0x0100
+#define ADV_CNTL_RESET_SCSI 0x0200
+#define ADV_CNTL_INIT_INQUIRY 0x0400
+#define ADV_CNTL_INIT_VERBOSE 0x0800
+#define ADV_CNTL_SCSI_PARITY 0x1000
+#define ADV_CNTL_BURST_MODE 0x2000
+#define ADV_CNTL_USE_8_IOP_BASE 0x4000
+
+ u_int16_t bug_fix_control;
+#define ADV_BUG_FIX_ADD_ONE_BYTE 0x0001
+
+ adv_state state;
+ u_int32_t max_dma_count;
+ u_int8_t isa_dma_speed;
+ u_int8_t scsi_id;
+ u_int8_t chip_version;
+ u_int8_t max_tags_per_target;
+ u_int8_t max_openings;
+ u_int8_t cur_active;
+ u_int8_t openings_needed;
+ u_int8_t sdtr_data[16];
+ struct scsi_sense_data *sense_buffers;
+};
+
+/*
+ * Structures for talking to the RISC engine.
+ */
+struct adv_scsiq_1 {
+ u_int8_t status;
+#define QS_FREE 0x00
+#define QS_READY 0x01
+#define QS_DISC1 0x02
+#define QS_DISC2 0x04
+#define QS_BUSY 0x08
+#define QS_ABORTED 0x40
+#define QS_DONE 0x80
+
+ u_int8_t q_no; /*
+ * Queue ID of the first queue
+ * used in this transaction.
+ */
+ u_int8_t cntl;
+#define QC_NO_CALLBACK 0x01
+#define QC_SG_SWAP_QUEUE 0x02
+#define QC_SG_HEAD 0x04
+#define QC_DATA_IN 0x08
+#define QC_DATA_OUT 0x10
+#define QC_URGENT 0x20
+#define QC_MSG_OUT 0x40
+#define QC_REQ_SENSE 0x80
+
+ u_int8_t sg_queue_cnt; /* Number of SG entries */
+
+ u_int8_t target_id; /* target id as a bit vector */
+ u_int8_t target_lun; /* LUN - taken from our xs */
+
+ u_int32_t data_addr; /*
+ * physical addres of first
+ * (possibly only) segment
+ * to transfer.
+ */
+ u_int32_t data_cnt; /*
+ * byte count of the first
+ * (possibly only) segment
+ * to transfer.
+ */
+ u_int32_t sense_addr; /*
+ * physical address of the sense
+ * buffer.
+ */
+ u_int8_t sense_len; /* length of sense buffer */
+ u_int8_t user_def;
+};
+
+struct adv_scsiq_2 {
+ u_int32_t xs_ptr; /* Pointer to our scsi_xfer */
+ u_int8_t target_ix; /* Combined TID and LUN */
+
+ u_int8_t flag;
+ u_int8_t cdb_len; /*
+ * Number of bytes in the SCSI
+ * command to execute.
+ */
+ u_int8_t tag_code; /*
+ * Tag type for this transaction
+ * (SIMPLE, ORDERED, HEAD )
+ */
+#define ADV_TAG_FLAG_ADD_ONE_BYTE 0x10
+#define ADV_TAG_FLAG_ISAPNP_ADD_BYTES 0x40
+
+ u_int16_t vm_id;
+};
+
+struct adv_scsiq_3 {
+ u_int8_t done_stat;
+#define QD_IN_PROGRESS 0x00
+#define QD_NO_ERROR 0x01
+#define QD_ABORTED_BY_HOST 0x02
+#define QD_WITH_ERROR 0x04
+#define QD_INVALID_REQUEST 0x80
+#define QD_INVALID_HOST_NUM 0x81
+#define QD_INVALID_DEVICE 0x82
+#define QD_ERR_INTERNAL 0xFF
+
+ u_int8_t host_stat;
+#define QHSTA_NO_ERROR 0x00
+#define QHSTA_M_SEL_TIMEOUT 0x11
+#define QHSTA_M_DATA_OVER_RUN 0x12
+#define QHSTA_M_DATA_UNDER_RUN 0x12
+#define QHSTA_M_UNEXPECTED_BUS_FREE 0x13
+#define QHSTA_M_BAD_BUS_PHASE_SEQ 0x14
+
+#define QHSTA_D_QDONE_SG_LIST_CORRUPTED 0x21
+#define QHSTA_D_ASC_DVC_ERROR_CODE_SET 0x22
+#define QHSTA_D_HOST_ABORT_FAILED 0x23
+#define QHSTA_D_EXE_SCSI_Q_FAILED 0x24
+#define QHSTA_D_EXE_SCSI_Q_BUSY_TIMEOUT 0x25
+#define QHSTA_D_ASPI_NO_BUF_POOL 0x26
+
+#define QHSTA_M_WTM_TIMEOUT 0x41
+#define QHSTA_M_BAD_CMPL_STATUS_IN 0x42
+#define QHSTA_M_NO_AUTO_REQ_SENSE 0x43
+#define QHSTA_M_AUTO_REQ_SENSE_FAIL 0x44
+#define QHSTA_M_TARGET_STATUS_BUSY 0x45
+#define QHSTA_M_BAD_TAG_CODE 0x46
+
+#define QHSTA_M_BAD_QUEUE_FULL_OR_BUSY 0x47
+
+#define QHSTA_D_LRAM_CMP_ERROR 0x81
+
+#define QHSTA_M_MICRO_CODE_ERROR_HALT 0xA1
+
+ u_int8_t scsi_stat;
+ u_int8_t scsi_msg;
+};
+
+struct adv_scsiq_4 {
+ u_int8_t cdb[ADV_MAX_CDB_LEN];
+ u_int8_t y_first_sg_list_qp;
+ u_int8_t y_working_sg_qp;
+ u_int8_t y_working_sg_ix;
+ u_int8_t y_cntl;
+ u_int16_t x_req_count;
+ u_int16_t x_reconnect_rtn;
+ u_int32_t x_saved_data_addr;
+ u_int32_t x_saved_data_cnt;
+};
+
+struct adv_q_done_info {
+ struct adv_scsiq_2 d2;
+ struct adv_scsiq_3 d3;
+ u_int8_t q_status;
+ u_int8_t q_no;
+ u_int8_t cntl;
+ u_int8_t sense_len;
+ u_int8_t user_def;
+ u_int8_t res;
+ u_int32_t remain_bytes;
+};
+
+struct adv_sg_entry {
+ u_int32_t addr;
+ u_int32_t bytes;
+};
+
+struct adv_sg_head {
+ u_int8_t entry_cnt; /* Number of SG entries in this list */
+
+ u_int8_t queue_cnt; /*
+ * Number of queues required to store
+ * entry_cnt SG entries.
+ */
+
+ u_int8_t entry_to_copy; /*
+ * Number of SG entries to copy to the
+ * board.
+ */
+ u_int8_t res;
+ struct adv_sg_entry sg_list[ADV_MAX_SG_LIST];
+};
+
+#define ADV_MIN_SG_LIST 2
+
+struct adv_min_sg_head {
+ u_int8_t entry_cnt;
+
+ u_int8_t queue_cnt;
+
+ u_int8_t entry_to_copy;
+ u_int8_t res;
+ struct adv_sg_entry sg_list[ADV_MIN_SG_LIST];
+};
+
+#define QCX_SORT (0x0001)
+#define QCX_COALEASE (0x0002)
+
+#if CC_LINK_BUSY_Q
+struct asc_ext_scsi_q {
+ u_int32_t lba;
+ u_int16_t lba_len;
+ struct adv_scsi_q *next;
+ struct adv_scsi_q *join;
+ u_int16_t cntl;
+ u_int16_t buffer_id;
+ u_int8_t q_required;
+ u_int8_t res;
+};
+#endif
+
+struct adv_scsi_q {
+ struct adv_scsiq_1 q1;
+ struct adv_scsiq_2 q2;
+ struct scsi_generic *cdbptr; /*
+ * Pointer to the SCSI command
+ * to execute.
+ */
+
+ struct adv_sg_head *sg_head; /*
+ * Pointer to possible SG list
+ */
+#if CC_LINK_BUSY_Q
+ struct adv_ext_scsi_q ext;
+#endif
+
+};
+#define ADV_SCSIQ_D_DATA_ADDR 8
+#define ADV_SCSIQ_D_DATA_CNT 12
+#define ADV_SCSIQ_B_SENSE_LEN 20
+#define ADV_SCSIQ_DONE_INFO_BEG 22
+#define ADV_SCSIQ_D_SRBPTR 22
+#define ADV_SCSIQ_B_TARGET_IX 26
+#define ADV_SCSIQ_B_CDB_LEN 28
+#define ADV_SCSIQ_B_TAG_CODE 29
+#define ADV_SCSIQ_W_VM_ID 30
+#define ADV_SCSIQ_DONE_STATUS 32
+#define ADV_SCSIQ_HOST_STATUS 33
+#define ADV_SCSIQ_SCSI_STATUS 34
+#define ADV_SCSIQ_CDB_BEG 36
+#define ADV_SCSIQ_DW_REMAIN_XFER_ADDR 56
+#define ADV_SCSIQ_DW_REMAIN_XFER_CNT 60
+#define ADV_SCSIQ_B_SG_WK_QP 49
+#define ADV_SCSIQ_B_SG_WK_IX 50
+#define ADV_SCSIQ_W_REQ_COUNT 52
+#define ADV_SCSIQ_B_LIST_CNT 6
+#define ADV_SCSIQ_B_CUR_LIST_CNT 7
+
+
+struct adv_scsi_req_q {
+ struct adv_scsiq_1 r1;
+ struct adv_scsiq_2 r2;
+ u_int8_t *cdbptr;
+ struct adv_sg_head *sg_head;
+
+#if CC_LINK_BUSY_Q
+ struct adv_ext_scsi_q ext;
+#endif
+
+ u_int8_t *sense_ptr;
+
+ struct adv_scsiq_3 r3;
+ u_int8_t cdb[ADV_MAX_CDB_LEN];
+ u_int8_t sense[ADV_MIN_SENSE_LEN];
+};
+
+struct adv_risc_q {
+ u_int8_t fwd;
+ u_int8_t bwd;
+ struct adv_scsiq_1 i1;
+ struct adv_scsiq_2 i2;
+ struct adv_scsiq_3 i3;
+ struct adv_scsiq_4 i4;
+};
+
+struct adv_sg_list_q {
+ u_int8_t seq_no;
+ u_int8_t q_no;
+ u_int8_t cntl;
+#define QCSG_SG_XFER_LIST 0x02
+#define QCSG_SG_XFER_MORE 0x04
+#define QCSG_SG_XFER_END 0x08
+
+ u_int8_t sg_head_qp;
+ u_int8_t sg_list_cnt;
+ u_int8_t sg_cur_list_cnt;
+};
+#define ADV_SGQ_B_SG_CNTL 4
+#define ADV_SGQ_B_SG_HEAD_QP 5
+#define ADV_SGQ_B_SG_LIST_CNT 6
+#define ADV_SGQ_B_SG_CUR_LIST_CNT 7
+#define ADV_SGQ_LIST_BEG 8
+
+struct asc_risc_sg_list_q {
+ u_int8_t fwd;
+ u_int8_t bwd;
+ struct adv_sg_list_q sg;
+ struct adv_sg_entry sg_list[7];
+};
+
+
+/* LRAM routines */
+u_int8_t adv_read_lram_8 __P((struct adv_softc *adv, u_int16_t addr));
+void adv_write_lram_8 __P((struct adv_softc *adv, u_int16_t addr,
+ u_int8_t value));
+u_int16_t adv_read_lram_16 __P((struct adv_softc *adv, u_int16_t addr));
+void adv_write_lram_16 __P((struct adv_softc *adv, u_int16_t addr,
+ u_int16_t value));
+
+/* Intialization */
+void adv_get_board_type __P((struct adv_softc *adv));
+u_int16_t adv_get_eeprom_config __P((struct adv_softc *adv,
+ struct adv_eeprom_config *eeprom_config));
+int adv_set_eeprom_config __P((struct adv_softc *adv,
+ struct adv_eeprom_config *eeprom_config));
+int adv_reset_chip_and_scsi_bus __P((struct adv_softc *adv));
+int adv_test_external_lram __P((struct adv_softc* adv));
+int adv_init_lram_and_mcode __P((struct adv_softc *adv));
+u_int8_t adv_get_chip_irq __P((struct adv_softc *adv));
+u_int8_t adv_set_chip_irq __P((struct adv_softc *adv, u_int8_t irq_no));
+
+/* Queue handling and execution */
+int adv_execute_scsi_queue __P((struct adv_softc *adv, struct adv_scsi_q *scsiq));
+u_int8_t adv_copy_lram_doneq __P((struct adv_softc *adv, u_int16_t q_addr,
+ struct adv_q_done_info *scsiq, u_int32_t max_dma_count));
+
+/* Chip Control */
+int adv_stop_execution __P((struct adv_softc *adv));
+int adv_is_chip_halted __P((struct adv_softc *adv));
+
+/* Interrupt processing */
+void adv_ack_interrupt __P((struct adv_softc *adv));
+void adv_isr_chip_halted __P((struct adv_softc *adv));
diff --git a/sys/dev/advansys/advmcode.c b/sys/dev/advansys/advmcode.c
new file mode 100644
index 000000000000..37fd53ce80ba
--- /dev/null
+++ b/sys/dev/advansys/advmcode.c
@@ -0,0 +1,166 @@
+/*
+ * Downloadable microcode for Advanced Systems Inc. SCSI controllers
+ *
+ * $Id$
+ *
+ * Obtained from:
+ * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-1996 Advanced System Products, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ */
+
+#include <sys/param.h>
+
+u_int8_t adv_mcode[] =
+{
+ 0x01, 0x03, 0x01, 0x19, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0x0A, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x23, 0x00, 0x16, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x88, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x73, 0x48, 0x04, 0x36, 0x00, 0x00, 0xA2, 0xC8, 0x00, 0x80, 0x73, 0x03, 0x23, 0x36, 0x40,
+ 0xB6, 0x00, 0x36, 0x00, 0x06, 0xD6, 0x0D, 0xD2, 0x15, 0xDE, 0x12, 0xDA, 0x00, 0xA2, 0xC8, 0x00,
+ 0x92, 0x80, 0xE0, 0x97, 0x50, 0x00, 0xF5, 0x00, 0x0A, 0x98, 0xDF, 0x23, 0x36, 0x60, 0xB6, 0x00,
+ 0x92, 0x80, 0x4F, 0x00, 0xF5, 0x00, 0x0A, 0x98, 0xEF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80,
+ 0x80, 0x62, 0x92, 0x80, 0x00, 0x62, 0x92, 0x80, 0x00, 0x46, 0x17, 0xEE, 0x13, 0xEA, 0x02, 0x01,
+ 0x09, 0xD8, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xDC, 0x00, 0x68, 0x97, 0x7F, 0x23, 0x04, 0x61,
+ 0x84, 0x01, 0xB2, 0x84, 0xCF, 0xC1, 0x80, 0x73, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xE8, 0x01,
+ 0x68, 0x97, 0xD4, 0x81, 0x00, 0x33, 0x02, 0x00, 0x82, 0x88, 0x80, 0x73, 0x80, 0x77, 0x00, 0x01,
+ 0x01, 0xA1, 0x08, 0x01, 0x4F, 0x00, 0x46, 0x97, 0x07, 0xA6, 0x12, 0x01, 0x00, 0x33, 0x03, 0x00,
+ 0x82, 0x88, 0x03, 0x03, 0x03, 0xDE, 0x00, 0x33, 0x05, 0x00, 0x82, 0x88, 0xCE, 0x00, 0x69, 0x60,
+ 0xCE, 0x00, 0x02, 0x03, 0x4A, 0x60, 0x00, 0xA2, 0x86, 0x01, 0x80, 0x63, 0x07, 0xA6, 0x32, 0x01,
+ 0x86, 0x81, 0x03, 0x03, 0x80, 0x63, 0xE2, 0x00, 0x07, 0xA6, 0x42, 0x01, 0x00, 0x33, 0x04, 0x00,
+ 0x82, 0x88, 0x03, 0x07, 0x02, 0x01, 0x04, 0xCA, 0x0D, 0x23, 0x2A, 0x98, 0x4D, 0x04, 0xD0, 0x84,
+ 0x05, 0xD8, 0x0D, 0x23, 0x2A, 0x98, 0xCD, 0x04, 0x15, 0x23, 0xB8, 0x88, 0xFB, 0x23, 0x02, 0x61,
+ 0x82, 0x01, 0x80, 0x63, 0x02, 0x03, 0x06, 0xA3, 0x70, 0x01, 0x00, 0x33, 0x0A, 0x00, 0x82, 0x88,
+ 0x4E, 0x00, 0x07, 0xA3, 0x7C, 0x01, 0x00, 0x33, 0x0B, 0x00, 0x82, 0x88, 0xCD, 0x04, 0x36, 0x2D,
+ 0x00, 0x33, 0x1A, 0x00, 0x82, 0x88, 0x50, 0x04, 0x96, 0x81, 0x06, 0xAB, 0x90, 0x01, 0x96, 0x81,
+ 0x4E, 0x00, 0x07, 0xA3, 0xA0, 0x01, 0x50, 0x00, 0x00, 0xA3, 0x4A, 0x01, 0x00, 0x05, 0x8A, 0x81,
+ 0x08, 0x97, 0x02, 0x01, 0x05, 0xC6, 0x04, 0x23, 0xA0, 0x01, 0x15, 0x23, 0xA1, 0x01, 0xCC, 0x81,
+ 0xFD, 0x23, 0x02, 0x61, 0x82, 0x01, 0x0A, 0xDA, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xC2, 0x01,
+ 0x80, 0x63, 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, 0x1B, 0x00, 0x82, 0x88, 0x06, 0x23, 0x2A, 0x98,
+ 0xCD, 0x04, 0xB2, 0x84, 0x06, 0x01, 0x00, 0xA2, 0xE2, 0x01, 0x57, 0x60, 0x00, 0xA0, 0xE8, 0x01,
+ 0xB2, 0x84, 0x80, 0x23, 0xA0, 0x01, 0xB2, 0x84, 0x80, 0x73, 0x4B, 0x00, 0x06, 0x61, 0x00, 0xA2,
+ 0x10, 0x02, 0x04, 0x01, 0x0D, 0xDE, 0x02, 0x01, 0x03, 0xCC, 0x4F, 0x00, 0x46, 0x97, 0x0A, 0x82,
+ 0x08, 0x23, 0x02, 0x41, 0x82, 0x01, 0x4F, 0x00, 0x24, 0x97, 0x48, 0x04, 0xFF, 0x23, 0x84, 0x80,
+ 0xB2, 0x97, 0x00, 0x46, 0x56, 0x00, 0x03, 0xC0, 0x01, 0x23, 0xE8, 0x00, 0x81, 0x73, 0x06, 0x29,
+ 0x03, 0x42, 0x06, 0xE2, 0x03, 0xEE, 0x66, 0xEB, 0x11, 0x23, 0xB8, 0x88, 0xC6, 0x97, 0xFA, 0x80,
+ 0x80, 0x73, 0x80, 0x77, 0x06, 0xA6, 0x3E, 0x02, 0x00, 0x33, 0x31, 0x00, 0x82, 0x88, 0x04, 0x01,
+ 0x03, 0xD8, 0x74, 0x98, 0x02, 0x96, 0x50, 0x82, 0xA2, 0x95, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63,
+ 0xB6, 0x2D, 0x02, 0xA6, 0x7A, 0x02, 0x07, 0xA6, 0x68, 0x02, 0x06, 0xA6, 0x6C, 0x02, 0x03, 0xA6,
+ 0x70, 0x02, 0x00, 0x33, 0x10, 0x00, 0x82, 0x88, 0x4A, 0x95, 0x52, 0x82, 0xF8, 0x95, 0x52, 0x82,
+ 0x04, 0x23, 0xA0, 0x01, 0x14, 0x23, 0xA1, 0x01, 0x16, 0x84, 0x04, 0x01, 0x0C, 0xDC, 0xE0, 0x23,
+ 0x25, 0x61, 0xEF, 0x00, 0x14, 0x01, 0x4F, 0x04, 0xA8, 0x01, 0x6F, 0x00, 0xA5, 0x01, 0x03, 0x23,
+ 0xA4, 0x01, 0x06, 0x23, 0x9C, 0x01, 0x24, 0x2B, 0x1C, 0x01, 0x02, 0xA6, 0xAC, 0x02, 0x07, 0xA6,
+ 0x68, 0x02, 0x06, 0xA6, 0x6C, 0x02, 0x00, 0x33, 0x12, 0x00, 0x82, 0x88, 0x00, 0x0E, 0x80, 0x63,
+ 0x00, 0x43, 0x00, 0xA0, 0x9A, 0x02, 0x4D, 0x04, 0x04, 0x01, 0x0B, 0xDC, 0xE7, 0x23, 0x04, 0x61,
+ 0x84, 0x01, 0x10, 0x31, 0x12, 0x35, 0x14, 0x01, 0xEC, 0x00, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00,
+ 0xEC, 0x82, 0x18, 0x23, 0x04, 0x61, 0x18, 0xA0, 0xE4, 0x02, 0x04, 0x01, 0x8E, 0xC8, 0x00, 0x33,
+ 0x1F, 0x00, 0x82, 0x88, 0x08, 0x31, 0x0A, 0x35, 0x0C, 0x39, 0x0E, 0x3D, 0x40, 0x98, 0xB6, 0x2D,
+ 0x01, 0xA6, 0x0E, 0x03, 0x00, 0xA6, 0x1C, 0x03, 0x07, 0xA6, 0x2A, 0x03, 0x06, 0xA6, 0x2E, 0x03,
+ 0x03, 0xA6, 0xFA, 0x03, 0x02, 0xA6, 0x7A, 0x02, 0x00, 0x33, 0x33, 0x00, 0x82, 0x88, 0x08, 0x23,
+ 0xB3, 0x01, 0x04, 0x01, 0x0E, 0xD0, 0x00, 0x33, 0x14, 0x00, 0x82, 0x88, 0x10, 0x23, 0xB3, 0x01,
+ 0x04, 0x01, 0x07, 0xCC, 0x00, 0x33, 0x15, 0x00, 0x82, 0x88, 0x4A, 0x95, 0xF0, 0x82, 0xF8, 0x95,
+ 0xF0, 0x82, 0x44, 0x98, 0x80, 0x42, 0x40, 0x98, 0x48, 0xE4, 0x04, 0x01, 0x29, 0xC8, 0x31, 0x05,
+ 0x07, 0x01, 0x00, 0xA2, 0x72, 0x03, 0x00, 0x43, 0x87, 0x01, 0x05, 0x05, 0x48, 0x98, 0x40, 0x98,
+ 0x00, 0xA6, 0x34, 0x03, 0x07, 0xA6, 0x6A, 0x03, 0x03, 0xA6, 0x16, 0x04, 0x06, 0xA6, 0x6E, 0x03,
+ 0x01, 0xA6, 0x34, 0x03, 0x00, 0x33, 0x25, 0x00, 0x82, 0x88, 0x4A, 0x95, 0x50, 0x83, 0xF8, 0x95,
+ 0x50, 0x83, 0x04, 0x01, 0x0C, 0xCE, 0x03, 0xC8, 0x00, 0x33, 0x42, 0x00, 0x82, 0x88, 0x00, 0x01,
+ 0x05, 0x05, 0xFF, 0xA2, 0x90, 0x03, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x4C, 0x83, 0x05, 0x05,
+ 0x01, 0xA6, 0x9A, 0x03, 0x00, 0xA6, 0xAA, 0x03, 0xF0, 0x83, 0x68, 0x98, 0x80, 0x42, 0x01, 0xA6,
+ 0x9A, 0x03, 0xBA, 0x83, 0x00, 0x33, 0x2F, 0x00, 0x82, 0x88, 0x68, 0x98, 0x80, 0x42, 0x00, 0xA6,
+ 0xAA, 0x03, 0xBA, 0x83, 0x00, 0x33, 0x26, 0x00, 0x82, 0x88, 0x38, 0x2B, 0x80, 0x32, 0x80, 0x36,
+ 0x04, 0x23, 0xA0, 0x01, 0x12, 0x23, 0xA1, 0x01, 0xF0, 0x83, 0x04, 0xF0, 0x80, 0x6B, 0x00, 0x33,
+ 0x20, 0x00, 0x82, 0x88, 0x03, 0xA6, 0xEE, 0x03, 0x07, 0xA6, 0xE6, 0x03, 0x06, 0xA6, 0xEA, 0x03,
+ 0x00, 0x33, 0x17, 0x00, 0x82, 0x88, 0x4A, 0x95, 0xD4, 0x83, 0xF8, 0x95, 0xD4, 0x83, 0xFA, 0x83,
+ 0x04, 0xF0, 0x80, 0x6B, 0x00, 0x33, 0x20, 0x00, 0x82, 0x88, 0xB6, 0x2D, 0x03, 0xA6, 0x16, 0x04,
+ 0x07, 0xA6, 0x0E, 0x04, 0x06, 0xA6, 0x12, 0x04, 0x00, 0x33, 0x30, 0x00, 0x82, 0x88, 0x4A, 0x95,
+ 0xFA, 0x83, 0xF8, 0x95, 0xFA, 0x83, 0xA2, 0x0D, 0x80, 0x63, 0x07, 0xA6, 0x24, 0x04, 0x00, 0x33,
+ 0x18, 0x00, 0x82, 0x88, 0x03, 0x03, 0x80, 0x63, 0xA3, 0x01, 0x07, 0xA4, 0x2E, 0x04, 0x23, 0x01,
+ 0x00, 0xA2, 0x50, 0x04, 0x0A, 0xA0, 0x40, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1D, 0x00, 0x82, 0x88,
+ 0x0B, 0xA0, 0x4C, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1E, 0x00, 0x82, 0x88, 0x42, 0x23, 0xB8, 0x88,
+ 0x00, 0x23, 0x22, 0xA3, 0xB2, 0x04, 0x08, 0x23, 0x22, 0xA3, 0x6C, 0x04, 0x28, 0x23, 0x22, 0xA3,
+ 0x78, 0x04, 0x02, 0x23, 0x22, 0xA3, 0x8E, 0x04, 0x42, 0x23, 0xB8, 0x88, 0x4A, 0x00, 0x06, 0x61,
+ 0x00, 0xA0, 0x78, 0x04, 0x45, 0x23, 0xB8, 0x88, 0xC6, 0x97, 0x00, 0xA2, 0x8A, 0x04, 0x74, 0x98,
+ 0x00, 0x33, 0x00, 0x82, 0xC0, 0x20, 0x81, 0x62, 0xF6, 0x81, 0x47, 0x23, 0xB8, 0x88, 0x04, 0x01,
+ 0x0C, 0xDE, 0x14, 0x01, 0x00, 0xA2, 0xA6, 0x04, 0xC6, 0x97, 0x74, 0x98, 0x00, 0x33, 0x00, 0x81,
+ 0xC0, 0x20, 0x81, 0x62, 0x10, 0x82, 0x43, 0x23, 0xB8, 0x88, 0x04, 0x23, 0xA0, 0x01, 0x44, 0x23,
+ 0xA1, 0x01, 0x80, 0x73, 0x4D, 0x00, 0x03, 0xA3, 0xC0, 0x04, 0x00, 0x33, 0x27, 0x00, 0x82, 0x88,
+ 0x04, 0x01, 0x04, 0xDC, 0x02, 0x23, 0xA2, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xC6, 0x97, 0xF4, 0x94,
+ 0x4B, 0x00, 0xF6, 0x00, 0x4F, 0x04, 0x4F, 0x00, 0x00, 0xA3, 0xEE, 0x04, 0x00, 0x05, 0x76, 0x00,
+ 0x06, 0x61, 0x00, 0xA2, 0xE8, 0x04, 0xD6, 0x84, 0x08, 0x97, 0xCD, 0x04, 0xF2, 0x84, 0x48, 0x04,
+ 0xFF, 0x23, 0x84, 0x80, 0x02, 0x01, 0x03, 0xDA, 0x80, 0x23, 0x82, 0x01, 0x02, 0x85, 0x02, 0x23,
+ 0xA0, 0x01, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x0E, 0x05, 0x1D, 0x01, 0x04, 0xD6, 0xFF, 0x23,
+ 0x86, 0x41, 0x4B, 0x60, 0xCB, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x49, 0x00, 0x81, 0x01, 0x04, 0x01,
+ 0x02, 0xC8, 0x30, 0x01, 0x80, 0x01, 0xF7, 0x04, 0x03, 0x01, 0x49, 0x04, 0x80, 0x01, 0xC9, 0x00,
+ 0x00, 0x05, 0x00, 0x01, 0xFF, 0xA0, 0x2E, 0x05, 0x77, 0x04, 0x01, 0x23, 0xEA, 0x00, 0x5D, 0x00,
+ 0xFE, 0xC7, 0x00, 0x62, 0x00, 0x23, 0xEA, 0x00, 0x00, 0x63, 0x07, 0xA4, 0xA0, 0x05, 0x03, 0x03,
+ 0x02, 0xA0, 0x5C, 0x05, 0x9C, 0x85, 0x00, 0x33, 0x2D, 0x00, 0x82, 0x88, 0x04, 0xA0, 0x82, 0x05,
+ 0x80, 0x63, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x6E, 0x05, 0x1D, 0x01, 0x06, 0xD6, 0x02, 0x23,
+ 0x02, 0x41, 0x82, 0x01, 0x50, 0x00, 0x24, 0x97, 0xD0, 0x84, 0x04, 0x23, 0x02, 0x41, 0x82, 0x01,
+ 0xD0, 0x84, 0x08, 0xA0, 0x88, 0x05, 0x9C, 0x85, 0x03, 0xA0, 0x8E, 0x05, 0x9C, 0x85, 0x01, 0xA0,
+ 0x9A, 0x05, 0x88, 0x00, 0x80, 0x63, 0x78, 0x96, 0x4A, 0x85, 0x88, 0x86, 0x80, 0x63, 0x4A, 0x85,
+ 0x00, 0x63, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0xDE, 0x05, 0x1D, 0x01, 0x18, 0xD4, 0xC0, 0x23,
+ 0x07, 0x41, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0xC0, 0x05, 0x00, 0x33, 0x37, 0x00, 0x82, 0x88,
+ 0x1D, 0x01, 0x02, 0xD6, 0x46, 0x23, 0xB8, 0x88, 0x63, 0x60, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6,
+ 0xD8, 0x05, 0x00, 0x33, 0x38, 0x00, 0x82, 0x88, 0xEF, 0x04, 0x6F, 0x00, 0x00, 0x63, 0x4B, 0x00,
+ 0x06, 0x41, 0xCB, 0x00, 0x52, 0x00, 0x06, 0x61, 0x00, 0xA2, 0xF2, 0x05, 0xC0, 0x23, 0x07, 0x41,
+ 0x00, 0x63, 0x80, 0x23, 0x07, 0x41, 0x00, 0x63, 0x80, 0x67, 0x08, 0x23, 0x83, 0x03, 0x80, 0x63,
+ 0x00, 0x63, 0x06, 0xA6, 0x14, 0x06, 0x07, 0xA6, 0x64, 0x06, 0x02, 0xA6, 0xBC, 0x06, 0x00, 0x33,
+ 0x39, 0x00, 0x82, 0x88, 0x00, 0x00, 0x01, 0xA0, 0xD6, 0x06, 0xA2, 0x95, 0x83, 0x03, 0x80, 0x63,
+ 0x06, 0xA6, 0x28, 0x06, 0x07, 0xA6, 0x64, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x40, 0x0E, 0x80, 0x63,
+ 0x01, 0x00, 0x06, 0xA6, 0x40, 0x06, 0x07, 0xA6, 0x64, 0x06, 0x00, 0x33, 0x3A, 0x00, 0x82, 0x88,
+ 0x40, 0x0E, 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, 0x32, 0x06, 0x06, 0xA6, 0x58, 0x06, 0x07, 0xA6,
+ 0x64, 0x06, 0x00, 0x33, 0x3B, 0x00, 0x82, 0x88, 0x80, 0x67, 0x40, 0x0E, 0x80, 0x63, 0x07, 0xA6,
+ 0x64, 0x06, 0x00, 0x63, 0x03, 0x03, 0x80, 0x63, 0x88, 0x00, 0x01, 0xA2, 0x78, 0x06, 0x07, 0xA2,
+ 0xBC, 0x06, 0x00, 0x33, 0x35, 0x00, 0x82, 0x88, 0x07, 0xA6, 0x82, 0x06, 0x00, 0x33, 0x2A, 0x00,
+ 0x82, 0x88, 0x03, 0x03, 0x03, 0xA2, 0x8E, 0x06, 0x07, 0x23, 0x80, 0x00, 0xC8, 0x86, 0x80, 0x63,
+ 0x89, 0x00, 0x0A, 0x2B, 0x07, 0xA6, 0x9E, 0x06, 0x00, 0x33, 0x29, 0x00, 0x82, 0x88, 0x00, 0x43,
+ 0x00, 0xA2, 0xAA, 0x06, 0xC0, 0x0E, 0x80, 0x63, 0x94, 0x86, 0xC0, 0x0E, 0x00, 0x33, 0x00, 0x80,
+ 0xC0, 0x20, 0x81, 0x62, 0x04, 0x01, 0x08, 0xDA, 0x80, 0x63, 0x00, 0x63, 0x80, 0x67, 0x00, 0x33,
+ 0x00, 0x40, 0xC0, 0x20, 0x81, 0x62, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x20, 0x06,
+ 0x00, 0x33, 0x2C, 0x00, 0x82, 0x88, 0x0C, 0xA2, 0xF0, 0x06, 0xA2, 0x95, 0x83, 0x03, 0x80, 0x63,
+ 0x06, 0xA6, 0xEE, 0x06, 0x07, 0xA6, 0x64, 0x06, 0x00, 0x33, 0x3D, 0x00, 0x82, 0x88, 0x00, 0x00,
+ 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x0C, 0xA0, 0x06, 0x07, 0x07, 0xA6, 0x64, 0x06, 0xBF, 0x23,
+ 0x04, 0x61, 0x84, 0x01, 0xB2, 0x84, 0x00, 0x63, 0xF0, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x00, 0x01,
+ 0xF2, 0x00, 0x01, 0x05, 0x80, 0x01, 0x72, 0x04, 0x71, 0x00, 0x81, 0x01, 0x70, 0x04, 0x80, 0x05,
+ 0x81, 0x05, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x70, 0x00,
+ 0x81, 0x01, 0x70, 0x04, 0x71, 0x00, 0x81, 0x01, 0x72, 0x00, 0x80, 0x01, 0x71, 0x04, 0x70, 0x00,
+ 0x80, 0x01, 0x70, 0x04, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x00, 0x01, 0xF1, 0x00,
+ 0x70, 0x00, 0x80, 0x01, 0x70, 0x04, 0x71, 0x00, 0x80, 0x01, 0x72, 0x00, 0x81, 0x01, 0x71, 0x04,
+ 0x70, 0x00, 0x81, 0x01, 0x70, 0x04, 0x00, 0x63, 0x00, 0x23, 0xB3, 0x01, 0x83, 0x05, 0xA3, 0x01,
+ 0xA2, 0x01, 0xA1, 0x01, 0x01, 0x23, 0xA0, 0x01, 0x00, 0x01, 0xC8, 0x00, 0x03, 0xA1, 0x86, 0x07,
+ 0x00, 0x33, 0x07, 0x00, 0x82, 0x88, 0x80, 0x05, 0x81, 0x05, 0x04, 0x01, 0x11, 0xC8, 0x48, 0x00,
+ 0xB0, 0x01, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x05, 0x01, 0x48, 0x04, 0x00, 0x43, 0x00, 0xA2,
+ 0xA6, 0x07, 0x00, 0x05, 0x9C, 0x87, 0x00, 0x01, 0xC8, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x05, 0x05,
+ 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x80, 0x43, 0x76, 0x08,
+ 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02,
+ 0x00, 0xA0, 0xD6, 0x07, 0xD8, 0x87, 0x00, 0x43, 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63,
+ 0xF3, 0x04, 0x00, 0x23, 0xF4, 0x00, 0x74, 0x00, 0x80, 0x43, 0xF4, 0x00, 0xCF, 0x40, 0x00, 0xA2,
+ 0x06, 0x08, 0x74, 0x04, 0x02, 0x01, 0xF7, 0xC9, 0xF6, 0xD9, 0x00, 0x01, 0x01, 0xA1, 0xE6, 0x07,
+ 0xC6, 0x97, 0xF4, 0x94, 0xE6, 0x87, 0x73, 0x04, 0x00, 0x63, 0xF3, 0x04, 0x75, 0x04, 0x1C, 0x88,
+ 0x02, 0x01, 0x04, 0xD8, 0x08, 0x97, 0xC6, 0x97, 0xF4, 0x94, 0x0C, 0x88, 0x75, 0x00, 0x00, 0xA3,
+ 0x26, 0x08, 0x00, 0x05, 0x10, 0x88, 0x73, 0x04, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6,
+ 0x38, 0x08, 0x00, 0x33, 0x3E, 0x00, 0x82, 0x88, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63,
+ 0x38, 0x2B, 0x5E, 0x88, 0x38, 0x2B, 0x54, 0x88, 0x32, 0x09, 0x31, 0x05, 0x54, 0x98, 0x05, 0x05,
+ 0xB2, 0x09, 0x00, 0x63, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x3E, 0x00, 0x63, 0x80, 0x32,
+ 0x80, 0x36, 0x80, 0x3A, 0x80, 0x3E, 0x00, 0x63, 0x38, 0x2B, 0x40, 0x32, 0x40, 0x36, 0x40, 0x3A,
+ 0x40, 0x3E, 0x00, 0x63, 0x5A, 0x20, 0xC9, 0x40, 0x00, 0xA0, 0x74, 0x08, 0x5D, 0x00, 0xFE, 0xC3,
+ 0x00, 0x63, 0x80, 0x73, 0xE6, 0x20, 0x02, 0x23, 0xE8, 0x00, 0x82, 0x73, 0xFF, 0xFD, 0x80, 0x73,
+ 0x13, 0x23, 0xB8, 0x88, 0x66, 0x20, 0xC0, 0x20, 0x04, 0x23, 0xA0, 0x01, 0xA1, 0x23, 0xA1, 0x01,
+ 0x81, 0x62, 0xA2, 0x88, 0x80, 0x73, 0x80, 0x77, 0x68, 0x00, 0x00, 0xA2, 0x80, 0x00, 0x03, 0xC2,
+ 0xF1, 0xC7, 0x41, 0x23, 0xB8, 0x88, 0x11, 0x23, 0xA1, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xB2, 0x84,
+};
+
+u_int16_t adv_mcode_size = sizeof(adv_mcode);
+u_int32_t adv_mcode_chksum = 0x012258FB;
diff --git a/sys/dev/advansys/advmcode.h b/sys/dev/advansys/advmcode.h
new file mode 100644
index 000000000000..c5688e579cf5
--- /dev/null
+++ b/sys/dev/advansys/advmcode.h
@@ -0,0 +1,19 @@
+/*
+ * Exported interface to downloadable microcode for AdvanSys SCSI Adapters
+ *
+ * $Id$
+ *
+ * Obtained from:
+ *
+ * Copyright (c) 1995-1996 Advanced System Products, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ */
+
+extern u_int16_t adv_mcode[];
+extern u_int16_t adv_mcode_size;
+extern u_int32_t adv_mcode_chksum;
diff --git a/sys/i386/isa/adv_isa.c b/sys/i386/isa/adv_isa.c
new file mode 100644
index 000000000000..e7c89151f762
--- /dev/null
+++ b/sys/i386/isa/adv_isa.c
@@ -0,0 +1,236 @@
+/*
+ * Device probe and attach routines for the following
+ * Advanced Systems Inc. SCSI controllers:
+ *
+ * Connectivity Products:
+ * ABP5140 - Bus-Master PnP ISA 16 CDB
+ *
+ * Single Channel Products:
+ * ABP542 - Bus-Master ISA 240 CDB
+ * ABP5150 - Bus-Master ISA 240 CDB (shipped by HP with the 4020i CD-R drive)
+ * ABP842 - Bus-Master VL 240 CDB
+ *
+ * Dual Channel Products:
+ * ABP852 - Dual Channel Bus-Master VL 240 CDB Per Channel
+ *
+ * Copyright (c) 1996 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 immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+
+#include <i386/scsi/advansys.h>
+
+#define ADV_ISA_MAX_DMA_ADDR (0x00FFFFFFL)
+#define ADV_ISA_MAX_DMA_COUNT (0x00FFFFFFL)
+
+#define ADV_VL_MAX_DMA_ADDR (0x07FFFFFFL)
+#define ADV_VL_MAX_DMA_COUNT (0x07FFFFFFL)
+
+/* Possible port addresses an ISA or VL adapter can live at */
+u_int16_t adv_isa_ioports[] =
+{
+ 0x100,
+ 0x110, /* First selection in BIOS setup */
+ 0x120,
+ 0x130, /* Second selection in BIOS setup */
+ 0x140,
+ 0x150, /* Third selection in BIOS setup */
+ 0x190, /* Fourth selection in BIOS setup */
+ 0x210, /* Fifth selection in BIOS setup */
+ 0x230, /* Sixth selection in BIOS setup */
+ 0x250, /* Seventh selection in BIOS setup */
+ 0x330 /* Eighth and default selection in BIOS setup */
+};
+
+#define MAX_ISA_IOPORT_INDEX (sizeof(adv_isa_ioports)/sizeof(u_short) - 1)
+
+static int advisaprobe __P((struct isa_device *id));
+static int advisaattach __P((struct isa_device *id));
+static void adv_set_isapnp_wait_for_key __P((void));
+static int adv_find_signature __P((u_int16_t iobase));
+
+void adv_isa_intr __P((int unit));
+
+struct isa_driver advdriver =
+{
+ advisaprobe,
+ advisaattach,
+ "adv"
+};
+
+static int
+advisaprobe(id)
+ struct isa_device *id;
+{
+ int port_index;
+ int max_port_index;
+
+ /*
+ * Default to scanning all possible device locations.
+ */
+ port_index = 0;
+ max_port_index = MAX_ISA_IOPORT_INDEX;
+
+ if (id->id_iobase > 0) {
+ for (;port_index <= max_port_index; port_index++)
+ if (id->id_iobase >= adv_isa_ioports[port_index])
+ break;
+ if ((port_index > max_port_index)
+ || (id->id_iobase != adv_isa_ioports[port_index])) {
+ printf("adv%d: Invalid baseport of 0x%x specified. "
+ "Neerest valid baseport is 0x%x. Failing "
+ "probe.\n", id->id_unit, id->id_iobase,
+ (port_index <= max_port_index) ?
+ adv_isa_ioports[port_index] :
+ adv_isa_ioports[max_port_index]);
+ return 0;
+ }
+ max_port_index = port_index;
+ }
+
+ /* Perform the actual probing */
+ adv_set_isapnp_wait_for_key();
+ for (;port_index <= max_port_index; port_index++) {
+ u_int16_t port_addr = adv_isa_ioports[port_index];
+ if (port_addr == 0)
+ /* Already been attached */
+ continue;
+ if (adv_find_signature(port_addr)) {
+ /*
+ * Got one. Now allocate our softc
+ * and see if we can initialize the card.
+ */
+ struct adv_softc *adv;
+ adv = adv_alloc(id->id_unit, port_addr);
+ if (adv == NULL)
+ return (0);
+
+ id->id_iobase = adv->iobase;
+ /*
+ * Determine the chip version.
+ */
+ adv->chip_version = ADV_INB(adv,
+ ADV_NONEISA_CHIP_REVISION);
+
+ if (adv_init(adv) != 0) {
+ adv_free(adv);
+ return (0);
+ }
+ switch (adv->type) {
+ case ADV_ISAPNP:
+ if (adv->chip_version == ADV_CHIP_VER_ASYN_BUG)
+ adv->needs_async_bug_fix = TARGET_BIT_VECTOR_SET;
+ /* Fall Through */
+ case ADV_ISA:
+ adv->max_dma_count = ADV_ISA_MAX_DMA_COUNT;
+ break;
+
+ case ADV_VL:
+ adv->max_dma_count = ADV_VL_MAX_DMA_COUNT;
+ break;
+ }
+
+ if ((adv->type & ADV_ISAPNP) == ADV_ISAPNP) {
+ }
+
+ /* Determine our IRQ */
+ if (id->id_irq == 0 /* irq ? */)
+ id->id_irq = 1 << adv_get_chip_irq(adv);
+ else
+ adv_set_chip_irq(adv, ffs(id->id_irq) - 1);
+
+ /* Mark as probed */
+ adv_isa_ioports[port_index] = 0;
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static int
+advisaattach(id)
+ struct isa_device *id;
+{
+ struct adv_softc *adv;
+
+ adv = advsoftcs[id->id_unit];
+ return (adv_attach(adv));
+}
+
+static void
+adv_set_isapnp_wait_for_key(void)
+{
+ static int isapnp_wait_set = 0;
+ if (isapnp_wait_set == 0) {
+ outb(ADV_ISA_PNP_PORT_ADDR, 0x02);
+ outb(ADV_ISA_PNP_PORT_WRITE, 0x02);
+ isapnp_wait_set++;
+ }
+ return;
+}
+
+/*
+ * Determine if there is a board at "iobase" by looking
+ * for the AdvanSys signatures. Return 1 if a board is
+ * found, 0 otherwise.
+ */
+static int
+adv_find_signature(iobase)
+ u_int16_t iobase;
+{
+ u_int16_t signature;
+
+ if (inb(iobase + ADV_SIGNATURE_BYTE) == ADV_1000_ID1B) {
+ signature = inw(iobase + ADV_SIGNATURE_WORD );
+ if ((signature == ADV_1000_ID0W)
+ || (signature == ADV_1000_ID0W_FIX))
+ return (1);
+ }
+ return (0);
+}
+
+
+/*
+ * Handle an ISA interrupt.
+ * XXX should go away as soon as ISA interrupt handlers
+ * take a (void *) arg.
+ */
+void
+adv_isa_intr(unit)
+ int unit;
+{
+ struct adv_softc *arg = advsoftcs[unit];
+ adv_intr((void *)arg);
+}
diff --git a/sys/i386/scsi/advansys.c b/sys/i386/scsi/advansys.c
new file mode 100644
index 000000000000..27ff1d71a93f
--- /dev/null
+++ b/sys/i386/scsi/advansys.c
@@ -0,0 +1,798 @@
+/*
+ * Generic driver for the Advanced Systems Inc. SCSI controllers
+ * Product specific probe and attach routines can be found in:
+ *
+ * i386/isa/adv_isa.c ABP5140, ABP542, ABP5150, ABP842, ABP852
+ *
+ * Copyright (c) 1996 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 immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+/*
+ * Ported from:
+ * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-1996 Advanced System Products, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+
+#include <machine/clock.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsiconf.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <i386/scsi/advansys.h>
+
+static void adv_scsi_cmd __P((struct scsi_xfer *xs));
+static void advminphys __P((struct buf *bp));
+static void * adv_get_cdb __P((void *adapter_softc, struct scsi_queue *sq));
+static void adv_free_cdb __P((void *adapter_softc, struct scsi_queue *sq,
+ void *cdb));
+static timeout_t
+ adv_timeout;
+static int adv_qdone __P((struct adv_softc *adv));
+static void adv_done __P((struct adv_softc *adv,
+ struct adv_q_done_info *qdonep));
+static int adv_poll __P((struct adv_softc *ahc, struct scsi_xfer *xs));
+
+struct adv_softc *advsoftcs[NADV]; /* XXX Config should handle this */
+
+static struct scsi_adapter adv_switch =
+{
+ adv_scsi_cmd,
+ advminphys,
+ 0,
+ 0,
+ adv_get_cdb,
+ adv_free_cdb,
+ "adv"
+};
+
+static void
+adv_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct adv_softc *adv;
+ struct adv_scsi_q scsiq;
+ struct adv_sg_head sghead;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("adv_scsi_cmd\n"));
+
+ adv = (struct adv_softc *)xs->sc_link->scsibus->adpt_link.adpt_softc;
+
+ /*
+ * Build up the request
+ */
+ scsiq.q1.cntl = 0;
+ scsiq.q1.sg_queue_cnt = 0;
+ scsiq.q1.status = 0;
+ scsiq.q1.q_no = 0;
+ scsiq.q1.target_id = ADV_TID_TO_TARGET_ID(xs->sc_link->target);
+ scsiq.q1.target_lun = xs->sc_link->lun;
+ scsiq.q1.sense_addr = (u_int32_t)vtophys(&xs->sense);
+ scsiq.q1.sense_len = sizeof(xs->sense);
+ scsiq.q1.data_cnt = 0;
+ scsiq.q1.data_addr = 0;
+ scsiq.q1.user_def = 0;
+ scsiq.q2.xs_ptr = (u_int32_t)xs;
+ scsiq.q2.target_ix = ADV_TIDLUN_TO_IX(xs->sc_link->target, xs->sc_link->lun);
+ scsiq.q2.flag = 0;
+ scsiq.q2.cdb_len = xs->cmdlen;
+ scsiq.q2.tag_code = xs->tag_type;
+ scsiq.q2.vm_id = 0;
+ scsiq.sg_head = NULL;
+ scsiq.cdbptr = &xs->cmdstore;
+
+ if (xs->datalen) {
+ /*
+ * Determin the number of segments needed for this
+ * transfer. We should only use SG if we need more
+ * than one.
+ */
+ int seg;
+ u_int32_t datalen;
+ vm_offset_t vaddr;
+ u_int32_t paddr;
+ u_int32_t nextpaddr;
+ struct adv_sg_entry *sg;
+
+ seg = 0;
+ datalen = xs->datalen;
+ vaddr = (vm_offset_t)xs->data;
+ paddr = vtophys(vaddr);
+ sg = &sghead.sg_list[0];
+
+ while ((datalen > 0) && (seg < ADV_MAX_SG_LIST)) {
+ /* put in the base address and length */
+ sg->addr = paddr;
+ sg->bytes = 0;
+
+ /* do it at least once */
+ nextpaddr = paddr;
+
+ while ((datalen > 0) && (paddr == nextpaddr)) {
+ u_int32_t size;
+ /*
+ * This page is contiguous (physically)
+ * with the the last, just extend the
+ * length
+ */
+ /* how far to the end of the page */
+ nextpaddr = (paddr & (~PAGE_MASK)) + PAGE_SIZE;
+
+ /*
+ * Compute the maximum size
+ */
+ size = nextpaddr - paddr;
+ if (size > datalen)
+ size = datalen;
+
+ sg->bytes += size;
+ vaddr += size;
+ datalen -= size;
+ if (datalen > 0)
+ paddr = vtophys(vaddr);
+ }
+ /*
+ * next page isn't contiguous, finish the seg
+ */
+ seg++;
+ sg++;
+ }
+ if (seg > 1) {
+ scsiq.q1.cntl |= QC_SG_HEAD;
+ scsiq.sg_head = &sghead;
+ sghead.entry_cnt = sghead.entry_to_copy = seg;
+ sghead.res = 0;
+ }
+ scsiq.q1.data_addr = sghead.sg_list[0].addr;
+ scsiq.q1.data_cnt = sghead.sg_list[0].bytes;
+ }
+
+ if (adv_execute_scsi_queue(adv, &scsiq) != 0) {
+ /* XXX Add freeze queue functionality */
+ xs->error = XS_BUSY;
+ scsi_done(xs);
+ DELAY(20 * 1000 * 1000);
+ } else if ((xs->flags & SCSI_POLL) != 0) {
+ /*
+ * If we can't use interrupts, poll for completion
+ */
+ int s;
+
+ s = splbio();
+ if (adv_poll(adv, xs)) {
+ if (!(xs->flags & SCSI_SILENT))
+ printf("cmd fail\n");
+ adv_timeout(xs);
+ }
+ splx(s);
+ }
+}
+
+
+static void
+advminphys(bp)
+ struct buf *bp;
+{
+ if (bp->b_bcount > ((ADV_MAX_SG_LIST - 1) * PAGE_SIZE))
+ bp->b_bcount = ((ADV_MAX_SG_LIST - 1) * PAGE_SIZE);
+}
+
+static void *
+adv_get_cdb(adapter_softc, sq)
+ void *adapter_softc;
+ struct scsi_queue *sq;
+{
+ return ((void *)1);
+}
+
+static void
+adv_free_cdb(adapter_softc, sq, cdb)
+ void *adapter_softc;
+ struct scsi_queue *sq;
+ void *cdb;
+{
+}
+
+static void
+adv_timeout(arg)
+ void *arg;
+{
+ printf("adv: Ooops. Had a timeout\n");
+}
+
+struct adv_softc *
+adv_alloc(unit, iobase)
+ int unit;
+ u_long iobase;
+{
+ struct adv_softc *adv;
+ int i;
+
+ if (unit >= NADV) {
+ printf("adv: unit number (%d) too high\n", unit);
+ return NULL;
+ }
+
+ /*
+ * Allocate a storage area for us
+ */
+ if (advsoftcs[unit]) {
+ printf("adv%d: memory already allocated\n", unit);
+ return NULL;
+ }
+
+ adv = malloc(sizeof(struct adv_softc), M_DEVBUF, M_NOWAIT);
+ if (!adv) {
+ printf("adv%d: cannot malloc!\n", unit);
+ return NULL;
+ }
+ bzero(adv, sizeof(struct adv_softc));
+ advsoftcs[unit] = adv;
+ adv->unit = unit;
+ adv->iobase = iobase;
+
+ /* Set reasonable defaults incase we can't read the EEPROM */
+ adv->max_openings = ADV_DEF_MAX_TOTAL_QNG;
+ adv->start_motor = TARGET_BIT_VECTOR_SET;
+ adv->disc_enable = TARGET_BIT_VECTOR_SET;
+ adv->cmd_qng_enabled = TARGET_BIT_VECTOR_SET;
+ adv->scsi_id = 7;
+
+ for (i = 0; i <= ADV_MAX_TID; i++)
+ adv->sdtr_data[i] = ADV_DEF_SDTR_OFFSET | (ADV_DEF_SDTR_INDEX << 4);
+
+ return(adv);
+}
+
+void
+adv_free(adv)
+ struct adv_softc *adv;
+{
+ if (adv->sense_buffers != NULL)
+ free(adv->sense_buffers, M_DEVBUF);
+ free(adv, M_DEVBUF);
+}
+
+int
+adv_init(adv)
+ struct adv_softc *adv;
+{
+ struct adv_eeprom_config eeprom_config;
+ int checksum, i;
+ u_int16_t config_lsw;
+ u_int16_t config_msw;
+
+ adv_get_board_type(adv);
+
+ /*
+ * Stop script execution.
+ */
+ adv_write_lram_16(adv, ADV_HALTCODE_W, 0x00FE);
+ adv_stop_execution(adv);
+ adv_reset_chip_and_scsi_bus(adv);
+ /*
+ * The generic SCSI code does a minimum delay for us
+ * already.
+ */
+ /* DELAY(3 * 1000 * 1000);*/ /* 3 Second Delay */
+ if (adv_is_chip_halted(adv) == 0) {
+ printf("adv%d: Unable to halt adapter. Initialization"
+ "failed\n", adv->unit);
+ return (1);
+ }
+ ADV_OUTW(adv, ADV_REG_PROG_COUNTER, ADV_MCODE_START_ADDR);
+ if (ADV_INW(adv, ADV_REG_PROG_COUNTER) != ADV_MCODE_START_ADDR) {
+ printf("adv%d: Unable to set program counter. Initialization"
+ "failed\n", adv->unit);
+ return (1);
+ }
+
+ config_lsw = ADV_INW(adv, ADV_CONFIG_LSW);
+ config_msw = ADV_INW(adv, ADV_CONFIG_MSW);
+
+#if 0
+ /* XXX Move to PCI probe code */
+ if (adv->type & ADV_PCI) {
+#if CC_DISABLE_PCI_PARITY_INT
+ config_msw &= 0xFFC0;
+ ADV_OUTW(adv, ADV_CONFIG_MSW, config_msw);
+#endif
+
+ if (asc_dvc->cfg->pci_device_id == ASC_PCI_DEVICE_ID_REV_A) {
+ asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ADD_ONE_BYTE;
+ }
+ }
+#endif
+ if ((config_msw & ADV_CFG_MSW_CLR_MASK) != 0) {
+ config_msw &= (~(ADV_CFG_MSW_CLR_MASK));
+ /*
+ * XXX The Linux code flags this as an error,
+ * but what should we report to the user???
+ * It seems that clearing the config register
+ * makes this error recoverable.
+ */
+ ADV_OUTW(adv, ADV_CONFIG_MSW, config_msw);
+ }
+
+ /* Suck in the configuration from the EEProm */
+ checksum = adv_get_eeprom_config(adv, &eeprom_config);
+
+ eeprom_config.cfg_msw &= (~(ADV_CFG_MSW_CLR_MASK));
+
+ if (ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_AUTO_CONFIG) {
+ /*
+ * XXX The Linux code sets a warning level for this
+ * condition, yet nothing of meaning is printed to
+ * the user. What does this mean???
+ */
+ if (adv->chip_version == 3) {
+ if (eeprom_config.cfg_lsw != config_lsw) {
+ /* XXX Yet another supposed Warning */
+ eeprom_config.cfg_lsw =
+ ADV_INW(adv, ADV_CONFIG_LSW);
+ }
+ if (eeprom_config.cfg_msw != config_msw) {
+ /* XXX Yet another supposed Warning */
+ eeprom_config.cfg_msw =
+ ADV_INW(adv, ADV_CONFIG_MSW);
+ }
+ }
+ }
+ eeprom_config.cfg_lsw |= ADV_CFG_LSW_HOST_INT_ON;
+ if (checksum == eeprom_config.chksum) {
+ if (adv_test_external_lram(adv) == 0) {
+ if (adv->type & ADV_PCI) {
+ eeprom_config.cfg_msw |= 0x0800;
+ config_msw |= 0x0800;
+ ADV_OUTW(adv, ADV_CONFIG_MSW, config_msw);
+ eeprom_config.max_total_qng = ADV_MAX_PCI_INRAM_TOTAL_QNG;
+ eeprom_config.max_tag_qng = ADV_MAX_INRAM_TAG_QNG;
+ }
+ }
+ /* XXX What about wide bussed cards?? */
+ for (i = 0; i <= 7; i++)
+ adv->sdtr_data[i] = eeprom_config.sdtr_data[i];
+
+ /* Range/Sanity checking */
+ if (eeprom_config.max_total_qng < ADV_MIN_TOTAL_QNG) {
+ eeprom_config.max_total_qng = ADV_MIN_TOTAL_QNG;
+ }
+ if (eeprom_config.max_total_qng > ADV_MAX_TOTAL_QNG) {
+ eeprom_config.max_total_qng = ADV_MAX_TOTAL_QNG;
+ }
+ if (eeprom_config.max_tag_qng > eeprom_config.max_total_qng) {
+ eeprom_config.max_tag_qng = eeprom_config.max_total_qng;
+ }
+ if (eeprom_config.max_tag_qng < ADV_MIN_TAG_Q_PER_DVC) {
+ eeprom_config.max_tag_qng = ADV_MIN_TAG_Q_PER_DVC;
+ }
+ adv->max_openings = eeprom_config.max_total_qng;
+
+ if ((eeprom_config.use_cmd_qng & eeprom_config.disc_enable) !=
+ eeprom_config.use_cmd_qng) {
+ eeprom_config.disc_enable |= eeprom_config.use_cmd_qng;
+ printf("adv:%d: WARNING! One or more targets with tagged "
+ "queuing enabled have the disconnection priveledge "
+ "disabled.\n"
+ "adv:%d: Overriding disconnection settings to "
+ "allow tagged queueing devices to disconnect.\n ",
+ adv->unit, adv->unit);
+ }
+#if 0
+ /*
+ * XXX We should range check our target ID
+ * based on the width of our bus
+ */
+ EEPROM_SET_SCSIID(eeprom_config,
+ EEPROM_SCSIID(eeprom_config) & ADV_MAX_TID);
+#endif
+ adv->initiate_sdtr = eeprom_config.init_sdtr;
+ adv->disc_enable = eeprom_config.disc_enable;
+ adv->cmd_qng_enabled = eeprom_config.use_cmd_qng;
+ adv->isa_dma_speed = EEPROM_DMA_SPEED(eeprom_config);
+ adv->scsi_id = EEPROM_SCSIID(eeprom_config);
+ adv->start_motor = eeprom_config.start_motor;
+ adv->control = eeprom_config.cntl;
+ adv->no_scam = eeprom_config.no_scam;
+ } else {
+ /*
+ * Use the defaults that adv was initialized with.
+ */
+ /*
+ * XXX Fixup EEPROM with default values???
+ */
+ printf("adv%d: Warning EEPROM Checksum mismatch. "
+ "Using default device parameters\n", adv->unit);
+ }
+
+#if 0
+ /* XXX Do this in the PCI probe */
+ if ((adv->btype & ADV_PCI) &&
+ !(asc_dvc->dvc_cntl & ASC_CNTL_NO_PCI_FIX_ASYN_XFER)) {
+ if ((asc_dvc->cfg->pci_device_id == ASC_PCI_DEVICE_ID_REV_A) ||
+ (asc_dvc->cfg->pci_device_id == ASC_PCI_DEVICE_ID_REV_B)) {
+ asc_dvc->pci_fix_asyn_xfer = ASC_ALL_DEVICE_BIT_SET;
+ }
+ }
+#endif
+ if (adv_set_eeprom_config(adv, &eeprom_config) != 0)
+ printf("adv:%d: WARNING! Failure writing to EEPROM.\n");
+
+ /* Allocate space for our sense buffers */
+ /* XXX this should really be done by the generic SCSI layer by ensuring
+ * that all scsi_xfer structs are allocated below 16M if any controller
+ * needs to bounce.
+ */
+ if (adv->type & ADV_ISA) {
+ adv->sense_buffers = (struct scsi_sense_data *)contigmalloc(sizeof(struct scsi_sense_data) * adv->max_openings,
+ M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 1ul,
+ 0x10000ul);
+ if (adv->sense_buffers == NULL) {
+ printf("adv%d: Unable to allocate sense buffer space.\n");
+ return (1);
+ }
+
+ }
+
+ if (adv_init_lram_and_mcode(adv))
+ return (1);
+
+ return (0);
+}
+
+void
+adv_intr(arg)
+ void *arg;
+{
+ struct adv_softc *adv;
+ u_int16_t chipstat;
+ u_int16_t saved_ram_addr;
+ u_int8_t ctrl_reg;
+ u_int8_t saved_ctrl_reg;
+ int status;
+ u_int8_t host_flag;
+
+ adv = (struct adv_softc *)arg;
+
+ ctrl_reg = ADV_INB(adv, ADV_CHIP_CTRL);
+ saved_ctrl_reg = ctrl_reg & (~(ADV_CC_SCSI_RESET | ADV_CC_CHIP_RESET |
+ ADV_CC_SINGLE_STEP | ADV_CC_DIAG | ADV_CC_TEST));
+
+
+ if ((chipstat = ADV_INW(adv, ADV_CHIP_STATUS)) & ADV_CSW_INT_PENDING) {
+
+ adv_ack_interrupt(adv);
+
+ host_flag = adv_read_lram_8(adv, ADVV_HOST_FLAG_B);
+ adv_write_lram_8(adv, ADVV_HOST_FLAG_B,
+ host_flag | ADV_HOST_FLAG_IN_ISR);
+ saved_ram_addr = ADV_INW(adv, ADV_LRAM_ADDR);
+
+ if ((chipstat & ADV_CSW_HALTED)
+ && (ctrl_reg & ADV_CC_SINGLE_STEP)) {
+ adv_isr_chip_halted(adv);
+ saved_ctrl_reg &= ~ADV_CC_HALT;
+ } else {
+ if ((adv->control & ADV_CNTL_INT_MULTI_Q) != 0) {
+ while (((status = adv_qdone(adv)) & 0x01) != 0)
+ ;
+ } else {
+ do {
+ status = adv_qdone(adv);
+ } while (status == 0x11);
+ }
+ }
+ ADV_OUTW(adv, ADV_LRAM_ADDR, saved_ram_addr);
+#ifdef DIAGNOSTIC
+ if (ADV_INW(adv, ADV_LRAM_ADDR) != saved_ram_addr)
+ panic("adv_intr: Unable to set LRAM addr");
+#endif
+ adv_write_lram_8(adv, ADVV_HOST_FLAG_B, host_flag);
+ }
+
+ ADV_OUTB(adv, ADV_CHIP_CTRL, saved_ctrl_reg);
+}
+
+int
+adv_qdone(adv)
+ struct adv_softc *adv;
+{
+ u_int8_t next_qp;
+ u_int8_t i;
+ u_int8_t n_q_used;
+ u_int8_t sg_list_qp;
+ u_int8_t sg_queue_cnt;
+ u_int8_t done_q_tail;
+ u_int8_t tid_no;
+ target_bit_vector target_id;
+ u_int16_t q_addr;
+ u_int16_t sg_q_addr;
+ struct adv_q_done_info scsiq_buf;
+ struct adv_q_done_info *scsiq;
+ int false_overrun;
+ u_int8_t tag_code;
+
+ n_q_used = 1;
+ scsiq = &scsiq_buf;
+ done_q_tail = adv_read_lram_16(adv, ADVV_DONE_Q_TAIL_W) & 0xFF;
+ q_addr = ADV_QNO_TO_QADDR(done_q_tail);
+ next_qp = adv_read_lram_8(adv, q_addr + ADV_SCSIQ_B_FWD);
+ if (next_qp != ADV_QLINK_END) {
+ adv_write_lram_16(adv, ADVV_DONE_Q_TAIL_W, next_qp);
+ q_addr = ADV_QNO_TO_QADDR(next_qp);
+
+ sg_queue_cnt = adv_copy_lram_doneq(adv, q_addr, scsiq, adv->max_dma_count);
+
+ adv_write_lram_8(adv, q_addr + ADV_SCSIQ_B_STATUS,
+ scsiq->q_status & ~(QS_READY | QS_ABORTED));
+ tid_no = ADV_TIX_TO_TID(scsiq->d2.target_ix);
+ target_id = ADV_TIX_TO_TARGET_ID(scsiq->d2.target_ix);
+ if ((scsiq->cntl & QC_SG_HEAD) != 0) {
+ sg_q_addr = q_addr;
+ sg_list_qp = next_qp;
+ for (i = 0; i < sg_queue_cnt; i++) {
+ sg_list_qp = adv_read_lram_8(adv,
+ sg_q_addr + ADV_SCSIQ_B_FWD);
+ sg_q_addr = ADV_QNO_TO_QADDR(sg_list_qp);
+#ifdef DIAGNOSTIC
+ if (sg_list_qp == ASC_QLINK_END) {
+ panic("adv_qdone: Corrupted SG list encountered");
+ }
+#endif
+ adv_write_lram_8(adv, sg_q_addr + ADV_SCSIQ_B_STATUS,
+ QS_FREE);
+ }
+
+ n_q_used = sg_queue_cnt + 1;
+ adv_write_lram_16(adv, ADVV_DONE_Q_TAIL_W, sg_list_qp);
+ }
+#if 0
+ /* XXX Fix later */
+ if (adv->queue_full_or_busy & target_id) {
+ cur_target_qng = adv_read_lram_8(adv,
+ ADV_QADR_BEG + scsiq->d2.target_ix);
+ if (cur_target_qng < adv->max_dvc_qng[tid_no]) {
+ scsi_busy = adv_read_lram_8(adv, ADVV_SCSIBUSY_B);
+ scsi_busy &= ~target_id;
+ adv_write_lram_8(adv, ADVV_SCSIBUSY_B, scsi_busy);
+ adv->queue_full_or_busy &= ~target_id;
+ }
+ }
+#endif
+#ifdef DIAGNOSTIC
+ if (adv->cur_total_qng < n_q_used)
+ panic("adv_qdone: Attempting to free more queues than are active");
+#endif
+ adv->cur_active -= n_q_used;
+
+ if ((scsiq->d2.xs_ptr == 0) ||
+ ((scsiq->q_status & QS_ABORTED) != 0))
+ return (0x11);
+ else if (scsiq->q_status == QS_DONE) {
+
+ false_overrun = FALSE;
+
+ if (adv->bug_fix_control & ADV_BUG_FIX_ADD_ONE_BYTE) {
+ tag_code = adv_read_lram_8(adv, q_addr + ADV_SCSIQ_B_TAG_CODE);
+ if (tag_code & ADV_TAG_FLAG_ADD_ONE_BYTE) {
+ if (scsiq->remain_bytes != 0) {
+ scsiq->remain_bytes--;
+ if (scsiq->remain_bytes == 0)
+ false_overrun = TRUE;
+ }
+ }
+ }
+ if ((scsiq->d3.done_stat == QD_WITH_ERROR) &&
+ (scsiq->d3.host_stat == QHSTA_M_DATA_OVER_RUN)) {
+ if ((scsiq->cntl & (QC_DATA_IN | QC_DATA_OUT)) == 0) {
+ scsiq->d3.done_stat = QD_NO_ERROR;
+ scsiq->d3.host_stat = QHSTA_NO_ERROR;
+ } else if (false_overrun) {
+ scsiq->d3.done_stat = QD_NO_ERROR;
+ scsiq->d3.host_stat = QHSTA_NO_ERROR;
+ }
+ }
+
+ if ((scsiq->cntl & QC_NO_CALLBACK) == 0)
+ adv_done(adv, scsiq);
+ else {
+ if ((adv_read_lram_8(adv, q_addr + ADV_SCSIQ_CDB_BEG) ==
+ START_STOP)) {
+ adv->unit_not_ready &= ~target_id;
+ if (scsiq->d3.done_stat != QD_NO_ERROR)
+ adv->start_motor &= ~target_id;
+ }
+ }
+ return (1);
+ } else {
+ panic("adv_qdone: completed scsiq with unknown status");
+#if 0
+ /*
+ * XXX Doesn't this simply indicate a software bug?
+ * What does setting the lram error code do for
+ * you. Would we even recover?
+ */
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_Q_STATUS);
+
+ FATAL_ERR_QDONE:
+ if ((scsiq->cntl & QC_NO_CALLBACK) == 0) {
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ }
+ return (0x80);
+#endif
+ }
+ }
+ return (0);
+}
+
+
+void
+adv_done(adv, qdonep)
+ struct adv_softc *adv;
+ struct adv_q_done_info *qdonep;
+{
+ struct scsi_xfer *xs;
+
+ xs = (struct scsi_xfer *)qdonep->d2.xs_ptr;
+
+ xs->status = qdonep->d3.scsi_stat;
+ /*
+ * 'qdonep' contains the command's ending status.
+ */
+ switch (qdonep->d3.done_stat) {
+ case QD_NO_ERROR:
+ switch (qdonep->d3.host_stat) {
+ case QHSTA_NO_ERROR:
+ break;
+ case QHSTA_M_SEL_TIMEOUT:
+ xs->error = XS_SELTIMEOUT;
+ break;
+ default:
+ /* QHSTA error occurred */
+ /* XXX Can I get more explicit information here? */
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
+ break;
+
+ case QD_WITH_ERROR:
+ switch (qdonep->d3.host_stat) {
+ case QHSTA_NO_ERROR:
+ if (qdonep->d3.scsi_stat == SCSI_CHECK) {
+ /* We have valid sense information to return */
+ xs->error = XS_SENSE;
+ if (adv->sense_buffers != NULL)
+ /* Structure copy */
+ xs->sense = adv->sense_buffers[qdonep->q_no];
+ }
+ break;
+ case QHSTA_M_SEL_TIMEOUT:
+ xs->error = XS_SELTIMEOUT;
+ break;
+ default:
+ /* XXX Can I get more explicit information here? */
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
+ break;
+
+ case QD_ABORTED_BY_HOST:
+ /* XXX Should have an explicit ABORTED error code */
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+
+ default:
+ printf("adv_done: Unknown done status 0x%x\n", qdonep->d3.done_stat);
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ return;
+}
+
+/*
+ * Function to poll for command completion when
+ * interrupts are disabled (crash dumps)
+ */
+static int
+adv_poll(adv, xs)
+ struct adv_softc *adv;
+ struct scsi_xfer *xs;
+{
+ int wait;
+
+ wait = xs->timeout;
+ do {
+ DELAY(1000);
+ adv_intr((void *)adv);
+ } while (--wait && ((xs->flags & ITSDONE) == 0));
+ if (wait == 0) {
+ printf("adv%d: board is not responding\n", adv->unit);
+ return (EIO);
+ }
+ return (0);
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+adv_attach(adv)
+ struct adv_softc *adv;
+{
+ struct scsi_bus *scbus;
+ struct scsi_queue *scsiq;
+
+ scsiq = scsi_alloc_queue(adv->max_openings);
+ if (scsiq == NULL)
+ return 0;
+
+ /*
+ * Prepare the scsi_bus area for the upperlevel scsi code.
+ */
+ scbus = scsi_alloc_bus(&adv_switch, adv, adv->unit, scsiq);
+ if (scbus == NULL) {
+ scsi_free_queue(scsiq);
+ return 0;
+ }
+
+ /* Override defaults */
+ if ((adv->type & ADV_ISA) != 0)
+ scbus->adpt_link.adpt_flags |= SADPT_BOUNCE;
+ scbus->adpt_link.adpt_target = adv->scsi_id;
+ scbus->adpt_link.adpt_openings = 2; /* XXX Is this correct for these cards? */
+ scbus->adpt_link.adpt_tagged_openings = adv->max_openings;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ if(bootverbose)
+ printf("adv%d: Probing SCSI bus\n", adv->unit);
+
+ scsi_attachdevs(scbus);
+
+ return 1;
+}
diff --git a/sys/i386/scsi/advansys.h b/sys/i386/scsi/advansys.h
new file mode 100644
index 000000000000..47bca6aad6ed
--- /dev/null
+++ b/sys/i386/scsi/advansys.h
@@ -0,0 +1,50 @@
+/*
+ * Generic driver for the Advanced Systems Inc. SCSI controllers
+ * Product specific probe and attach routines can be found in:
+ *
+ * XXX Fill this in.
+ *
+ * Copyright (c) 1996 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 immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef _ADVANSYS_H_
+#define _ADVANSYS_H_
+
+#include "adv.h"
+#include <dev/advansys/advlib.h>
+
+struct adv_softc * adv_alloc __P((int unit, u_long iobase));
+void adv_free __P((struct adv_softc *adv));
+int adv_init __P((struct adv_softc *adv));
+void adv_intr __P((void *arg));
+int adv_attach __P((struct adv_softc *adv));
+
+extern struct adv_softc *advsoftcs[NADV]; /* XXX Config should handle this */
+#endif /* _ADVANSYS_H_ */