diff --git a/sys/dev/advansys/adv_isa.c b/sys/dev/advansys/adv_isa.c new file mode 100644 index 00000000000..e7c89151f76 --- /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 +#include + +#include +#include + +#include + +#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 00000000000..365f3432c1b --- /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 +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* + * 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 00000000000..9e18137c336 --- /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 00000000000..37fd53ce80b --- /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 + +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 00000000000..c5688e579cf --- /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 00000000000..e7c89151f76 --- /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 +#include + +#include +#include + +#include + +#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 00000000000..27ff1d71a93 --- /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 +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +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 00000000000..47bca6aad6e --- /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 + +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_ */