diff options
Diffstat (limited to 'sys/dev/advansys/advlib.c')
-rw-r--r-- | sys/dev/advansys/advlib.c | 2064 |
1 files changed, 2064 insertions, 0 deletions
diff --git a/sys/dev/advansys/advlib.c b/sys/dev/advansys/advlib.c new file mode 100644 index 0000000..7d3ec52 --- /dev/null +++ b/sys/dev/advansys/advlib.c @@ -0,0 +1,2064 @@ +/* + * Low level routines for the Advanced Systems Inc. SCSI controllers chips + * + * Copyright (c) 1996-1997, 1999-2000 Justin Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 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. + * + * $FreeBSD$ + */ +/* + * Ported from: + * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters + * + * Copyright (c) 1995-1996 Advanced System Products, Inc. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that redistributions of source + * code retain the above copyright notice and this comment without + * modification. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> + +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> + +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> +#include <cam/scsi/scsi_da.h> +#include <cam/scsi/scsi_cd.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> + +#include <dev/advansys/advansys.h> +#include <dev/advansys/advmcode.h> + +struct adv_quirk_entry { + struct scsi_inquiry_pattern inq_pat; + u_int8_t quirks; +#define ADV_QUIRK_FIX_ASYN_XFER_ALWAYS 0x01 +#define ADV_QUIRK_FIX_ASYN_XFER 0x02 +}; + +static struct adv_quirk_entry adv_quirk_table[] = +{ + { + { T_CDROM, SIP_MEDIA_REMOVABLE, "HP", "*", "*" }, + ADV_QUIRK_FIX_ASYN_XFER_ALWAYS|ADV_QUIRK_FIX_ASYN_XFER + }, + { + { T_CDROM, SIP_MEDIA_REMOVABLE, "NEC", "CD-ROM DRIVE", "*" }, + 0 + }, + { + { + T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, + "TANDBERG", " TDC 36", "*" + }, + 0 + }, + { + { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK", "*", "*" }, + 0 + }, + { + { + T_PROCESSOR, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, + "*", "*", "*" + }, + 0 + }, + { + { + T_SCANNER, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, + "*", "*", "*" + }, + 0 + }, + { + /* Default quirk entry */ + { + T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, + /*vendor*/"*", /*product*/"*", /*revision*/"*" + }, + ADV_QUIRK_FIX_ASYN_XFER, + } +}; + +/* + * Allowable periods in ns + */ +static u_int8_t adv_sdtr_period_tbl[] = +{ + 25, + 30, + 35, + 40, + 50, + 60, + 70, + 85 +}; + +static u_int8_t adv_sdtr_period_tbl_ultra[] = +{ + 12, + 19, + 25, + 32, + 38, + 44, + 50, + 57, + 63, + 69, + 75, + 82, + 88, + 94, + 100, + 107 +}; + +struct ext_msg { + u_int8_t msg_type; + u_int8_t msg_len; + u_int8_t msg_req; + union { + struct { + u_int8_t sdtr_xfer_period; + u_int8_t sdtr_req_ack_offset; + } sdtr; + struct { + u_int8_t wdtr_width; + } wdtr; + struct { + u_int8_t mdp[4]; + } mdp; + } u_ext_msg; + u_int8_t res; +}; + +#define xfer_period u_ext_msg.sdtr.sdtr_xfer_period +#define req_ack_offset u_ext_msg.sdtr.sdtr_req_ack_offset +#define wdtr_width u_ext_msg.wdtr.wdtr_width +#define mdp_b3 u_ext_msg.mdp_b3 +#define mdp_b2 u_ext_msg.mdp_b2 +#define mdp_b1 u_ext_msg.mdp_b1 +#define mdp_b0 u_ext_msg.mdp_b0 + +/* + * Some of the early PCI adapters have problems with + * async transfers. Instead use an offset of 1. + */ +#define ASYN_SDTR_DATA_FIX_PCI_REV_AB 0x41 + +/* LRAM routines */ +static void adv_read_lram_16_multi(struct adv_softc *adv, u_int16_t s_addr, + u_int16_t *buffer, int count); +static void adv_write_lram_16_multi(struct adv_softc *adv, + u_int16_t s_addr, u_int16_t *buffer, + int count); +static void adv_mset_lram_16(struct adv_softc *adv, u_int16_t s_addr, + u_int16_t set_value, int count); +static u_int32_t adv_msum_lram_16(struct adv_softc *adv, u_int16_t s_addr, + int count); + +static int adv_write_and_verify_lram_16(struct adv_softc *adv, + u_int16_t addr, u_int16_t value); +static u_int32_t adv_read_lram_32(struct adv_softc *adv, u_int16_t addr); + + +static void adv_write_lram_32(struct adv_softc *adv, u_int16_t addr, + u_int32_t value); +static void adv_write_lram_32_multi(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(struct adv_softc *adv, u_int8_t addr); +static u_int16_t adv_write_eeprom_16(struct adv_softc *adv, u_int8_t addr, + u_int16_t value); +static int adv_write_eeprom_cmd_reg(struct adv_softc *adv, + u_int8_t cmd_reg); +static int adv_set_eeprom_config_once(struct adv_softc *adv, + struct adv_eeprom_config *eeconfig); + +/* Initialization */ +static u_int32_t adv_load_microcode(struct adv_softc *adv, u_int16_t s_addr, + u_int16_t *mcode_buf, u_int16_t mcode_size); + +static void adv_reinit_lram(struct adv_softc *adv); +static void adv_init_lram(struct adv_softc *adv); +static int adv_init_microcode_var(struct adv_softc *adv); +static void adv_init_qlink_var(struct adv_softc *adv); + +/* Interrupts */ +static void adv_disable_interrupt(struct adv_softc *adv); +static void adv_enable_interrupt(struct adv_softc *adv); +static void adv_toggle_irq_act(struct adv_softc *adv); + +/* Chip Control */ +static int adv_host_req_chip_halt(struct adv_softc *adv); +static void adv_set_chip_ih(struct adv_softc *adv, u_int16_t ins_code); +#if UNUSED +static u_int8_t adv_get_chip_scsi_ctrl(struct adv_softc *adv); +#endif + +/* Queue handling and execution */ +static __inline int + adv_sgcount_to_qcount(int sgcount); + +static __inline int +adv_sgcount_to_qcount(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); +} + +static void adv_get_q_info(struct adv_softc *adv, u_int16_t s_addr, + u_int16_t *inbuf, int words); +static u_int adv_get_num_free_queues(struct adv_softc *adv, u_int8_t n_qs); +static u_int8_t adv_alloc_free_queues(struct adv_softc *adv, + u_int8_t free_q_head, u_int8_t n_free_q); +static u_int8_t adv_alloc_free_queue(struct adv_softc *adv, + u_int8_t free_q_head); +static int adv_send_scsi_queue(struct adv_softc *adv, + struct adv_scsi_q *scsiq, + u_int8_t n_q_required); +static void adv_put_ready_sg_list_queue(struct adv_softc *adv, + struct adv_scsi_q *scsiq, + u_int q_no); +static void adv_put_ready_queue(struct adv_softc *adv, + struct adv_scsi_q *scsiq, u_int q_no); +static void adv_put_scsiq(struct adv_softc *adv, u_int16_t s_addr, + u_int16_t *buffer, int words); + +/* Messages */ +static void adv_handle_extmsg_in(struct adv_softc *adv, + u_int16_t halt_q_addr, u_int8_t q_cntl, + target_bit_vector target_id, + int tid); +static void adv_msgout_sdtr(struct adv_softc *adv, u_int8_t sdtr_period, + u_int8_t sdtr_offset); +static void adv_set_sdtr_reg_at_id(struct adv_softc *adv, int id, + u_int8_t sdtr_data); + + +/* Exported functions first */ + +void +advasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) +{ + struct adv_softc *adv; + + adv = (struct adv_softc *)callback_arg; + switch (code) { + case AC_FOUND_DEVICE: + { + struct ccb_getdev *cgd; + target_bit_vector target_mask; + int num_entries; + caddr_t match; + struct adv_quirk_entry *entry; + struct adv_target_transinfo* tinfo; + + cgd = (struct ccb_getdev *)arg; + + target_mask = ADV_TID_TO_TARGET_MASK(cgd->ccb_h.target_id); + + num_entries = sizeof(adv_quirk_table)/sizeof(*adv_quirk_table); + match = cam_quirkmatch((caddr_t)&cgd->inq_data, + (caddr_t)adv_quirk_table, + num_entries, sizeof(*adv_quirk_table), + scsi_inquiry_match); + + if (match == NULL) + panic("advasync: device didn't match wildcard entry!!"); + + entry = (struct adv_quirk_entry *)match; + + if (adv->bug_fix_control & ADV_BUG_FIX_ASYN_USE_SYN) { + if ((entry->quirks & ADV_QUIRK_FIX_ASYN_XFER_ALWAYS)!=0) + adv->fix_asyn_xfer_always |= target_mask; + else + adv->fix_asyn_xfer_always &= ~target_mask; + /* + * We start out life with all bits set and clear them + * after we've determined that the fix isn't necessary. + * It may well be that we've already cleared a target + * before the full inquiry session completes, so don't + * gratuitously set a target bit even if it has this + * quirk. But, if the quirk exonerates a device, clear + * the bit now. + */ + if ((entry->quirks & ADV_QUIRK_FIX_ASYN_XFER) == 0) + adv->fix_asyn_xfer &= ~target_mask; + } + /* + * Reset our sync settings now that we've determined + * what quirks are in effect for the device. + */ + tinfo = &adv->tinfo[cgd->ccb_h.target_id]; + adv_set_syncrate(adv, cgd->ccb_h.path, + cgd->ccb_h.target_id, + tinfo->current.period, + tinfo->current.offset, + ADV_TRANS_CUR); + break; + } + case AC_LOST_DEVICE: + { + u_int target_mask; + + if (adv->bug_fix_control & ADV_BUG_FIX_ASYN_USE_SYN) { + target_mask = 0x01 << xpt_path_target_id(path); + adv->fix_asyn_xfer |= target_mask; + } + + /* + * Revert to async transfers + * for the next device. + */ + adv_set_syncrate(adv, /*path*/NULL, + xpt_path_target_id(path), + /*period*/0, + /*offset*/0, + ADV_TRANS_GOAL|ADV_TRANS_CUR); + } + default: + break; + } +} + +void +adv_set_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); +} + +u_int8_t +adv_read_lram_8(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(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(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(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); +} + +/* + * Determine if there is a board at "iobase" by looking + * for the AdvanSys signatures. Return 1 if a board is + * found, 0 otherwise. + */ +int +adv_find_signature(bus_space_tag_t tag, bus_space_handle_t bsh) +{ + u_int16_t signature; + + if (bus_space_read_1(tag, bsh, ADV_SIGNATURE_BYTE) == ADV_1000_ID1B) { + signature = bus_space_read_2(tag, bsh, ADV_SIGNATURE_WORD); + if ((signature == ADV_1000_ID0W) + || (signature == ADV_1000_ID0W_FIX)) + return (1); + } + return (0); +} + +void +adv_lib_init(struct adv_softc *adv) +{ + if ((adv->type & ADV_ULTRA) != 0) { + adv->sdtr_period_tbl = adv_sdtr_period_tbl_ultra; + adv->sdtr_period_tbl_size = sizeof(adv_sdtr_period_tbl_ultra); + } else { + adv->sdtr_period_tbl = adv_sdtr_period_tbl; + adv->sdtr_period_tbl_size = sizeof(adv_sdtr_period_tbl); + } +} + +u_int16_t +adv_get_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(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(struct adv_softc *adv, int reset_bus) +{ + adv_stop_chip(adv); + ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_CHIP_RESET | ADV_CC_HALT + | (reset_bus ? ADV_CC_SCSI_RESET : 0)); + DELAY(60); + + adv_set_chip_ih(adv, ADV_INS_RFLAG_WTM); + adv_set_chip_ih(adv, ADV_INS_HALT); + + if (reset_bus) + ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_CHIP_RESET | ADV_CC_HALT); + + ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_HALT); + if (reset_bus) + DELAY(200 * 1000); + + ADV_OUTW(adv, ADV_CHIP_STATUS, ADV_CIW_CLR_SCSI_RESET_INT); + ADV_OUTW(adv, ADV_CHIP_STATUS, 0); + return (adv_is_chip_halted(adv)); +} + +int +adv_test_external_lram(struct adv_softc* adv) +{ + u_int16_t q_addr; + u_int16_t saved_value; + int success; + + success = 0; + + 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(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(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(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)); +} + +void +adv_set_chip_scsiid(struct adv_softc *adv, int new_id) +{ + u_int16_t cfg_lsw; + + cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW); + if (ADV_CONFIG_SCSIID(cfg_lsw) == new_id) + return; + cfg_lsw &= ~ADV_CFG_LSW_SCSIID; + cfg_lsw |= (new_id & ADV_MAX_TID) << ADV_CFG_LSW_SCSIID_SHIFT; + ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw); +} + +int +adv_execute_scsi_queue(struct adv_softc *adv, struct adv_scsi_q *scsiq, + u_int32_t datalen) +{ + struct adv_target_transinfo* tinfo; + u_int32_t *p_data_addr; + u_int32_t *p_data_bcount; + int disable_syn_offset_one_fix; + int retval; + u_int n_q_required; + 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; + + 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); + tinfo = &adv->tinfo[tid_no]; + + if (scsiq->cdbptr[0] == REQUEST_SENSE) { + /* Renegotiate if appropriate. */ + adv_set_syncrate(adv, /*struct cam_path */NULL, + tid_no, /*period*/0, /*offset*/0, + ADV_TRANS_CUR); + if (tinfo->current.period != tinfo->goal.period) { + adv_msgout_sdtr(adv, tinfo->goal.period, + tinfo->goal.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)) != 0) { + int i; + + 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; + } + + disable_syn_offset_one_fix = FALSE; + + if ((adv->fix_asyn_xfer & scsiq->q1.target_id) != 0 + && (adv->fix_asyn_xfer_always & scsiq->q1.target_id) == 0) { + + if (datalen != 0) { + if (datalen < 512) { + disable_syn_offset_one_fix = TRUE; + } else { + if (scsiq->cdbptr[0] == INQUIRY + || scsiq->cdbptr[0] == REQUEST_SENSE + || scsiq->cdbptr[0] == READ_CAPACITY + || scsiq->cdbptr[0] == MODE_SELECT_6 + || scsiq->cdbptr[0] == MODE_SENSE_6 + || scsiq->cdbptr[0] == MODE_SENSE_10 + || scsiq->cdbptr[0] == MODE_SELECT_10 + || scsiq->cdbptr[0] == READ_TOC) { + disable_syn_offset_one_fix = TRUE; + } + } + } + } + + if (disable_syn_offset_one_fix) { + scsiq->q2.tag_code &= + ~(MSG_SIMPLE_Q_TAG|MSG_HEAD_OF_Q_TAG|MSG_ORDERED_Q_TAG); + scsiq->q2.tag_code |= (ADV_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX + | ADV_TAG_FLAG_DISABLE_DISCONNECT); + } + + if ((adv->bug_fix_control & ADV_BUG_FIX_IF_NOT_DWB) != 0 + && (scsiq->cdbptr[0] == READ_10 || scsiq->cdbptr[0] == READ_6)) { + u_int8_t extra_bytes; + + addr = *p_data_addr + *p_data_bcount; + extra_bytes = addr & 0x0003; + if (extra_bytes != 0 + && ((scsiq->q1.cntl & QC_SG_HEAD) != 0 + || (scsiq->q1.data_cnt & 0x01FF) == 0)) { + scsiq->q2.tag_code |= ADV_TAG_FLAG_EXTRA_BYTES; + scsiq->q1.extra_bytes = extra_bytes; + *p_data_bcount -= extra_bytes; + } + } + + 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); + + return (retval); +} + + +u_int8_t +adv_copy_lram_doneq(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->extra_bytes = (val >> 8) & 0xFF; + + /* + * Due to a bug in accessing LRAM on the 940UA, the residual + * is split into separate high and low 16bit quantities. + */ + scsiq->remain_bytes = + adv_read_lram_16(adv, q_addr + ADV_SCSIQ_DW_REMAIN_XFER_CNT); + scsiq->remain_bytes |= + adv_read_lram_16(adv, q_addr + ADV_SCSIQ_W_ALT_DC1) << 16; + + /* + * 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_start_chip(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); +} + +int +adv_stop_execution(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(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(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(struct adv_softc *adv) +{ + u_int16_t int_halt_code; + u_int16_t halt_q_addr; + target_bit_vector target_mask; + target_bit_vector scsi_busy; + u_int8_t halt_qp; + u_int8_t target_ix; + u_int8_t q_cntl; + u_int8_t tid_no; + + 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_mask = ADV_TID_TO_TARGET_MASK(tid_no); + if (int_halt_code == ADV_HALT_DISABLE_ASYN_USE_SYN_FIX) { + /* + * Temporarily disable the async fix by removing + * this target from the list of affected targets, + * setting our async rate, and then putting us + * back into the mask. + */ + adv->fix_asyn_xfer &= ~target_mask; + adv_set_syncrate(adv, /*struct cam_path */NULL, + tid_no, /*period*/0, /*offset*/0, + ADV_TRANS_ACTIVE); + adv->fix_asyn_xfer |= target_mask; + } else if (int_halt_code == ADV_HALT_ENABLE_ASYN_USE_SYN_FIX) { + adv_set_syncrate(adv, /*struct cam_path */NULL, + tid_no, /*period*/0, /*offset*/0, + ADV_TRANS_ACTIVE); + } else if (int_halt_code == ADV_HALT_EXTMSG_IN) { + adv_handle_extmsg_in(adv, halt_q_addr, q_cntl, + target_mask, tid_no); + } else if (int_halt_code == ADV_HALT_CHK_CONDITION) { + struct adv_target_transinfo* tinfo; + union ccb *ccb; + u_int32_t cinfo_index; + u_int8_t tag_code; + u_int8_t q_status; + + tinfo = &adv->tinfo[tid_no]; + q_cntl |= QC_REQ_SENSE; + + /* Renegotiate if appropriate. */ + adv_set_syncrate(adv, /*struct cam_path */NULL, + tid_no, /*period*/0, /*offset*/0, + ADV_TRANS_CUR); + if (tinfo->current.period != tinfo->goal.period) { + adv_msgout_sdtr(adv, tinfo->goal.period, + tinfo->goal.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); + + if ((adv->fix_asyn_xfer & target_mask) != 0 + && (adv->fix_asyn_xfer_always & target_mask) == 0) { + tag_code |= (ADV_TAG_FLAG_DISABLE_DISCONNECT + | ADV_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX); + } + 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); + /* + * Freeze the devq until we can handle the sense condition. + */ + cinfo_index = + adv_read_lram_32(adv, halt_q_addr + ADV_SCSIQ_D_CINFO_IDX); + ccb = adv->ccb_infos[cinfo_index].ccb; + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status |= CAM_DEV_QFRZN; + adv_abort_ccb(adv, tid_no, ADV_TIX_TO_LUN(target_ix), + /*ccb*/NULL, CAM_REQUEUE_REQ, + /*queued_only*/TRUE); + scsi_busy = adv_read_lram_8(adv, ADVV_SCSIBUSY_B); + scsi_busy &= ~target_mask; + adv_write_lram_8(adv, ADVV_SCSIBUSY_B, scsi_busy); + /* + * Ensure we have enough time to actually + * retrieve the sense. + */ + untimeout(adv_timeout, (caddr_t)ccb, ccb->ccb_h.timeout_ch); + ccb->ccb_h.timeout_ch = + timeout(adv_timeout, (caddr_t)ccb, 5 * hz); + } else if (int_halt_code == ADV_HALT_SDTR_REJECTED) { + struct ext_msg 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)) { + + /* Revert to Async */ + adv_set_syncrate(adv, /*struct cam_path */NULL, + tid_no, /*period*/0, /*offset*/0, + ADV_TRANS_GOAL|ADV_TRANS_ACTIVE); + } + 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 scsi_status; + union ccb *ccb; + u_int32_t cinfo_index; + + scsi_status = adv_read_lram_8(adv, halt_q_addr + + ADV_SCSIQ_SCSI_STATUS); + cinfo_index = + adv_read_lram_32(adv, halt_q_addr + ADV_SCSIQ_D_CINFO_IDX); + ccb = adv->ccb_infos[cinfo_index].ccb; + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status |= CAM_DEV_QFRZN|CAM_SCSI_STATUS_ERROR; + ccb->csio.scsi_status = SCSI_STATUS_QUEUE_FULL; + adv_abort_ccb(adv, tid_no, ADV_TIX_TO_LUN(target_ix), + /*ccb*/NULL, CAM_REQUEUE_REQ, + /*queued_only*/TRUE); + scsi_busy = adv_read_lram_8(adv, ADVV_SCSIBUSY_B); + scsi_busy &= ~target_mask; + adv_write_lram_8(adv, ADVV_SCSIBUSY_B, scsi_busy); + } else { + printf("Unhandled Halt Code %x\n", int_halt_code); + } + adv_write_lram_16(adv, ADVV_HALTCODE_W, 0); +} + +void +adv_sdtr_to_period_offset(struct adv_softc *adv, + u_int8_t sync_data, u_int8_t *period, + u_int8_t *offset, int tid) +{ + if (adv->fix_asyn_xfer & ADV_TID_TO_TARGET_MASK(tid) + && (sync_data == ASYN_SDTR_DATA_FIX_PCI_REV_AB)) { + *period = *offset = 0; + } else { + *period = adv->sdtr_period_tbl[((sync_data >> 4) & 0xF)]; + *offset = sync_data & 0xF; + } +} + +void +adv_set_syncrate(struct adv_softc *adv, struct cam_path *path, + u_int tid, u_int period, u_int offset, u_int type) +{ + struct adv_target_transinfo* tinfo; + u_int old_period; + u_int old_offset; + u_int8_t sdtr_data; + + tinfo = &adv->tinfo[tid]; + + /* Filter our input */ + sdtr_data = adv_period_offset_to_sdtr(adv, &period, + &offset, tid); + + old_period = tinfo->current.period; + old_offset = tinfo->current.offset; + + if ((type & ADV_TRANS_CUR) != 0 + && ((old_period != period || old_offset != offset) + || period == 0 || offset == 0) /*Changes in asyn fix settings*/) { + int s; + int halted; + + s = splcam(); + halted = adv_is_chip_halted(adv); + if (halted == 0) + /* Must halt the chip first */ + adv_host_req_chip_halt(adv); + + /* Update current hardware settings */ + adv_set_sdtr_reg_at_id(adv, tid, sdtr_data); + + /* + * If a target can run in sync mode, we don't need + * to check it for sync problems. + */ + if (offset != 0) + adv->fix_asyn_xfer &= ~ADV_TID_TO_TARGET_MASK(tid); + + if (halted == 0) + /* Start the chip again */ + adv_start_chip(adv); + + splx(s); + tinfo->current.period = period; + tinfo->current.offset = offset; + + if (path != NULL) { + /* + * Tell the SCSI layer about the + * new transfer parameters. + */ + struct ccb_trans_settings neg; + + neg.sync_period = period; + neg.sync_offset = offset; + neg.valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID; + xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); + xpt_async(AC_TRANSFER_NEG, path, &neg); + } + } + + if ((type & ADV_TRANS_GOAL) != 0) { + tinfo->goal.period = period; + tinfo->goal.offset = offset; + } + + if ((type & ADV_TRANS_USER) != 0) { + tinfo->user.period = period; + tinfo->user.offset = offset; + } +} + +u_int8_t +adv_period_offset_to_sdtr(struct adv_softc *adv, u_int *period, + u_int *offset, int tid) +{ + u_int i; + u_int dummy_offset; + u_int dummy_period; + + if (offset == NULL) { + dummy_offset = 0; + offset = &dummy_offset; + } + + if (period == NULL) { + dummy_period = 0; + period = &dummy_period; + } + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + + *offset = MIN(ADV_SYN_MAX_OFFSET, *offset); + if (*period != 0 && *offset != 0) { + for (i = 0; i < adv->sdtr_period_tbl_size; i++) { + if (*period <= adv->sdtr_period_tbl[i]) { + /* + * When responding to a target that requests + * sync, the requested rate may fall between + * two rates that we can output, but still be + * a rate that we can receive. Because of this, + * we want to respond to the target with + * the same rate that it sent to us even + * if the period we use to send data to it + * is lower. Only lower the response period + * if we must. + */ + if (i == 0 /* Our maximum rate */) + *period = adv->sdtr_period_tbl[0]; + return ((i << 4) | *offset); + } + } + } + + /* Must go async */ + *period = 0; + *offset = 0; + if (adv->fix_asyn_xfer & ADV_TID_TO_TARGET_MASK(tid)) + return (ASYN_SDTR_DATA_FIX_PCI_REV_AB); + return (0); +} + +/* Internal Routines */ + +static void +adv_read_lram_16_multi(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(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(struct adv_softc *adv, u_int16_t s_addr, + u_int16_t set_value, int count) +{ + ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr); + bus_space_set_multi_2(adv->tag, adv->bsh, ADV_LRAM_DATA, + set_value, count); +} + +static u_int32_t +adv_msum_lram_16(struct adv_softc *adv, u_int16_t s_addr, int count) +{ + u_int32_t sum; + int i; + + sum = 0; + ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr); + for (i = 0; i < count; i++) + sum += ADV_INW(adv, ADV_LRAM_DATA); + return (sum); +} + +static int +adv_write_and_verify_lram_16(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); + DELAY(10000); + 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(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(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(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, (u_int16_t *)buffer, count * 2); +} + +static u_int16_t +adv_read_eeprom_16(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(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(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(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(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_reinit_lram(struct adv_softc *adv) { + adv_init_lram(adv); + adv_init_qlink_var(adv); +} + +static void +adv_init_lram(struct adv_softc *adv) +{ + u_int8_t i; + u_int16_t s_addr; + + adv_mset_lram_16(adv, ADV_QADR_BEG, 0, + (((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(struct adv_softc *adv) +{ + int i; + + for (i = 0; i <= ADV_MAX_TID; i++) { + + /* Start out async all around */ + adv_set_syncrate(adv, /*path*/NULL, + i, 0, 0, + ADV_TRANS_GOAL|ADV_TRANS_CUR); + } + + adv_init_qlink_var(adv); + + 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); + + adv_write_lram_32(adv, ADVV_OVERRUN_PADDR_D, adv->overrun_physbase); + + adv_write_lram_32(adv, ADVV_OVERRUN_BSIZE_D, ADV_OVERRUN_BSIZE); + + 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); + } + return (0); +} + +static void +adv_init_qlink_var(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_Q_DONE_IN_PROGRESS_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(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(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(struct adv_softc *adv) +{ + ADV_OUTW(adv, ADV_CHIP_STATUS, ADV_CIW_IRQ_ACT); + ADV_OUTW(adv, ADV_CHIP_STATUS, 0); +} + +void +adv_start_execution(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); + } +} + +int +adv_stop_chip(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 int +adv_host_req_chip_halt(struct adv_softc *adv) +{ + int count; + u_int8_t saved_stop_code; + + if (adv_is_chip_halted(adv)) + return (1); + + count = 0; + saved_stop_code = adv_read_lram_8(adv, ADVV_STOP_CODE_B); + adv_write_lram_8(adv, ADVV_STOP_CODE_B, + ADV_STOP_HOST_REQ_RISC_HALT | ADV_STOP_REQ_RISC_STOP); + while (adv_is_chip_halted(adv) == 0 + && count++ < 2000) + ; + + adv_write_lram_8(adv, ADVV_STOP_CODE_B, saved_stop_code); + return (count < 2000); +} + +static void +adv_set_chip_ih(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); +} + +#if UNUSED +static u_int8_t +adv_get_chip_scsi_ctrl(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 + +/* + * 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(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(struct adv_softc *adv, u_int8_t n_qs) +{ + u_int cur_used_qs; + u_int cur_free_qs; + + 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); + } + adv->openings_needed = n_qs; + return (0); +} + +static u_int8_t +adv_alloc_free_queues(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(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(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) { + scsiq->q1.q_no = free_q_head; + + /* + * Now that we know our Q number, point our sense + * buffer pointer to a bus dma mapped area where + * we can dma the data to. + */ + scsiq->q1.sense_addr = adv->sense_physbase + + ((free_q_head - 1) * sizeof(struct scsi_sense_data)); + 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(struct adv_softc *adv, struct adv_scsi_q *scsiq, + u_int 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 << 1; + 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(struct adv_softc *adv, struct adv_scsi_q *scsiq, + u_int q_no) +{ + struct adv_target_transinfo* tinfo; + u_int q_addr; + u_int tid_no; + + tid_no = ADV_TIX_TO_TID(scsiq->q2.target_ix); + tinfo = &adv->tinfo[tid_no]; + if ((tinfo->current.period != tinfo->goal.period) + || (tinfo->current.offset != tinfo->goal.offset)) { + + adv_msgout_sdtr(adv, tinfo->goal.period, tinfo->goal.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(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 void +adv_handle_extmsg_in(struct adv_softc *adv, u_int16_t halt_q_addr, + u_int8_t q_cntl, target_bit_vector target_mask, + int tid_no) +{ + struct ext_msg ext_msg; + + adv_read_lram_16_multi(adv, ADVV_MSGIN_BEG, (u_int16_t *) &ext_msg, + sizeof(ext_msg) >> 1); + if ((ext_msg.msg_type == MSG_EXTENDED) + && (ext_msg.msg_req == MSG_EXT_SDTR) + && (ext_msg.msg_len == MSG_EXT_SDTR_LEN)) { + union ccb *ccb; + struct adv_target_transinfo* tinfo; + u_int32_t cinfo_index; + u_int period; + u_int offset; + int sdtr_accept; + u_int8_t orig_offset; + + cinfo_index = + adv_read_lram_32(adv, halt_q_addr + ADV_SCSIQ_D_CINFO_IDX); + ccb = adv->ccb_infos[cinfo_index].ccb; + tinfo = &adv->tinfo[tid_no]; + sdtr_accept = TRUE; + + orig_offset = ext_msg.req_ack_offset; + if (ext_msg.xfer_period < tinfo->goal.period) { + sdtr_accept = FALSE; + ext_msg.xfer_period = tinfo->goal.period; + } + + /* Perform range checking */ + period = ext_msg.xfer_period; + offset = ext_msg.req_ack_offset; + adv_period_offset_to_sdtr(adv, &period, &offset, tid_no); + ext_msg.xfer_period = period; + ext_msg.req_ack_offset = offset; + + /* Record our current sync settings */ + adv_set_syncrate(adv, ccb->ccb_h.path, + tid_no, ext_msg.xfer_period, + ext_msg.req_ack_offset, + ADV_TRANS_GOAL|ADV_TRANS_ACTIVE); + + /* Offset too high or large period forced async */ + if (orig_offset != ext_msg.req_ack_offset) + sdtr_accept = FALSE; + + if (sdtr_accept && (q_cntl & QC_MSG_OUT)) { + /* Valid response to our requested negotiation */ + q_cntl &= ~QC_MSG_OUT; + } else { + /* Must Respond */ + q_cntl |= QC_MSG_OUT; + adv_msgout_sdtr(adv, ext_msg.xfer_period, + ext_msg.req_ack_offset); + } + + } else if (ext_msg.msg_type == MSG_EXTENDED + && ext_msg.msg_req == MSG_EXT_WDTR + && ext_msg.msg_len == MSG_EXT_WDTR_LEN) { + + ext_msg.wdtr_width = 0; + adv_write_lram_16_multi(adv, ADVV_MSGOUT_BEG, + (u_int16_t *)&ext_msg, + sizeof(ext_msg) >> 1); + q_cntl |= QC_MSG_OUT; + } else { + + ext_msg.msg_type = MSG_MESSAGE_REJECT; + adv_write_lram_16_multi(adv, ADVV_MSGOUT_BEG, + (u_int16_t *)&ext_msg, + sizeof(ext_msg) >> 1); + q_cntl |= QC_MSG_OUT; + } + adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL, q_cntl); +} + +static void +adv_msgout_sdtr(struct adv_softc *adv, u_int8_t sdtr_period, + u_int8_t sdtr_offset) +{ + struct ext_msg 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); +} + +int +adv_abort_ccb(struct adv_softc *adv, int target, int lun, union ccb *ccb, + u_int32_t status, int queued_only) +{ + u_int16_t q_addr; + u_int8_t q_no; + struct adv_q_done_info scsiq_buf; + struct adv_q_done_info *scsiq; + u_int8_t target_ix; + int count; + + scsiq = &scsiq_buf; + target_ix = ADV_TIDLUN_TO_IX(target, lun); + count = 0; + for (q_no = ADV_MIN_ACTIVE_QNO; q_no <= adv->max_openings; q_no++) { + struct adv_ccb_info *ccb_info; + q_addr = ADV_QNO_TO_QADDR(q_no); + + adv_copy_lram_doneq(adv, q_addr, scsiq, adv->max_dma_count); + ccb_info = &adv->ccb_infos[scsiq->d2.ccb_index]; + if (((scsiq->q_status & QS_READY) != 0) + && ((scsiq->q_status & QS_ABORTED) == 0) + && ((scsiq->cntl & QCSG_SG_XFER_LIST) == 0) + && (scsiq->d2.target_ix == target_ix) + && (queued_only == 0 + || !(scsiq->q_status & (QS_DISC1|QS_DISC2|QS_BUSY|QS_DONE))) + && (ccb == NULL || (ccb == ccb_info->ccb))) { + union ccb *aborted_ccb; + struct adv_ccb_info *cinfo; + + scsiq->q_status |= QS_ABORTED; + adv_write_lram_8(adv, q_addr + ADV_SCSIQ_B_STATUS, + scsiq->q_status); + aborted_ccb = ccb_info->ccb; + /* Don't clobber earlier error codes */ + if ((aborted_ccb->ccb_h.status & CAM_STATUS_MASK) + == CAM_REQ_INPROG) + aborted_ccb->ccb_h.status |= status; + cinfo = (struct adv_ccb_info *) + aborted_ccb->ccb_h.ccb_cinfo_ptr; + cinfo->state |= ACCB_ABORT_QUEUED; + count++; + } + } + return (count); +} + +int +adv_reset_bus(struct adv_softc *adv, int initiate_bus_reset) +{ + int count; + int i; + union ccb *ccb; + + i = 200; + while ((ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_SCSI_RESET_ACTIVE) != 0 + && i--) + DELAY(1000); + adv_reset_chip(adv, initiate_bus_reset); + adv_reinit_lram(adv); + for (i = 0; i <= ADV_MAX_TID; i++) + adv_set_syncrate(adv, NULL, i, /*period*/0, + /*offset*/0, ADV_TRANS_CUR); + ADV_OUTW(adv, ADV_REG_PROG_COUNTER, ADV_MCODE_START_ADDR); + + /* Tell the XPT layer that a bus reset occured */ + if (adv->path != NULL) + xpt_async(AC_BUS_RESET, adv->path, NULL); + + count = 0; + while ((ccb = (union ccb *)LIST_FIRST(&adv->pending_ccbs)) != NULL) { + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) + ccb->ccb_h.status |= CAM_SCSI_BUS_RESET; + adv_done(adv, ccb, QD_ABORTED_BY_HOST, 0, 0, 0); + count++; + } + + adv_start_chip(adv); + return (count); +} + +static void +adv_set_sdtr_reg_at_id(struct adv_softc *adv, int tid, u_int8_t sdtr_data) +{ + int orig_id; + + adv_set_bank(adv, 1); + orig_id = ffs(ADV_INB(adv, ADV_HOST_SCSIID)) - 1; + ADV_OUTB(adv, ADV_HOST_SCSIID, tid); + if (ADV_INB(adv, ADV_HOST_SCSIID) == (0x01 << tid)) { + adv_set_bank(adv, 0); + ADV_OUTB(adv, ADV_SYN_OFFSET, sdtr_data); + } + adv_set_bank(adv, 1); + ADV_OUTB(adv, ADV_HOST_SCSIID, orig_id); + adv_set_bank(adv, 0); +} |