diff --git a/sys/dev/ic/adw.c b/sys/dev/ic/adw.c new file mode 100644 index 000000000000..dbc27122aaf1 --- /dev/null +++ b/sys/dev/ic/adw.c @@ -0,0 +1,1004 @@ +/* $NetBSD: adw.c,v 1.1 1998/09/26 16:10:40 dante Exp $ */ + +/* + * Generic driver for the Advanced Systems Inc. SCSI controllers + * + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Baldassare Dante Profeta + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifndef DDB +#define Debugger() panic("should call debugger here (adv.c)") +#endif /* ! DDB */ + +/******************************************************************************/ + + +static void adw_enqueue __P((ADW_SOFTC *, struct scsipi_xfer *, int)); +static struct scsipi_xfer *adw_dequeue __P((ADW_SOFTC *)); + +static int adw_alloc_ccbs __P((ADW_SOFTC *)); +static int adw_create_ccbs __P((ADW_SOFTC *, ADW_CCB *, int)); +static void adw_free_ccb __P((ADW_SOFTC *, ADW_CCB *)); +static void adw_reset_ccb __P((ADW_CCB *)); +static int adw_init_ccb __P((ADW_SOFTC *, ADW_CCB *)); +static ADW_CCB *adw_get_ccb __P((ADW_SOFTC *, int)); +static void adw_queue_ccb __P((ADW_SOFTC *, ADW_CCB *)); +static void adw_start_ccbs __P((ADW_SOFTC *)); + +static int adw_scsi_cmd __P((struct scsipi_xfer *)); +static int adw_build_req __P((struct scsipi_xfer *, ADW_CCB *)); +static void adw_build_sglist __P(( ADW_CCB *, ADW_SCSI_REQ_Q *)); +static void adwminphys __P((struct buf *)); +static void adw_wide_isr_callback __P((ADW_SOFTC *, ADW_SCSI_REQ_Q *)); + +static int adw_poll __P((ADW_SOFTC *, struct scsipi_xfer *, int)); +static void adw_timeout __P((void *)); +static void adw_watchdog __P((void *)); + + +/******************************************************************************/ + + +struct scsipi_adapter adw_switch = +{ + adw_scsi_cmd, /* called to start/enqueue a SCSI command */ + adwminphys, /* to limit the transfer to max device can do */ + 0, /* IT SEEMS IT IS NOT USED YET */ + 0, /* as above... */ +}; + + +/* the below structure is so we have a default dev struct for out link struct */ +struct scsipi_device adw_dev = +{ + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + + +#define ADW_ABORT_TIMEOUT 10000 /* time to wait for abort (mSec) */ +#define ADW_WATCH_TIMEOUT 10000 /* time to wait for watchdog (mSec) */ + + +/******************************************************************************/ +/* scsipi_xfer queue routines */ +/******************************************************************************/ + +/* + * Insert a scsipi_xfer into the software queue. We overload xs->free_list + * to avoid having to allocate additional resources (since we're used + * only during resource shortages anyhow. + */ +static void +adw_enqueue(sc, xs, infront) + ADW_SOFTC *sc; + struct scsipi_xfer *xs; + int infront; +{ + + if (infront || sc->sc_queue.lh_first == NULL) { + if (sc->sc_queue.lh_first == NULL) + sc->sc_queuelast = xs; + LIST_INSERT_HEAD(&sc->sc_queue, xs, free_list); + return; + } + LIST_INSERT_AFTER(sc->sc_queuelast, xs, free_list); + sc->sc_queuelast = xs; +} + + +/* + * Pull a scsipi_xfer off the front of the software queue. + */ +static struct scsipi_xfer * +adw_dequeue(sc) + ADW_SOFTC *sc; +{ + struct scsipi_xfer *xs; + + xs = sc->sc_queue.lh_first; + LIST_REMOVE(xs, free_list); + + if (sc->sc_queue.lh_first == NULL) + sc->sc_queuelast = NULL; + + return (xs); +} + + +/******************************************************************************/ +/* Control Blocks routines */ +/******************************************************************************/ + + +static int +adw_alloc_ccbs(sc) + ADW_SOFTC *sc; +{ + bus_dma_segment_t seg; + int error, rseg; + + /* + * Allocate the control blocks. + */ + if ((error = bus_dmamem_alloc(sc->sc_dmat, sizeof(struct adw_control), + NBPG, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to allocate control structures," + " error = %d\n", sc->sc_dev.dv_xname, error); + return (error); + } + if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, + sizeof(struct adw_control), (caddr_t *) & sc->sc_control, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { + printf("%s: unable to map control structures, error = %d\n", + sc->sc_dev.dv_xname, error); + return (error); + } + /* + * Create and load the DMA map used for the control blocks. + */ + if ((error = bus_dmamap_create(sc->sc_dmat, sizeof(struct adw_control), + 1, sizeof(struct adw_control), 0, BUS_DMA_NOWAIT, + &sc->sc_dmamap_control)) != 0) { + printf("%s: unable to create control DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + return (error); + } + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap_control, + sc->sc_control, sizeof(struct adw_control), NULL, + BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load control DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + return (error); + } + return (0); +} + + +/* + * Create a set of ccbs and add them to the free list. Called once + * by adw_init(). We return the number of CCBs successfully created. + */ +static int +adw_create_ccbs(sc, ccbstore, count) + ADW_SOFTC *sc; + ADW_CCB *ccbstore; + int count; +{ + ADW_CCB *ccb; + int i, error; + + bzero(ccbstore, sizeof(ADW_CCB) * count); + for (i = 0; i < count; i++) { + ccb = &ccbstore[i]; + if ((error = adw_init_ccb(sc, ccb)) != 0) { + printf("%s: unable to initialize ccb, error = %d\n", + sc->sc_dev.dv_xname, error); + return (i); + } + TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, chain); + } + + return (i); +} + + +/* + * A ccb is put onto the free list. + */ +static void +adw_free_ccb(sc, ccb) + ADW_SOFTC *sc; + ADW_CCB *ccb; +{ + int s; + + s = splbio(); + + adw_reset_ccb(ccb); + TAILQ_INSERT_HEAD(&sc->sc_free_ccb, ccb, chain); + + /* + * If there were none, wake anybody waiting for one to come free, + * starting with queued entries. + */ + if (ccb->chain.tqe_next == 0) + wakeup(&sc->sc_free_ccb); + + splx(s); +} + + +static void +adw_reset_ccb(ccb) + ADW_CCB *ccb; +{ + + ccb->flags = 0; +} + + +static int +adw_init_ccb(sc, ccb) + ADW_SOFTC *sc; + ADW_CCB *ccb; +{ + int error; + + /* + * Create the DMA map for this CCB. + */ + error = bus_dmamap_create(sc->sc_dmat, + (ADW_MAX_SG_LIST - 1) * PAGE_SIZE, + ADW_MAX_SG_LIST, (ADW_MAX_SG_LIST - 1) * PAGE_SIZE, + 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->dmamap_xfer); + if (error) { + printf("%s: unable to create DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + return (error); + } + adw_reset_ccb(ccb); + return (0); +} + + +/* + * Get a free ccb + * + * If there are none, see if we can allocate a new one + */ +static ADW_CCB * +adw_get_ccb(sc, flags) + ADW_SOFTC *sc; + int flags; +{ + ADW_CCB *ccb = 0; + int s; + + s = splbio(); + + /* + * If we can and have to, sleep waiting for one to come free + * but only if we can't allocate a new one. + */ + for (;;) { + ccb = sc->sc_free_ccb.tqh_first; + if (ccb) { + TAILQ_REMOVE(&sc->sc_free_ccb, ccb, chain); + break; + } + if ((flags & SCSI_NOSLEEP) != 0) + goto out; + + tsleep(&sc->sc_free_ccb, PRIBIO, "adwccb", 0); + } + + ccb->flags |= CCB_ALLOC; + +out: + splx(s); + return (ccb); +} + + +/* + * Queue a CCB to be sent to the controller, and send it if possible. + */ +static void +adw_queue_ccb(sc, ccb) + ADW_SOFTC *sc; + ADW_CCB *ccb; +{ + + TAILQ_INSERT_TAIL(&sc->sc_waiting_ccb, ccb, chain); + + adw_start_ccbs(sc); +} + + +static void +adw_start_ccbs(sc) + ADW_SOFTC *sc; +{ + ADW_CCB *ccb; + + while ((ccb = sc->sc_waiting_ccb.tqh_first) != NULL) { + if (ccb->flags & CCB_WATCHDOG) + untimeout(adw_watchdog, ccb); + + if (AdvExeScsiQueue(sc, &ccb->scsiq) == ADW_BUSY) { + ccb->flags |= CCB_WATCHDOG; + timeout(adw_watchdog, ccb, + (ADW_WATCH_TIMEOUT * hz) / 1000); + break; + } + TAILQ_REMOVE(&sc->sc_waiting_ccb, ccb, chain); + + if ((ccb->xs->flags & SCSI_POLL) == 0) + timeout(adw_timeout, ccb, (ccb->timeout * hz) / 1000); + } +} + + +/******************************************************************************/ +/* SCSI layer interfacing routines */ +/******************************************************************************/ + + +int +adw_init(sc) + ADW_SOFTC *sc; +{ + u_int16_t warn_code; + + + sc->cfg.lib_version = (ADW_LIB_VERSION_MAJOR << 8) | + ADW_LIB_VERSION_MINOR; + sc->cfg.chip_version = + ADW_GET_CHIP_VERSION(sc->sc_iot, sc->sc_ioh, sc->bus_type); + + /* + * Reset the chip to start and allow register writes. + */ + if (ADW_FIND_SIGNATURE(sc->sc_iot, sc->sc_ioh) == 0) { + panic("adw_init: adw_find_signature failed"); + } + else + { + AdvResetChip(sc->sc_iot, sc->sc_ioh); + + warn_code = AdvInitFromEEP(sc); + if(warn_code & ASC_WARN_EEPROM_CHKSUM) + printf("%s: Bad checksum found. " + "Setting default values\n", + sc->sc_dev.dv_xname); + if(warn_code & ASC_WARN_EEPROM_TERMINATION) + printf("%s: Bad bus termination setting." + "Using automatic termination.\n", + sc->sc_dev.dv_xname); + + /* + * Reset the SCSI Bus if the EEPROM indicates that SCSI Bus + * Resets should be performed. + */ + if (sc->bios_ctrl & BIOS_CTRL_RESET_SCSI_BUS) + AdvResetSCSIBus(sc); + } + + sc->isr_callback = (ulong) adw_wide_isr_callback; + + return (0); +} + + +void +adw_attach(sc) + ADW_SOFTC *sc; +{ + int i, error; + + + /* + * Initialize the ASC3550. + */ + switch(AdvInitAsc3550Driver(sc)) + { + case ASC_IERR_MCODE_CHKSUM: + panic("%s: Microcode checksum error", + sc->sc_dev.dv_xname); + break; + + case ASC_IERR_ILLEGAL_CONNECTION: + panic("%s: All three connectors are in use", + sc->sc_dev.dv_xname); + break; + + case ASC_IERR_REVERSED_CABLE: + panic("%s: Cable is reversed", + sc->sc_dev.dv_xname); + break; + + case ASC_IERR_SINGLE_END_DEVICE: + panic("%s: single-ended device is attached to" + " one of the connectors", + sc->sc_dev.dv_xname); + break; + } + + + /* + * fill in the prototype scsipi_link. + */ + sc->sc_link.scsipi_scsi.channel = SCSI_CHANNEL_ONLY_ONE; + sc->sc_link.adapter_softc = sc; + sc->sc_link.scsipi_scsi.adapter_target = sc->chip_scsi_id; + sc->sc_link.adapter = &adw_switch; + sc->sc_link.device = &adw_dev; + sc->sc_link.openings = 4; + sc->sc_link.scsipi_scsi.max_target = ADW_MAX_TID; + sc->sc_link.type = BUS_SCSI; + + + TAILQ_INIT(&sc->sc_free_ccb); + TAILQ_INIT(&sc->sc_waiting_ccb); + LIST_INIT(&sc->sc_queue); + + + /* + * Allocate the Control Blocks. + */ + error = adw_alloc_ccbs(sc); + if (error) + return; /* (error) */ ; + + /* + * Create and initialize the Control Blocks. + */ + i = adw_create_ccbs(sc, sc->sc_control->ccbs, ADW_MAX_CCB); + if (i == 0) { + printf("%s: unable to create control blocks\n", + sc->sc_dev.dv_xname); + return; /* (ENOMEM) */ ; + } else if (i != ADW_MAX_CCB) { + printf("%s: WARNING: only %d of %d control blocks" + " created\n", + sc->sc_dev.dv_xname, i, ADW_MAX_CCB); + } + + config_found(&sc->sc_dev, &sc->sc_link, scsiprint); +} + + +static void +adwminphys(bp) + struct buf *bp; +{ + + if (bp->b_bcount > ((ADW_MAX_SG_LIST - 1) * PAGE_SIZE)) + bp->b_bcount = ((ADW_MAX_SG_LIST - 1) * PAGE_SIZE); + minphys(bp); +} + + +/* + * start a scsi operation given the command and the data address. Also needs + * the unit, target and lu. + */ +static int +adw_scsi_cmd(xs) + struct scsipi_xfer *xs; +{ + struct scsipi_link *sc_link = xs->sc_link; + ADW_SOFTC *sc = sc_link->adapter_softc; + ADW_CCB *ccb; + int s, fromqueue = 1, dontqueue = 0; + + s = splbio(); /* protect the queue */ + + /* + * If we're running the queue from adw_done(), we've been + * called with the first queue entry as our argument. + */ + if (xs == sc->sc_queue.lh_first) { + xs = adw_dequeue(sc); + fromqueue = 1; + } else { + + /* Polled requests can't be queued for later. */ + dontqueue = xs->flags & SCSI_POLL; + + /* + * If there are jobs in the queue, run them first. + */ + if (sc->sc_queue.lh_first != NULL) { + /* + * If we can't queue, we have to abort, since + * we have to preserve order. + */ + if (dontqueue) { + splx(s); + xs->error = XS_DRIVER_STUFFUP; + return (TRY_AGAIN_LATER); + } + /* + * Swap with the first queue entry. + */ + adw_enqueue(sc, xs, 0); + xs = adw_dequeue(sc); + fromqueue = 1; + } + } + + + /* + * get a ccb to use. If the transfer + * is from a buf (possibly from interrupt time) + * then we can't allow it to sleep + */ + + if ((ccb = adw_get_ccb(sc, xs->flags)) == NULL) { + /* + * If we can't queue, we lose. + */ + if (dontqueue) { + splx(s); + xs->error = XS_DRIVER_STUFFUP; + return (TRY_AGAIN_LATER); + } + /* + * Stuff ourselves into the queue, in front + * if we came off in the first place. + */ + adw_enqueue(sc, xs, fromqueue); + splx(s); + return (SUCCESSFULLY_QUEUED); + } + splx(s); /* done playing with the queue */ + + ccb->xs = xs; + ccb->timeout = xs->timeout; + + if(adw_build_req(xs, ccb)) + { + s = splbio(); + adw_queue_ccb(sc, ccb); + splx(s); + + /* + * Usually return SUCCESSFULLY QUEUED + */ + if ((xs->flags & SCSI_POLL) == 0) + return (SUCCESSFULLY_QUEUED); + + /* + * If we can't use interrupts, poll on completion + */ + if (adw_poll(sc, xs, ccb->timeout)) { + adw_timeout(ccb); + if (adw_poll(sc, xs, ccb->timeout)) + adw_timeout(ccb); + } + } + + return (COMPLETE); +} + + +/* + * Build a request structure for the Wide Boards. + */ +static int +adw_build_req(xs, ccb) + struct scsipi_xfer *xs; + ADW_CCB *ccb; +{ + struct scsipi_link *sc_link = xs->sc_link; + ADW_SOFTC *sc = sc_link->adapter_softc; + bus_dma_tag_t dmat = sc->sc_dmat; + ADW_SCSI_REQ_Q *scsiqp; + int error; + + scsiqp = &ccb->scsiq; + bzero(scsiqp, sizeof(ADW_SCSI_REQ_Q)); + + /* + * Set the ADW_SCSI_REQ_Q 'ccb_ptr' to point to the CCB structure. + */ + scsiqp->ccb_ptr = (ulong) ccb; + + + /* + * Build the ADW_SCSI_REQ_Q request. + */ + + /* + * Set CDB length and copy it to the request structure. + */ + bcopy(xs->cmd, &scsiqp->cdb, scsiqp->cdb_len = xs->cmdlen); + + scsiqp->target_id = sc_link->scsipi_scsi.target; + scsiqp->target_lun = sc_link->scsipi_scsi.lun; + + scsiqp->vsense_addr = (ulong) &ccb->scsi_sense; + scsiqp->sense_addr = sc->sc_dmamap_control->dm_segs[0].ds_addr + + ADW_CCB_OFF(ccb) + offsetof(struct adw_ccb, scsi_sense); + scsiqp->sense_len = sizeof(struct scsipi_sense_data); + + /* + * Build ADW_SCSI_REQ_Q for a scatter-gather buffer command. + */ + if (xs->datalen) { + /* + * Map the DMA transfer. + */ +#ifdef TFS + if (xs->flags & SCSI_DATA_UIO) { + error = bus_dmamap_load_uio(dmat, + ccb->dmamap_xfer, (struct uio *) xs->data, + (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + } else +#endif /* TFS */ + { + error = bus_dmamap_load(dmat, + ccb->dmamap_xfer, xs->data, xs->datalen, NULL, + (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + } + + if (error) { + if (error == EFBIG) { + printf("%s: adw_scsi_cmd, more than %d dma" + " segments\n", + sc->sc_dev.dv_xname, ADW_MAX_SG_LIST); + } else { + printf("%s: adw_scsi_cmd, error %d loading" + " dma map\n", + sc->sc_dev.dv_xname, error); + } + + xs->error = XS_DRIVER_STUFFUP; + adw_free_ccb(sc, ccb); + return (0); + } + + bus_dmamap_sync(dmat, ccb->dmamap_xfer, 0, + ccb->dmamap_xfer->dm_mapsize, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : + BUS_DMASYNC_PREWRITE); + + /* + * Build scatter-gather list. + */ + scsiqp->data_cnt = xs->datalen; + scsiqp->vdata_addr = (ulong) xs->data; + scsiqp->data_addr = ccb->dmamap_xfer->dm_segs[0].ds_addr; + scsiqp->sg_list_ptr = &ccb->sg_block[0]; + bzero(scsiqp->sg_list_ptr, sizeof(ADW_SG_BLOCK) * ADW_NUM_SG_BLOCK); + adw_build_sglist(ccb, scsiqp); + } else { + /* + * No data xfer, use non S/G values. + */ + scsiqp->data_cnt = 0; + scsiqp->vdata_addr = 0; + scsiqp->data_addr = 0; + scsiqp->sg_list_ptr = NULL; + } + + return (1); +} + + +/* + * Build scatter-gather list for Wide Boards. + */ +static void +adw_build_sglist(ccb, scsiqp) + ADW_CCB *ccb; + ADW_SCSI_REQ_Q *scsiqp; +{ + struct scsipi_xfer *xs = ccb->xs; + ADW_SOFTC *sc = xs->sc_link->adapter_softc; + ADW_SG_BLOCK *sg_block = scsiqp->sg_list_ptr; + ulong sg_block_next_addr; /* block and its next */ + ulong sg_block_physical_addr; + int sg_block_index, i; /* how many SG entries */ + bus_dma_segment_t *sg_list = &ccb->dmamap_xfer->dm_segs[0]; + int sg_elem_cnt = ccb->dmamap_xfer->dm_nsegs; + + + sg_block_next_addr = (ulong) sg_block; /* allow math operation */ + sg_block_physical_addr = sc->sc_dmamap_control->dm_segs[0].ds_addr + + ADW_CCB_OFF(ccb) + offsetof(struct adw_ccb, sg_block[0]); + scsiqp->sg_real_addr = sg_block_physical_addr; + + /* + * If there are more than NO_OF_SG_PER_BLOCK dma segments (hw sg-list) + * then split the request into multiple sg-list blocks. + */ + + sg_block_index = 0; + do + { + sg_block->first_entry_no = sg_block_index; + for (i = 0; i < NO_OF_SG_PER_BLOCK; i++) + { + sg_block->sg_list[i].sg_addr = sg_list->ds_addr; + sg_block->sg_list[i].sg_count = sg_list->ds_len; + + if (--sg_elem_cnt == 0) + { + /* last entry, get out */ + scsiqp->sg_entry_cnt = sg_block_index + i + 1; + sg_block->last_entry_no = sg_block_index + i; + sg_block->sg_ptr = NULL;/* next link = NULL */ + return; + } + sg_list++; + } + sg_block_next_addr += sizeof(ADW_SG_BLOCK); + sg_block_physical_addr += sizeof(ADW_SG_BLOCK); + + sg_block_index += NO_OF_SG_PER_BLOCK; + sg_block->sg_ptr = (ADW_SG_BLOCK *) sg_block_physical_addr; + sg_block->last_entry_no = sg_block_index - 1; + sg_block = (ADW_SG_BLOCK *) sg_block_next_addr; /* virtual addr */ + } + while (1); +} + + +int +adw_intr(arg) + void *arg; +{ + ADW_SOFTC *sc = arg; + struct scsipi_xfer *xs; + + + AdvISR(sc); + + /* + * If there are queue entries in the software queue, try to + * run the first one. We should be more or less guaranteed + * to succeed, since we just freed a CCB. + * + * NOTE: adw_scsi_cmd() relies on our calling it with + * the first entry in the queue. + */ + if ((xs = sc->sc_queue.lh_first) != NULL) + (void) adw_scsi_cmd(xs); + + return (1); +} + + +/* + * Poll a particular unit, looking for a particular xs + */ +static int +adw_poll(sc, xs, count) + ADW_SOFTC *sc; + struct scsipi_xfer *xs; + int count; +{ + + /* timeouts are in msec, so we loop in 1000 usec cycles */ + while (count) { + adw_intr(sc); + if (xs->flags & ITSDONE) + return (0); + delay(1000); /* only happens in boot so ok */ + count--; + } + return (1); +} + + +static void +adw_timeout(arg) + void *arg; +{ + ADW_CCB *ccb = arg; + struct scsipi_xfer *xs = ccb->xs; + struct scsipi_link *sc_link = xs->sc_link; + ADW_SOFTC *sc = sc_link->adapter_softc; + int s; + + scsi_print_addr(sc_link); + printf("timed out"); + + s = splbio(); + + /* + * If it has been through before, then a previous abort has failed, + * don't try abort again, reset the bus instead. + */ + if (ccb->flags & CCB_ABORT) { + /* abort timed out */ + printf(" AGAIN. Resetting Bus\n"); + /* Lets try resetting the bus! */ + AdvResetSCSIBus(sc); + ccb->timeout = ADW_ABORT_TIMEOUT; + adw_queue_ccb(sc, ccb); + } else { + /* abort the operation that has timed out */ + printf("\n"); + ADW_ABORT_CCB(sc, ccb); + xs->error = XS_TIMEOUT; + ccb->timeout = ADW_ABORT_TIMEOUT; + ccb->flags |= CCB_ABORT; + adw_queue_ccb(sc, ccb); + } + + splx(s); +} + + +static void +adw_watchdog(arg) + void *arg; +{ + ADW_CCB *ccb = arg; + struct scsipi_xfer *xs = ccb->xs; + struct scsipi_link *sc_link = xs->sc_link; + ADW_SOFTC *sc = sc_link->adapter_softc; + int s; + + s = splbio(); + + ccb->flags &= ~CCB_WATCHDOG; + adw_start_ccbs(sc); + + splx(s); +} + + +/******************************************************************************/ +/* NARROW and WIDE boards Interrupt callbacks */ +/******************************************************************************/ + + +/* + * adw_wide_isr_callback() - Second Level Interrupt Handler called by AdvISR() + * + * Interrupt callback function for the Wide SCSI Adv Library. + */ +static void +adw_wide_isr_callback(sc, scsiq) + ADW_SOFTC *sc; + ADW_SCSI_REQ_Q *scsiq; +{ + bus_dma_tag_t dmat = sc->sc_dmat; + ADW_CCB *ccb = (ADW_CCB *) scsiq->ccb_ptr; + struct scsipi_xfer *xs = ccb->xs; + struct scsipi_sense_data *s1, *s2; +// int underrun = ASC_FALSE; + + + untimeout(adw_timeout, ccb); + + /* + * If we were a data transfer, unload the map that described + * the data buffer. + */ + if (xs->datalen) { + bus_dmamap_sync(dmat, ccb->dmamap_xfer, 0, + ccb->dmamap_xfer->dm_mapsize, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dmat, ccb->dmamap_xfer); + } + + if ((ccb->flags & CCB_ALLOC) == 0) { + printf("%s: exiting ccb not allocated!\n", sc->sc_dev.dv_xname); + Debugger(); + return; + } + + + /* + * Check for an underrun condition. + */ +/* if (xs->request_bufflen != 0 && scsiqp->data_cnt != 0) { + ASC_DBG1(1, "adw_isr_callback: underrun condition %lu bytes\n", + scsiqp->data_cnt); + underrun = ASC_TRUE; + } +*/ + /* + * 'done_status' contains the command's ending status. + */ + switch (scsiq->done_status) { + case QD_NO_ERROR: + switch (scsiq->host_status) { + case QHSTA_NO_ERROR: + xs->error = XS_NOERROR; + xs->resid = 0; + break; + default: + /* QHSTA error occurred. */ + xs->error = XS_DRIVER_STUFFUP; + break; + } + /* + * If there was an underrun without any other error, + * set DID_ERROR to indicate the underrun error. + * + * Note: There is no way yet to indicate the number + * of underrun bytes. + */ +/* if (xs->error == XS_NOERROR && underrun == ASC_TRUE) { + scp->result = HOST_BYTE(DID_UNDERRUN); + } +*/ break; + + case QD_WITH_ERROR: + switch (scsiq->host_status) { + case QHSTA_NO_ERROR: + if (scsiq->scsi_status == SS_CHK_CONDITION) { + s1 = &ccb->scsi_sense; + s2 = &xs->sense.scsi_sense; + *s2 = *s1; + xs->error = XS_SENSE; + } else { + xs->error = XS_DRIVER_STUFFUP; + } + break; + + default: + /* Some other QHSTA error occurred. */ + xs->error = XS_DRIVER_STUFFUP; + break; + } + break; + + case QD_ABORTED_BY_HOST: + default: + xs->error = XS_DRIVER_STUFFUP; + break; + } + + + adw_free_ccb(sc, ccb); + xs->flags |= ITSDONE; + scsipi_done(xs); +} diff --git a/sys/dev/ic/adw.h b/sys/dev/ic/adw.h new file mode 100644 index 000000000000..d60ecd7f5b59 --- /dev/null +++ b/sys/dev/ic/adw.h @@ -0,0 +1,110 @@ +/* $NetBSD: adw.h,v 1.1 1998/09/26 16:10:41 dante Exp $ */ + +/* + * Generic driver definitions and exported functions for the Advanced + * Systems Inc. SCSI controllers + * + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Baldassare Dante Profeta + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _ADVANSYS_WIDE_H_ +#define _ADVANSYS_WIDE_H_ + +/******************************************************************************/ + +typedef int (* ADW_ISR_CALLBACK) (ADW_SOFTC *, ADW_SCSI_REQ_Q *); +typedef int (* ADW_SBRESET_CALLBACK) (ADW_SOFTC *); + +/* per request scatter-gather element limit */ +#define ADW_MAX_SG_LIST 64 + +/* + * Scatter-Gather Definitions per request. + */ + +#define NO_OF_SG_PER_BLOCK 15 + +/* Number of SG blocks needed. */ +#define ADW_NUM_SG_BLOCK \ + ((ADW_MAX_SG_LIST + (NO_OF_SG_PER_BLOCK - 1))/NO_OF_SG_PER_BLOCK) + + +struct adw_ccb +{ + ADW_SG_BLOCK sg_block[ADW_NUM_SG_BLOCK]; + ADW_SCSI_REQ_Q scsiq; + + struct scsipi_sense_data scsi_sense; + + TAILQ_ENTRY(adw_ccb) chain; + struct scsipi_xfer *xs; /* the scsipi_xfer for this cmd */ + int flags; /* see below */ + + int timeout; + /* + * This DMA map maps the buffer involved in the transfer. + */ + bus_dmamap_t dmamap_xfer; +}; + +typedef struct adw_ccb ADW_CCB; + +/* flags for ADW_CCB */ +#define CCB_ALLOC 0x01 +#define CCB_ABORT 0x02 +#define CCB_WATCHDOG 0x10 + + +#define ADW_MAX_CCB 16 + +struct adw_control +{ + ADW_CCB ccbs[ADW_MAX_CCB]; /* all our control blocks */ +}; + +/* + * Offset of a CCB from the beginning of the control DMA mapping. + */ +#define ADW_CCB_OFF(c) (offsetof(struct adw_control, ccbs[0]) + \ + (((u_long)(c)) - ((u_long)&sc->sc_control->ccbs[0]))) + +/******************************************************************************/ + +int adw_init __P((ADW_SOFTC *sc)); +void adw_attach __P((ADW_SOFTC *sc)); +int adw_intr __P((void *arg)); + +/******************************************************************************/ + +#endif /* _ADVANSYS_ADW_H_ */ diff --git a/sys/dev/ic/adwlib.c b/sys/dev/ic/adwlib.c new file mode 100644 index 000000000000..d5023a64419e --- /dev/null +++ b/sys/dev/ic/adwlib.c @@ -0,0 +1,1353 @@ +/* $NetBSD: adwlib.c,v 1.1 1998/09/26 16:10:41 dante Exp $ */ + +/* + * Low level routines for the Advanced Systems Inc. SCSI controllers chips + * + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Baldassare Dante Profeta + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +/* + * Ported from: + */ +/* + * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters + * + * Copyright (c) 1995-1998 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 + +#include +#include +#include + +#include +#include +#include + + +/* Static Functions */ + +static u_int16_t AdvGetEEPConfig __P((bus_space_tag_t, bus_space_handle_t, + ADWEEP_CONFIG *)); +static u_int16_t AdvReadEEPWord __P((bus_space_tag_t, bus_space_handle_t, int)); +static void AdvWaitEEPCmd __P((bus_space_tag_t, bus_space_handle_t)); +static void AdvSetEEPConfig __P((bus_space_tag_t, bus_space_handle_t, + ADWEEP_CONFIG *)); +static int AdvSendScsiCmd __P((ADW_SOFTC *, ADW_SCSI_REQ_Q *)); +static void AdvInquiryHandling __P((ADW_SOFTC *, ADW_SCSI_REQ_Q *)); + +static void DvcSleepMilliSecond __P((ulong)); +static void DvcDelayMicroSecond __P((ulong)); + + +/* + * EEPROM Configuration. + * + * All drivers should use this structure to set the default EEPROM + * configuration. The BIOS now uses this structure when it is built. + * Additional structure information can be found in advlib.h where + * the structure is defined. + */ +static ADWEEP_CONFIG +Default_EEPROM_Config = { + ADW_EEPROM_BIOS_ENABLE, /* cfg_msw */ + 0x0000, /* cfg_lsw */ + 0xFFFF, /* disc_enable */ + 0xFFFF, /* wdtr_able */ + 0xFFFF, /* sdtr_able */ + 0xFFFF, /* start_motor */ + 0xFFFF, /* tagqng_able */ + 0xFFFF, /* bios_scan */ + 0, /* scam_tolerant */ + 7, /* adapter_scsi_id */ + 0, /* bios_boot_delay */ + 3, /* scsi_reset_delay */ + 0, /* bios_id_lun */ + 0, /* termination */ + 0, /* reserved1 */ + 0xFFEF, /* bios_ctrl */ + 0xFFFF, /* ultra_able */ + 0, /* reserved2 */ + ASC_DEF_MAX_HOST_QNG, /* max_host_qng */ + ASC_DEF_MAX_DVC_QNG, /* max_dvc_qng */ + 0, /* dvc_cntl */ + 0, /* bug_fix */ + 0, /* serial_number_word1 */ + 0, /* serial_number_word2 */ + 0, /* serial_number_word3 */ + 0, /* check_sum */ + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* oem_name[16] */ + 0, /* dvc_err_code */ + 0, /* adv_err_code */ + 0, /* adv_err_addr */ + 0, /* saved_dvc_err_code */ + 0, /* saved_adv_err_code */ + 0, /* saved_adv_err_addr */ + 0 /* num_of_err */ +}; + +/* + * Initialize the ASC3550. + * + * On failure set the ADW_SOFTC field 'err_code' and return ADW_ERROR. + * + * For a non-fatal error return a warning code. If there are no warnings + * then 0 is returned. + */ +int +AdvInitAsc3550Driver(sc) + ADW_SOFTC *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_int16_t warn_code; + u_int32_t sum; + int begin_addr; + int end_addr; + int code_sum; + int word; + int rql_addr; /* RISC Queue List address */ + int i; + u_int16_t scsi_cfg1; + u_int8_t biosmem[ASC_MC_BIOSLEN]; /* BIOS RISC Memory 0x40-0x8F */ + + + warn_code = 0; + + /* + * Save the RISC memory BIOS region before writing the microcode. + * The BIOS may already be loaded and using its RISC LRAM region + * so its region must be saved and restored. + * + * Note: This code makes the assumption, which is currently true, + * that a chip reset does not clear RISC LRAM. + */ + for (i = 0; i < ASC_MC_BIOSLEN; i++) { + ADW_READ_BYTE_LRAM(iot, ioh, ASC_MC_BIOSMEM + i, biosmem[i]); + } + + /* + * Load the Microcode + * + * Write the microcode image to RISC memory starting at address 0. + */ + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_RAM_ADDR, 0); + for (word = 0; word < adv_mcode_size; word += 2) { + ADW_WRITE_WORD_AUTO_INC_LRAM(iot, ioh, + *((u_int16_t *) (&adv_mcode[word]))); + } + + /* + * Clear the rest of Condor's Internal RAM (8KB). + */ + for (; word < ADW_CONDOR_MEMSIZE; word += 2) { + ADW_WRITE_WORD_AUTO_INC_LRAM(iot, ioh, 0); + } + + /* + * Verify the microcode checksum. + */ + sum = 0; + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_RAM_ADDR, 0); + for (word = 0; word < adv_mcode_size; word += 2) { + sum += ADW_READ_WORD_AUTO_INC_LRAM(iot, ioh); + } + + if (sum != adv_mcode_chksum) + return ASC_IERR_MCODE_CHKSUM; + + /* + * Restore the RISC memory BIOS region. + */ + for (i = 0; i < ASC_MC_BIOSLEN; i++) { + ADW_WRITE_BYTE_LRAM(iot, ioh, ASC_MC_BIOSMEM + i, biosmem[i]); + } + + /* + * Calculate and write the microcode code checksum to the microcode + * code checksum location ASC_MC_CODE_CHK_SUM (0x2C). + */ + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_CODE_BEGIN_ADDR, begin_addr); + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_CODE_END_ADDR, end_addr); + code_sum = 0; + for (word = begin_addr; word < end_addr; word += 2) { + code_sum += *((u_int16_t *) (&adv_mcode[word])); + } + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_CODE_CHK_SUM, code_sum); + + /* + * Read microcode version and date. + */ + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_VERSION_DATE, sc->cfg.mcode_date); + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_VERSION_NUM, sc->cfg.mcode_version); + + /* + * Initialize microcode operating variables + */ + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_ADAPTER_SCSI_ID, + sc->chip_scsi_id); + + /* + * If the PCI Configuration Command Register "Parity Error Response + * Control" Bit was clear (0), then set the microcode variable + * 'control_flag' CONTROL_FLAG_IGNORE_PERR flag to tell the microcode + * to ignore DMA parity errors. + */ + if (sc->cfg.control_flag & CONTROL_FLAG_IGNORE_PERR) { + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_CONTROL_FLAG, word); + word |= CONTROL_FLAG_IGNORE_PERR; + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_CONTROL_FLAG, word); + } + + /* + * Set default microcode operating variables for WDTR, SDTR, and + * command tag queuing based on the EEPROM configuration values. + * + * These ADW_DVC_VAR fields and the microcode variables will be + * changed in AdvInquiryHandling() if it is found a device is + * incapable of a particular feature. + */ + + /* + * Set the microcode ULTRA target mask from EEPROM value. The + * SDTR target mask overrides the ULTRA target mask in the + * microcode so it is safe to set this value without determining + * whether the device supports SDTR. + * + * Note: There is no way to know whether a device supports ULTRA + * speed without attempting a SDTR ULTRA speed negotiation with + * the device. The device will reject the speed if it does not + * support it by responding with an SDTR message containing a + * slower speed. + */ + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_ULTRA_ABLE, sc->ultra_able); + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_DISC_ENABLE, sc->cfg.disc_enable); + + + /* + * Set SCSI_CFG0 Microcode Default Value. + * + * The microcode will set the SCSI_CFG0 register using this value + * after it is started below. + */ + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_DEFAULT_SCSI_CFG0, + ADW_PARITY_EN | ADW_SEL_TMO_LONG | ADW_OUR_ID_EN | sc->chip_scsi_id); + + /* + * Determine SCSI_CFG1 Microcode Default Value. + * + * The microcode will set the SCSI_CFG1 register using this value + * after it is started below. + */ + + /* Read current SCSI_CFG1 Register value. */ + scsi_cfg1 = ADW_READ_WORD_REGISTER(iot, ioh, IOPW_SCSI_CFG1); + + /* + * If all three connectors are in use, return an error. + */ + if ((scsi_cfg1 & CABLE_ILLEGAL_A) == 0 || + (scsi_cfg1 & CABLE_ILLEGAL_B) == 0) { + return ASC_IERR_ILLEGAL_CONNECTION; + } + + /* + * If the internal narrow cable is reversed all of the SCSI_CTRL + * register signals will be set. Check for and return an error if + * this condition is found. + */ + if ((ADW_READ_WORD_REGISTER(iot, ioh, IOPW_SCSI_CTRL) & 0x3F07) == 0x3F07) + return ASC_IERR_REVERSED_CABLE; + + /* + * If this is a differential board and a single-ended device + * is attached to one of the connectors, return an error. + */ + if ((scsi_cfg1 & ADW_DIFF_MODE) && (scsi_cfg1 & ADW_DIFF_SENSE) == 0) + return ASC_IERR_SINGLE_END_DEVICE; + + /* + * If automatic termination control is enabled, then set the + * termination value based on a table listed in advlib.h. + * + * If manual termination was specified with an EEPROM setting + * then 'termination' was set-up in AdvInitFromEEP() and + * is ready to be 'ored' into SCSI_CFG1. + */ + if (sc->cfg.termination == 0) { + /* + * The software always controls termination by setting ADW_TERM_CTL_SEL. + * If ADW_TERM_CTL_SEL were set to 0, the hardware would set termination. + */ + sc->cfg.termination |= ADW_TERM_CTL_SEL; + + switch(scsi_cfg1 & ADW_CABLE_DETECT) + { + /* ADW_TERM_CTL_H: on, ADW_TERM_CTL_L: on */ + case 0x3: case 0x7: case 0xB: case 0xD: case 0xE: case 0xF: + sc->cfg.termination |= (ADW_TERM_CTL_H | ADW_TERM_CTL_L); + break; + + /* ADW_TERM_CTL_H: on, ADW_TERM_CTL_L: off */ + case 0x1: case 0x5: case 0x9: case 0xA: case 0xC: + sc->cfg.termination |= ADW_TERM_CTL_H; + break; + + /* ADW_TERM_CTL_H: off, ADW_TERM_CTL_L: off */ + case 0x2: case 0x6: + break; + } + } + + /* + * Clear any set ADW_TERM_CTL_H and ADW_TERM_CTL_L bits. + */ + scsi_cfg1 &= ~ADW_TERM_CTL; + + /* + * Invert the ADW_TERM_CTL_H and ADW_TERM_CTL_L bits and then + * set 'scsi_cfg1'. The ADW_TERM_POL bit does not need to be + * referenced, because the hardware internally inverts + * the Termination High and Low bits if ADW_TERM_POL is set. + */ + scsi_cfg1 |= (ADW_TERM_CTL_SEL | (~sc->cfg.termination & ADW_TERM_CTL)); + + /* + * Set SCSI_CFG1 Microcode Default Value + * + * Set filter value and possibly modified termination control + * bits in the Microcode SCSI_CFG1 Register Value. + * + * The microcode will set the SCSI_CFG1 register using this value + * after it is started below. + */ + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_DEFAULT_SCSI_CFG1, + ADW_FLTR_11_TO_20NS | scsi_cfg1); + + /* + * Set SEL_MASK Microcode Default Value + * + * The microcode will set the SEL_MASK register using this value + * after it is started below. + */ + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_DEFAULT_SEL_MASK, + ADW_TID_TO_TIDMASK(sc->chip_scsi_id)); + + /* + * Link all the RISC Queue Lists together in a doubly-linked + * NULL terminated list. + * + * Skip the NULL (0) queue which is not used. + */ + for (i = 1, rql_addr = ASC_MC_RISC_Q_LIST_BASE + ASC_MC_RISC_Q_LIST_SIZE; + i < ASC_MC_RISC_Q_TOTAL_CNT; + i++, rql_addr += ASC_MC_RISC_Q_LIST_SIZE) { + /* + * Set the current RISC Queue List's RQL_FWD and RQL_BWD pointers + * in a one word write and set the state (RQL_STATE) to free. + */ + ADW_WRITE_WORD_LRAM(iot, ioh, rql_addr, ((i + 1) + ((i - 1) << 8))); + ADW_WRITE_BYTE_LRAM(iot, ioh, rql_addr + RQL_STATE, ASC_MC_QS_FREE); + } + + /* + * Set the Host and RISC Queue List pointers. + * + * Both sets of pointers are initialized with the same values: + * ASC_MC_RISC_Q_FIRST(0x01) and ASC_MC_RISC_Q_LAST (0xFF). + */ + ADW_WRITE_BYTE_LRAM(iot, ioh, ASC_MC_HOST_NEXT_READY, ASC_MC_RISC_Q_FIRST); + ADW_WRITE_BYTE_LRAM(iot, ioh, ASC_MC_HOST_NEXT_DONE, ASC_MC_RISC_Q_LAST); + + ADW_WRITE_BYTE_LRAM(iot, ioh, ASC_MC_RISC_NEXT_READY, ASC_MC_RISC_Q_FIRST); + ADW_WRITE_BYTE_LRAM(iot, ioh, ASC_MC_RISC_NEXT_DONE, ASC_MC_RISC_Q_LAST); + + /* + * Finally, set up the last RISC Queue List (255) with + * a NULL forward pointer. + */ + ADW_WRITE_WORD_LRAM(iot, ioh, rql_addr, (ASC_MC_NULL_Q + ((i - 1) << 8))); + ADW_WRITE_BYTE_LRAM(iot, ioh, rql_addr + RQL_STATE, ASC_MC_QS_FREE); + + ADW_WRITE_BYTE_REGISTER(iot, ioh, IOPB_INTR_ENABLES, + (ADW_INTR_ENABLE_HOST_INTR | ADW_INTR_ENABLE_GLOBAL_INTR)); + + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_CODE_BEGIN_ADDR, word); + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_PC, word); + + /* finally, finally, gentlemen, start your engine */ + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_RISC_CSR, ADW_RISC_CSR_RUN); + + return warn_code; +} + +/* + * Read the board's EEPROM configuration. Set fields in ADW_SOFTC and + * ADW_DVC_CFG based on the EEPROM settings. The chip is stopped while + * all of this is done. + * + * On failure set the ADW_DVC_VAR field 'err_code' and return ADW_ERROR. + * + * For a non-fatal error return a warning code. If there are no warnings + * then 0 is returned. + * + * Note: Chip is stopped on entry. + */ +int +AdvInitFromEEP(sc) + ADW_SOFTC *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_int16_t warn_code; + ADWEEP_CONFIG eep_config; + int eep_chksum, i; + + + warn_code = 0; + + /* + * Read the board's EEPROM configuration. + * + * Set default values if a bad checksum is found. + */ + eep_chksum = AdvGetEEPConfig(iot, ioh, &eep_config); + + if (eep_chksum != eep_config.check_sum) + { + warn_code |= ASC_WARN_EEPROM_CHKSUM; + + /* + * Set EEPROM default values. + */ + for (i = 0; i < sizeof(ADWEEP_CONFIG); i++) { + *((u_int8_t *) &eep_config + i) = + *((u_int8_t *) &Default_EEPROM_Config + i); + } + + /* + * Assume the 6 byte board serial number that was read + * from EEPROM is correct even if the EEPROM checksum + * failed. + */ + eep_config.serial_number_word3 = + AdvReadEEPWord(iot, ioh, ASC_EEP_DVC_CFG_END - 1); + eep_config.serial_number_word2 = + AdvReadEEPWord(iot, ioh, ASC_EEP_DVC_CFG_END - 2); + eep_config.serial_number_word1 = + AdvReadEEPWord(iot, ioh, ASC_EEP_DVC_CFG_END - 3); + AdvSetEEPConfig(iot, ioh, &eep_config); + } + + /* + * Set ADW_DVC_VAR and ADW_DVC_CFG variables from the + * EEPROM configuration that was read. + * + * This is the mapping of EEPROM fields to Adv Library fields. + */ + sc->wdtr_able = eep_config.wdtr_able; + sc->sdtr_able = eep_config.sdtr_able; + sc->ultra_able = eep_config.ultra_able; + sc->tagqng_able = eep_config.tagqng_able; + sc->cfg.disc_enable = eep_config.disc_enable; + sc->max_host_qng = eep_config.max_host_qng; + sc->max_dvc_qng = eep_config.max_dvc_qng; + sc->chip_scsi_id = (eep_config.adapter_scsi_id & ADW_MAX_TID); + sc->start_motor = eep_config.start_motor; + sc->scsi_reset_wait = eep_config.scsi_reset_delay; + sc->cfg.bios_boot_wait = eep_config.bios_boot_delay; + sc->bios_ctrl = eep_config.bios_ctrl; + sc->no_scam = eep_config.scam_tolerant; + sc->cfg.serial1 = eep_config.serial_number_word1; + sc->cfg.serial2 = eep_config.serial_number_word2; + sc->cfg.serial3 = eep_config.serial_number_word3; + + /* + * Set the host maximum queuing (max. 253, min. 16) and the per device + * maximum queuing (max. 63, min. 4). + */ + if (eep_config.max_host_qng > ASC_DEF_MAX_HOST_QNG) { + eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG; + } else if (eep_config.max_host_qng < ASC_DEF_MIN_HOST_QNG) { + /* If the value is zero, assume it is uninitialized. */ + if (eep_config.max_host_qng == 0) { + eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG; + } else { + eep_config.max_host_qng = ASC_DEF_MIN_HOST_QNG; + } + } + + if (eep_config.max_dvc_qng > ASC_DEF_MAX_DVC_QNG) + { + eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG; + } else if (eep_config.max_dvc_qng < ASC_DEF_MIN_DVC_QNG) + { + /* If the value is zero, assume it is uninitialized. */ + if (eep_config.max_dvc_qng == 0) + { + eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG; + } else + { + eep_config.max_dvc_qng = ASC_DEF_MIN_DVC_QNG; + } + } + + /* + * If 'max_dvc_qng' is greater than 'max_host_qng', then + * set 'max_dvc_qng' to 'max_host_qng'. + */ + if (eep_config.max_dvc_qng > eep_config.max_host_qng) + { + eep_config.max_dvc_qng = eep_config.max_host_qng; + } + + /* + * Set ADW_DVC_VAR 'max_host_qng' and ADW_DVC_CFG 'max_dvc_qng' + * values based on possibly adjusted EEPROM values. + */ + sc->max_host_qng = eep_config.max_host_qng; + sc->max_dvc_qng = eep_config.max_dvc_qng; + + + /* + * If the EEPROM 'termination' field is set to automatic (0), then set + * the ADW_DVC_CFG 'termination' field to automatic also. + * + * If the termination is specified with a non-zero 'termination' + * value check that a legal value is set and set the ADW_DVC_CFG + * 'termination' field appropriately. + */ + if (eep_config.termination == 0) + { + sc->cfg.termination = 0; /* auto termination */ + } else + { + /* Enable manual control with low off / high off. */ + if (eep_config.termination == 1) + { + sc->cfg.termination = ADW_TERM_CTL_SEL; + + /* Enable manual control with low off / high on. */ + } else if (eep_config.termination == 2) + { + sc->cfg.termination = ADW_TERM_CTL_SEL | ADW_TERM_CTL_H; + + /* Enable manual control with low on / high on. */ + } else if (eep_config.termination == 3) + { + sc->cfg.termination = ADW_TERM_CTL_SEL | ADW_TERM_CTL_H | ADW_TERM_CTL_L; + } else + { + /* + * The EEPROM 'termination' field contains a bad value. Use + * automatic termination instead. + */ + sc->cfg.termination = 0; + warn_code |= ASC_WARN_EEPROM_TERMINATION; + } + } + + return warn_code; +} + +/* + * Read EEPROM configuration into the specified buffer. + * + * Return a checksum based on the EEPROM configuration read. + */ +static u_int16_t +AdvGetEEPConfig(iot, ioh, cfg_buf) + bus_space_tag_t iot; + bus_space_handle_t ioh; + ADWEEP_CONFIG *cfg_buf; +{ + u_int16_t wval, chksum; + u_int16_t *wbuf; + int eep_addr; + + wbuf = (u_int16_t *) cfg_buf; + chksum = 0; + + for (eep_addr = ASC_EEP_DVC_CFG_BEGIN; + eep_addr < ASC_EEP_DVC_CFG_END; + eep_addr++, wbuf++) + { + wval = AdvReadEEPWord(iot, ioh, eep_addr); + chksum += wval; + *wbuf = wval; + } + *wbuf = AdvReadEEPWord(iot, ioh, eep_addr); + wbuf++; + for (eep_addr = ASC_EEP_DVC_CTL_BEGIN; + eep_addr < ASC_EEP_MAX_WORD_ADDR; + eep_addr++, wbuf++) + { + *wbuf = AdvReadEEPWord(iot, ioh, eep_addr); + } + return chksum; +} + +/* + * Read the EEPROM from specified location + */ +static u_int16_t +AdvReadEEPWord(iot, ioh, eep_word_addr) + bus_space_tag_t iot; + bus_space_handle_t ioh; + int eep_word_addr; +{ + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_EE_CMD, + ASC_EEP_CMD_READ | eep_word_addr); + AdvWaitEEPCmd(iot, iot); + return ADW_READ_WORD_REGISTER(iot, ioh, IOPW_EE_DATA); +} + +/* + * Wait for EEPROM command to complete + */ +static void +AdvWaitEEPCmd(iot, ioh) + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + DvcSleepMilliSecond(1); + + for(;;) + { + if (ADW_READ_WORD_REGISTER(iot, ioh, IOPW_EE_CMD) & + ASC_EEP_CMD_DONE) { + break; + } + + DvcSleepMilliSecond(1); + } + + return; +} + +/* + * Write the EEPROM from 'cfg_buf'. + */ +static void +AdvSetEEPConfig(iot, ioh, cfg_buf) + bus_space_tag_t iot; + bus_space_handle_t ioh; + ADWEEP_CONFIG *cfg_buf; +{ + u_int16_t *wbuf; + u_int16_t addr, chksum; + + wbuf = (u_int16_t *) cfg_buf; + chksum = 0; + + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_ABLE); + AdvWaitEEPCmd(iot, ioh); + + /* + * Write EEPROM from word 0 to word 15 + */ + for (addr = ASC_EEP_DVC_CFG_BEGIN; + addr < ASC_EEP_DVC_CFG_END; addr++, wbuf++) + { + chksum += *wbuf; + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_EE_DATA, *wbuf); + ADW_WRITE_WORD_REGISTER(iot, ioh, + IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr); + AdvWaitEEPCmd(iot, ioh); + DvcSleepMilliSecond(ASC_EEP_DELAY_MS); + } + + /* + * Write EEPROM checksum at word 18 + */ + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_EE_DATA, chksum); + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_EE_CMD, + ASC_EEP_CMD_WRITE | addr); + AdvWaitEEPCmd(iot, ioh); + wbuf++; /* skip over check_sum */ + + /* + * Write EEPROM OEM name at words 19 to 26 + */ + for (addr = ASC_EEP_DVC_CTL_BEGIN; + addr < ASC_EEP_MAX_WORD_ADDR; addr++, wbuf++) + { + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_EE_DATA, *wbuf); + ADW_WRITE_WORD_REGISTER(iot, ioh, + IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr); + AdvWaitEEPCmd(iot, ioh); + } + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_EE_CMD, + ASC_EEP_CMD_WRITE_DISABLE); + AdvWaitEEPCmd(iot, ioh); + return; +} + +/* + * This function resets the chip and SCSI bus + * + * It is up to the caller to add a delay to let the bus settle after + * calling this function. + * + * The SCSI_CFG0, SCSI_CFG1, and MEM_CFG registers are set-up in + * AdvInitAsc3550Driver(). Here when doing a write to one of these + * registers read first and then write. + * + * Note: A SCSI Bus Reset can not be done until after the EEPROM + * configuration is read to determine whether SCSI Bus Resets + * should be performed. + */ +void +AdvResetChip(iot, ioh) + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + u_int16_t word; + u_int8_t byte; + + + /* + * Reset Chip. + */ + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_CTRL_REG, ADW_CTRL_REG_CMD_RESET); + DvcSleepMilliSecond(100); + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_CTRL_REG, ADW_CTRL_REG_CMD_WR_IO_REG); + + /* + * Initialize Chip registers. + * + * Note: Don't remove the use of a temporary variable in the following + * code, otherwise the Microsoft C compiler will turn the following lines + * into a no-op. + */ + byte = ADW_READ_BYTE_REGISTER(iot, ioh, IOPB_MEM_CFG); + byte |= RAM_SZ_8KB; + ADW_WRITE_BYTE_REGISTER(iot, ioh, IOPB_MEM_CFG, byte); + + word = ADW_READ_WORD_REGISTER(iot, ioh, IOPW_SCSI_CFG1); + word &= ~BIG_ENDIAN; + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_SCSI_CFG1, word); + + /* + * Setting the START_CTL_EMFU 3:2 bits sets a FIFO threshold + * of 128 bytes. This register is only accessible to the host. + */ + ADW_WRITE_BYTE_REGISTER(iot, ioh, IOPB_DMA_CFG0, + START_CTL_EMFU | READ_CMD_MRM); +} + +/* + * Description: + * Send a SCSI request to the ASC3550 chip + * + * If there is no SG list for the request, set 'sg_entry_cnt' to 0. + * + * If 'sg_real_addr' is non-zero on entry, AscGetSGList() will not be + * called. It is assumed the caller has already initialized 'sg_real_addr'. + * + * Return: + * ADW_SUCCESS(1) - the request is in the mailbox + * ADW_BUSY(0) - total request count > 253, try later + * ADW_ERROR(-1) - invalid scsi request Q + */ +int +AdvExeScsiQueue(sc, scsiq) + ADW_SOFTC *sc; + ADW_SCSI_REQ_Q *scsiq; +{ + return AdvSendScsiCmd(sc, scsiq); +} + +/* + * Reset SCSI Bus and purge all outstanding requests. + * + * Return Value: + * ADW_TRUE(1) - All requests are purged and SCSI Bus is reset. + * + * Note: Should always return ADW_TRUE. + */ +int +AdvResetCCB(sc) + ADW_SOFTC *sc; +{ + int status; + + status = AdvSendIdleCmd(sc, (u_int16_t) IDLE_CMD_SCSI_RESET, 0L, 0); + + AdvResetSCSIBus(sc); + + return status; +} + +/* + * Reset SCSI Bus and delay. + */ +void +AdvResetSCSIBus(sc) + ADW_SOFTC *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_int16_t scsi_ctrl; + + + + /* + * The microcode currently sets the SCSI Bus Reset signal while + * handling the AdvSendIdleCmd() IDLE_CMD_SCSI_RESET command above. + * But the SCSI Bus Reset Hold Time in the microcode is not deterministic + * (it may in fact be for less than the SCSI Spec. minimum of 25 us). + * Therefore on return the Adv Library sets the SCSI Bus Reset signal + * for ASC_SCSI_RESET_HOLD_TIME_US, which is defined to be greater + * than 25 us. + */ + scsi_ctrl = ADW_READ_WORD_REGISTER(iot, ioh, IOPW_SCSI_CTRL); + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_SCSI_CTRL, + scsi_ctrl | ADW_SCSI_CTRL_RSTOUT); + DvcDelayMicroSecond( (u_int16_t) ASC_SCSI_RESET_HOLD_TIME_US); + ADW_WRITE_WORD_REGISTER(iot, ioh, IOPW_SCSI_CTRL, + scsi_ctrl & ~ADW_SCSI_CTRL_RSTOUT); + + DvcSleepMilliSecond((ulong) sc->scsi_reset_wait * 1000); +} + + +/* + * Adv Library Interrupt Service Routine + * + * This function is called by a driver's interrupt service routine. + * The function disables and re-enables interrupts. + * + * When a microcode idle command is completed, the ADW_DVC_VAR + * 'idle_cmd_done' field is set to ADW_TRUE. + * + * Note: AdvISR() can be called when interrupts are disabled or even + * when there is no hardware interrupt condition present. It will + * always check for completed idle commands and microcode requests. + * This is an important feature that shouldn't be changed because it + * allows commands to be completed from polling mode loops. + * + * Return: + * ADW_TRUE(1) - interrupt was pending + * ADW_FALSE(0) - no interrupt was pending + */ +int +AdvISR(sc) + ADW_SOFTC *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_int8_t int_stat; + u_int16_t next_done_loc, target_bit; + int completed_q; + ADW_SCSI_REQ_Q *scsiq; + ASC_REQ_SENSE *sense_data; + int ret; + + + ret = (ADW_IS_INT_PENDING(iot, ioh))? ADW_TRUE : ADW_FALSE; + + /* Reading the register clears the interrupt. */ + int_stat = ADW_READ_BYTE_REGISTER(iot, ioh, IOPB_INTR_STATUS_REG); + + if (int_stat & ADW_INTR_STATUS_INTRB) + { + sc->idle_cmd_done = ADW_TRUE; + } + + /* + * Notify the driver of a hardware detected SCSI Bus Reset. + */ + if (int_stat & ADW_INTR_STATUS_INTRC) + { + if (sc->sbreset_callback) + { + (*(ADW_SBRESET_CALLBACK) sc->sbreset_callback)(sc); + } + } + + /* + * ASC_MC_HOST_NEXT_DONE (0x129) is actually the last completed RISC + * Queue List request. Its forward pointer (RQL_FWD) points to the + * current completed RISC Queue List request. + */ + ADW_READ_BYTE_LRAM(iot, ioh, ASC_MC_HOST_NEXT_DONE, next_done_loc); + next_done_loc = ASC_MC_RISC_Q_LIST_BASE + + (next_done_loc * ASC_MC_RISC_Q_LIST_SIZE) + RQL_FWD; + + ADW_READ_BYTE_LRAM(iot, ioh, next_done_loc, completed_q); + + /* Loop until all completed Q's are processed. */ + while (completed_q != ASC_MC_NULL_Q) + { + ADW_WRITE_BYTE_LRAM(iot, ioh, ASC_MC_HOST_NEXT_DONE, + completed_q); + + next_done_loc = ASC_MC_RISC_Q_LIST_BASE + + (completed_q * ASC_MC_RISC_Q_LIST_SIZE); + + /* + * Read the ADW_SCSI_REQ_Q virtual address pointer from + * the RISC list entry. The microcode has changed the + * ADW_SCSI_REQ_Q physical address to its virtual address. + * + * Refer to comments at the end of AdvSendScsiCmd() for + * more information on the RISC list structure. + */ + { + ushort lsw, msw; + ADW_READ_WORD_LRAM(iot, ioh, + next_done_loc + RQL_PHYADDR, lsw); + ADW_READ_WORD_LRAM(iot, ioh, + next_done_loc + RQL_PHYADDR + 2, msw); + + scsiq = (ADW_SCSI_REQ_Q *) + (((u_int32_t) msw << 16) | lsw); + } + + target_bit = ADW_TID_TO_TIDMASK(scsiq->target_id); + + /* + * Clear request microcode control flag. + */ + scsiq->cntl = 0; + + /* + * Check Condition handling + */ + if ((scsiq->done_status == QD_WITH_ERROR) && + (scsiq->scsi_status == SS_CHK_CONDITION) && + (sense_data = (ASC_REQ_SENSE *) scsiq->vsense_addr) != 0 && + (scsiq->orig_sense_len - scsiq->sense_len) >= ASC_MIN_SENSE_LEN) + { + /* + * Command returned with a check condition and valid + * sense data. + */ + } + /* + * If the command that completed was a SCSI INQUIRY and + * LUN 0 was sent the command, then process the INQUIRY + * command information for the device. + */ + else if (scsiq->done_status == QD_NO_ERROR && + scsiq->cdb[0] == INQUIRY && + scsiq->target_lun == 0) + { + AdvInquiryHandling(sc, scsiq); + } + + + /* Change the RISC Queue List state to free. */ + ADW_WRITE_BYTE_LRAM(iot, ioh, + next_done_loc + RQL_STATE, ASC_MC_QS_FREE); + + /* Get the RISC Queue List forward pointer. */ + ADW_READ_BYTE_LRAM(iot, ioh, + next_done_loc + RQL_FWD, completed_q); + + /* + * Notify the driver of the completed request by passing + * the ADW_SCSI_REQ_Q pointer to its callback function. + */ + sc->cur_host_qng--; + scsiq->a_flag |= ADW_SCSIQ_DONE; + (*(ADW_ISR_CALLBACK) sc->isr_callback)(sc, scsiq); + /* + * Note: After the driver callback function is called, 'scsiq' + * can no longer be referenced. + * + * Fall through and continue processing other completed + * requests... + */ + } + return ret; +} + +/* + * Send an idle command to the chip and wait for completion. + * + * Interrupts do not have to be enabled on entry. + * + * Return Values: + * ADW_TRUE - command completed successfully + * ADW_FALSE - command failed + */ +int +AdvSendIdleCmd(sc, idle_cmd, idle_cmd_parameter, flags) + ADW_SOFTC *sc; + u_int16_t idle_cmd; + u_int32_t idle_cmd_parameter; + int flags; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_int32_t i; + int ret; + + sc->idle_cmd_done = 0; + + /* + * Write the idle command value after the idle command parameter + * has been written to avoid a race condition. If the order is not + * followed, the microcode may process the idle command before the + * parameters have been written to LRAM. + */ + ADW_WRITE_DWORD_LRAM(iot, ioh, ASC_MC_IDLE_PARA_STAT, + idle_cmd_parameter); + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_IDLE_CMD, idle_cmd); + + /* + * If the 'flags' argument contains the ADW_NOWAIT flag, then + * return with success. + */ + if (flags & ADW_NOWAIT) + return ADW_TRUE; + + for (i = 0; i < SCSI_WAIT_10_SEC * SCSI_MS_PER_SEC; i++) + { + /* + * 'idle_cmd_done' is set by AdvISR(). + */ + if (sc->idle_cmd_done) + break; + + DvcSleepMilliSecond(1); + + /* + * If interrupts were disabled on entry to AdvSendIdleCmd(), + * then they will still be disabled here. Call AdvISR() to + * check for the idle command completion. + */ + (void) AdvISR(sc); + } + + if (sc->idle_cmd_done == ADW_FALSE) + { + return ADW_FALSE; + } else + { + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_IDLE_PARA_STAT, ret); + return ret; + } +} + +/* + * Send the SCSI request block to the adapter + * + * Each of the 255 Adv Library/Microcode RISC Lists or mailboxes has the + * following structure: + * + * 0: RQL_FWD - RISC list forward pointer (1 byte) + * 1: RQL_BWD - RISC list backward pointer (1 byte) + * 2: RQL_STATE - RISC list state byte - free, ready, done, aborted (1 byte) + * 3: RQL_TID - request target id (1 byte) + * 4: RQL_PHYADDR - ADW_SCSI_REQ_Q physical pointer (4 bytes) + * + * Return: + * ADW_SUCCESS(1) - the request is in the mailbox + * ADW_BUSY(0) - total request count > 253, try later + */ +static int +AdvSendScsiCmd(sc, scsiq) + ADW_SOFTC *sc; + ADW_SCSI_REQ_Q *scsiq; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + ADW_CCB *ccb = (ADW_CCB *)scsiq->ccb_ptr; + u_int16_t next_ready_loc; + u_int8_t next_ready_loc_fwd; + long req_size; + u_int32_t q_phy_addr; + + + if (sc->cur_host_qng >= sc->max_host_qng) + { + return ADW_BUSY; + } else + { + sc->cur_host_qng++; + } + + /* + * Clear the ADW_SCSI_REQ_Q done flag. + */ + scsiq->a_flag &= ~ADW_SCSIQ_DONE; + + /* + * Save the original sense buffer length. + * + * After the request completes 'sense_len' will be set to the residual + * byte count of the Auto-Request Sense if a command returns CHECK + * CONDITION and the Sense Data is valid indicated by 'host_status' not + * being set to QHSTA_M_AUTO_REQ_SENSE_FAIL. To determine the valid + * Sense Data Length subtract 'sense_len' from 'orig_sense_len'. + */ + scsiq->orig_sense_len = scsiq->sense_len; + + ADW_READ_BYTE_LRAM(iot, ioh, ASC_MC_HOST_NEXT_READY, next_ready_loc); + next_ready_loc = ASC_MC_RISC_Q_LIST_BASE + + (next_ready_loc * ASC_MC_RISC_Q_LIST_SIZE); + + /* + * Write the physical address of the Q to the mailbox. + * We need to skip the first four bytes, because the microcode + * uses them internally for linking Q's together. + */ + req_size = sizeof(ADW_SCSI_REQ_Q); + q_phy_addr = sc->sc_dmamap_control->dm_segs[0].ds_addr + + ADW_CCB_OFF(ccb) + offsetof(struct adw_ccb, scsiq); + + scsiq->scsiq_ptr = scsiq; + + /* + * The RISC list structure, which 'next_ready_loc' is a pointer + * to in microcode LRAM, has the format detailed in the comment + * header for this function. + * + * Write the ADW_SCSI_REQ_Q physical pointer to 'next_ready_loc' request. + */ + ADW_WRITE_DWORD_LRAM(iot, ioh, next_ready_loc + RQL_PHYADDR, q_phy_addr); + + /* Write target_id to 'next_ready_loc' request. */ + ADW_WRITE_BYTE_LRAM(iot, ioh, next_ready_loc + RQL_TID, scsiq->target_id); + + /* + * Set the ASC_MC_HOST_NEXT_READY (0x128) microcode variable to + * the 'next_ready_loc' request forward pointer. + * + * Do this *before* changing the 'next_ready_loc' queue to QS_READY. + * After the state is changed to QS_READY 'RQL_FWD' will be changed + * by the microcode. + * + * NOTE: The temporary variable 'next_ready_loc_fwd' is required to + * prevent some compilers from optimizing out 'AdvReadByteLram()' if + * it were used as the 3rd argument to 'AdvWriteByteLram()'. + */ + ADW_READ_BYTE_LRAM(iot, ioh, next_ready_loc + RQL_FWD, + next_ready_loc_fwd); + ADW_WRITE_BYTE_LRAM(iot, ioh, ASC_MC_HOST_NEXT_READY, + next_ready_loc_fwd); + + /* + * Change the state of 'next_ready_loc' request from QS_FREE to + * QS_READY which will cause the microcode to pick it up and + * execute it. + * + * Can't reference 'next_ready_loc' after changing the request + * state to QS_READY. The microcode now owns the request. + */ + ADW_WRITE_BYTE_LRAM(iot, ioh, next_ready_loc + RQL_STATE, + ASC_MC_QS_READY); + + return ADW_SUCCESS; +} + +/* + * Inquiry Information Byte 7 Handling + * + * Handle SCSI Inquiry Command information for a device by setting + * microcode operating variables that affect WDTR, SDTR, and Tag + * Queuing. + */ +static void +AdvInquiryHandling(sc, scsiq) + ADW_SOFTC *sc; + ADW_SCSI_REQ_Q *scsiq; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + ASC_SCSI_INQUIRY *inq; + u_int16_t cfg_word; + u_int16_t tidmask; + u_int8_t tid; + + /* + * AdvInquiryHandling() requires up to INQUIRY information Byte 7 + * to be available. + * + * If less than 8 bytes of INQUIRY information were requested or less + * than 8 bytes were transferred, then return. cdb[4] is the request + * length and the ADW_SCSI_REQ_Q 'data_cnt' field is set by the + * microcode to the transfer residual count. + */ + if (scsiq->cdb[4] < 8 || (scsiq->cdb[4] - scsiq->data_cnt) < 8) + { + return; + } + + tid = scsiq->target_id; + inq = (ASC_SCSI_INQUIRY *) scsiq->vdata_addr; + + /* + * WDTR, SDTR, and Tag Queuing cannot be enabled for old devices. + */ + if (inq->byte3.rsp_data_fmt < 2 && inq->byte2.ansi_apr_ver < 2) + { + return; + } + else + { + /* + * INQUIRY Byte 7 Handling + * + * Use a device's INQUIRY byte 7 to determine whether it + * supports WDTR, SDTR, and Tag Queuing. If the feature + * is enabled in the EEPROM and the device supports the + * feature, then enable it in the microcode. + */ + + tidmask = ADW_TID_TO_TIDMASK(tid); + /* + * Wide Transfers + * + * If the EEPROM enabled WDTR for the device and the device + * supports wide bus (16 bit) transfers, then turn on the + * device's 'wdtr_able' bit and write the new value to the + * microcode. + */ + if ((sc->wdtr_able & tidmask) && inq->byte7.WBus16) + { + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_WDTR_ABLE, + cfg_word); + if ((cfg_word & tidmask) == 0) + { + cfg_word |= tidmask; + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_WDTR_ABLE, + cfg_word); + + /* + * Clear the microcode "WDTR negotiation" done indicator + * for the target to cause it to negotiate with the new + * setting set above. + */ + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_WDTR_DONE, + cfg_word); + cfg_word &= ~tidmask; + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_WDTR_DONE, + cfg_word); + } + } + + /* + * Synchronous Transfers + * + * If the EEPROM enabled SDTR for the device and the device + * supports synchronous transfers, then turn on the device's + * 'sdtr_able' bit. Write the new value to the microcode. + */ + if ((sc->sdtr_able & tidmask) && inq->byte7.Sync) + { + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_SDTR_ABLE, + cfg_word); + if ((cfg_word & tidmask) == 0) + { + cfg_word |= tidmask; + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_SDTR_ABLE, + cfg_word); + + /* + * Clear the microcode "SDTR negotiation" done indicator + * for the target to cause it to negotiate with the new + * setting set above. + */ + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_SDTR_DONE, + cfg_word); + cfg_word &= ~tidmask; + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_SDTR_DONE, + cfg_word); + } + } + + /* + * If the EEPROM enabled Tag Queuing for device and the + * device supports Tag Queuing, then turn on the device's + * 'tagqng_enable' bit in the microcode and set the microcode + * maximum command count to the ADW_DVC_VAR 'max_dvc_qng' + * value. + * + * Tag Queuing is disabled for the BIOS which runs in polled + * mode and would see no benefit from Tag Queuing. Also by + * disabling Tag Queuing in the BIOS devices with Tag Queuing + * bugs will at least work with the BIOS. + */ + if ((sc->tagqng_able & tidmask) && inq->byte7.CmdQue) + { + ADW_READ_WORD_LRAM(iot, ioh, ASC_MC_TAGQNG_ABLE, + cfg_word); + cfg_word |= tidmask; + ADW_WRITE_WORD_LRAM(iot, ioh, ASC_MC_TAGQNG_ABLE, + cfg_word); + ADW_WRITE_BYTE_LRAM(iot, ioh, + ASC_MC_NUMBER_OF_MAX_CMD + tid, + sc->max_dvc_qng); + } + } +} + +static void +DvcSleepMilliSecond(n) + ulong n; +{ + + DELAY(n * 1000); +} + +static void +DvcDelayMicroSecond(n) + ulong n; +{ + + DELAY(n); +} diff --git a/sys/dev/ic/adwlib.h b/sys/dev/ic/adwlib.h new file mode 100644 index 000000000000..a43a87fcdd98 --- /dev/null +++ b/sys/dev/ic/adwlib.h @@ -0,0 +1,1090 @@ +/* $NetBSD: adwlib.h,v 1.1 1998/09/26 16:10:42 dante Exp $ */ + +/* + * Definitions for low level routines and data structures + * for the Advanced Systems Inc. SCSI controllers chips. + * + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Baldassare Dante Profeta + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +/* + * 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. + */ + +#ifndef _ADVANSYS_WIDE_LIBRARY_H_ +#define _ADVANSYS_WIDE_LIBRARY_H_ + + +/* + * --- Adv Library Constants and Macros + */ + +#define ADW_LIB_VERSION_MAJOR 3 +#define ADW_LIB_VERSION_MINOR 45 + +/* + * Define Adv Reset Hold Time grater than 25 uSec. + * See AdvResetSCSIBus() for more info. + */ +#define ASC_SCSI_RESET_HOLD_TIME_US 60 + +/* + * Define Adv EEPROM constants. + */ + +#define ASC_EEP_DVC_CFG_BEGIN (0x00) +#define ASC_EEP_DVC_CFG_END (0x15) +#define ASC_EEP_DVC_CTL_BEGIN (0x16) /* location of OEM name */ +#define ASC_EEP_MAX_WORD_ADDR (0x1E) + +#define ASC_EEP_DELAY_MS 100 + +/* + * EEPROM bits reference by the RISC after initialization. + */ +#define ADW_EEPROM_BIG_ENDIAN 0x8000 /* EEPROM Bit 15 */ +#define ADW_EEPROM_BIOS_ENABLE 0x4000 /* EEPROM Bit 14 */ +#define ADW_EEPROM_TERM_POL 0x2000 /* EEPROM Bit 13 */ + +/* + * EEPROM configuration format + * + * Field naming convention: + * + * *_enable indicates the field enables or disables the feature. The + * value is never reset. + * + * *_able indicates both whether a feature should be enabled or disabled + * and whether a device isi capable of the feature. At initialization + * this field may be set, but later if a device is found to be incapable + * of the feature, the field is cleared. + * + * Default values are maintained in a_init.c in the structure + * Default_EEPROM_Config. + */ +typedef struct adweep_config +{ + /* Word Offset, Description */ + + u_int16_t cfg_lsw; /* 00 power up initialization */ + /* bit 13 set - Term Polarity Control */ + /* bit 14 set - BIOS Enable */ + /* bit 15 set - Big Endian Mode */ + u_int16_t cfg_msw; /* 01 unused */ + u_int16_t disc_enable; /* 02 disconnect enable */ + u_int16_t wdtr_able; /* 03 Wide DTR able */ + u_int16_t sdtr_able; /* 04 Synchronous DTR able */ + u_int16_t start_motor; /* 05 send start up motor */ + u_int16_t tagqng_able; /* 06 tag queuing able */ + u_int16_t bios_scan; /* 07 BIOS device control */ + u_int16_t scam_tolerant; /* 08 no scam */ + + u_int8_t adapter_scsi_id; /* 09 Host Adapter ID */ + u_int8_t bios_boot_delay; /* power up wait */ + + u_int8_t scsi_reset_delay; /* 10 reset delay */ + u_int8_t bios_id_lun; /* first boot device scsi id & lun */ + /* high nibble is lun */ + /* low nibble is scsi id */ + + u_int8_t termination; /* 11 0 - automatic */ + /* 1 - low off / high off */ + /* 2 - low off / high on */ + /* 3 - low on / high on */ + /* There is no low on / high off */ + + u_int8_t reserved1; /* reserved byte (not used) */ + + u_int16_t bios_ctrl; /* 12 BIOS control bits */ + /* bit 0 set: BIOS don't act as initiator. */ + /* bit 1 set: BIOS > 1 GB support */ + /* bit 2 set: BIOS > 2 Disk Support */ + /* bit 3 set: BIOS don't support removables */ + /* bit 4 set: BIOS support bootable CD */ + /* bit 5 set: */ + /* bit 6 set: BIOS support multiple LUNs */ + /* bit 7 set: BIOS display of message */ + /* bit 8 set: */ + /* bit 9 set: Reset SCSI bus during init. */ + /* bit 10 set: */ + /* bit 11 set: No verbose initialization. */ + /* bit 12 set: SCSI parity enabled */ + /* bit 13 set: */ + /* bit 14 set: */ + /* bit 15 set: */ + u_int16_t ultra_able; /* 13 ULTRA speed able */ + u_int16_t reserved2; /* 14 reserved */ + u_int8_t max_host_qng; /* 15 maximum host queuing */ + u_int8_t max_dvc_qng; /* maximum per device queuing */ + u_int16_t dvc_cntl; /* 16 control bit for driver */ + u_int16_t bug_fix; /* 17 control bit for bug fix */ + u_int16_t serial_number_word1; /* 18 Board serial number word 1 */ + u_int16_t serial_number_word2; /* 19 Board serial number word 2 */ + u_int16_t serial_number_word3; /* 20 Board serial number word 3 */ + u_int16_t check_sum; /* 21 EEP check sum */ + u_int8_t oem_name[16]; /* 22 OEM name */ + u_int16_t dvc_err_code; /* 30 last device driver error code */ + u_int16_t adv_err_code; /* 31 last uc and Adv Lib error code */ + u_int16_t adv_err_addr; /* 32 last uc error address */ + u_int16_t saved_dvc_err_code; /* 33 saved last dev. driver error code */ + u_int16_t saved_adv_err_code; /* 34 saved last uc and Adv Lib error code */ + u_int16_t saved_adv_err_addr; /* 35 saved last uc error address */ + u_int16_t num_of_err; /* 36 number of error */ +} ADWEEP_CONFIG; + +/* + * EEPROM Commands + */ +#define ASC_EEP_CMD_READ 0x80 +#define ASC_EEP_CMD_WRITE 0x40 +#define ASC_EEP_CMD_WRITE_ABLE 0x30 +#define ASC_EEP_CMD_WRITE_DISABLE 0x00 + +#define ASC_EEP_CMD_DONE 0x0200 +#define ASC_EEP_CMD_DONE_ERR 0x0001 + +/* cfg_word */ +#define EEP_CFG_WORD_BIG_ENDIAN 0x8000 + +/* bios_ctrl */ +#define BIOS_CTRL_BIOS 0x0001 +#define BIOS_CTRL_EXTENDED_XLAT 0x0002 +#define BIOS_CTRL_GT_2_DISK 0x0004 +#define BIOS_CTRL_BIOS_REMOVABLE 0x0008 +#define BIOS_CTRL_BOOTABLE_CD 0x0010 +#define BIOS_CTRL_MULTIPLE_LUN 0x0040 +#define BIOS_CTRL_DISPLAY_MSG 0x0080 +#define BIOS_CTRL_NO_SCAM 0x0100 +#define BIOS_CTRL_RESET_SCSI_BUS 0x0200 +#define BIOS_CTRL_INIT_VERBOSE 0x0800 +#define BIOS_CTRL_SCSI_PARITY 0x1000 + +/* + * ASC 3550 Internal Memory Size - 8KB + */ +#define ADW_CONDOR_MEMSIZE 0x2000 /* 8 KB Internal Memory */ + +/* + * ASC 3550 I/O Length - 64 bytes + */ +#define ADW_CONDOR_IOLEN 0x40 /* I/O Port Range in bytes */ + +/* + * Byte I/O register address from base of 'iop_base'. + */ +#define IOPB_INTR_STATUS_REG 0x00 +#define IOPB_CHIP_ID_1 0x01 +#define IOPB_INTR_ENABLES 0x02 +#define IOPB_CHIP_TYPE_REV 0x03 +#define IOPB_RES_ADDR_4 0x04 +#define IOPB_RES_ADDR_5 0x05 +#define IOPB_RAM_DATA 0x06 +#define IOPB_RES_ADDR_7 0x07 +#define IOPB_FLAG_REG 0x08 +#define IOPB_RES_ADDR_9 0x09 +#define IOPB_RISC_CSR 0x0A +#define IOPB_RES_ADDR_B 0x0B +#define IOPB_RES_ADDR_C 0x0C +#define IOPB_RES_ADDR_D 0x0D +#define IOPB_RES_ADDR_E 0x0E +#define IOPB_RES_ADDR_F 0x0F +#define IOPB_MEM_CFG 0x10 +#define IOPB_RES_ADDR_11 0x11 +#define IOPB_RES_ADDR_12 0x12 +#define IOPB_RES_ADDR_13 0x13 +#define IOPB_FLASH_PAGE 0x14 +#define IOPB_RES_ADDR_15 0x15 +#define IOPB_RES_ADDR_16 0x16 +#define IOPB_RES_ADDR_17 0x17 +#define IOPB_FLASH_DATA 0x18 +#define IOPB_RES_ADDR_19 0x19 +#define IOPB_RES_ADDR_1A 0x1A +#define IOPB_RES_ADDR_1B 0x1B +#define IOPB_RES_ADDR_1C 0x1C +#define IOPB_RES_ADDR_1D 0x1D +#define IOPB_RES_ADDR_1E 0x1E +#define IOPB_RES_ADDR_1F 0x1F +#define IOPB_DMA_CFG0 0x20 +#define IOPB_DMA_CFG1 0x21 +#define IOPB_TICKLE 0x22 +#define IOPB_DMA_REG_WR 0x23 +#define IOPB_SDMA_STATUS 0x24 +#define IOPB_SCSI_BYTE_CNT 0x25 +#define IOPB_HOST_BYTE_CNT 0x26 +#define IOPB_BYTE_LEFT_TO_XFER 0x27 +#define IOPB_BYTE_TO_XFER_0 0x28 +#define IOPB_BYTE_TO_XFER_1 0x29 +#define IOPB_BYTE_TO_XFER_2 0x2A +#define IOPB_BYTE_TO_XFER_3 0x2B +#define IOPB_ACC_GRP 0x2C +#define IOPB_RES_ADDR_2D 0x2D +#define IOPB_DEV_ID 0x2E +#define IOPB_RES_ADDR_2F 0x2F +#define IOPB_SCSI_DATA 0x30 +#define IOPB_RES_ADDR_31 0x31 +#define IOPB_RES_ADDR_32 0x32 +#define IOPB_SCSI_DATA_HSHK 0x33 +#define IOPB_SCSI_CTRL 0x34 +#define IOPB_RES_ADDR_35 0x35 +#define IOPB_RES_ADDR_36 0x36 +#define IOPB_RES_ADDR_37 0x37 +#define IOPB_RES_ADDR_38 0x38 +#define IOPB_RES_ADDR_39 0x39 +#define IOPB_RES_ADDR_3A 0x3A +#define IOPB_RES_ADDR_3B 0x3B +#define IOPB_RFIFO_CNT 0x3C +#define IOPB_RES_ADDR_3D 0x3D +#define IOPB_RES_ADDR_3E 0x3E +#define IOPB_RES_ADDR_3F 0x3F + +/* + * Word I/O register address from base of 'iop_base'. + */ +#define IOPW_CHIP_ID_0 0x00 /* CID0 */ +#define IOPW_CTRL_REG 0x02 /* CC */ +#define IOPW_RAM_ADDR 0x04 /* LA */ +#define IOPW_RAM_DATA 0x06 /* LD */ +#define IOPW_RES_ADDR_08 0x08 +#define IOPW_RISC_CSR 0x0A /* CSR */ +#define IOPW_SCSI_CFG0 0x0C /* CFG0 */ +#define IOPW_SCSI_CFG1 0x0E /* CFG1 */ +#define IOPW_RES_ADDR_10 0x10 +#define IOPW_SEL_MASK 0x12 /* SM */ +#define IOPW_RES_ADDR_14 0x14 +#define IOPW_FLASH_ADDR 0x16 /* FA */ +#define IOPW_RES_ADDR_18 0x18 +#define IOPW_EE_CMD 0x1A /* EC */ +#define IOPW_EE_DATA 0x1C /* ED */ +#define IOPW_SFIFO_CNT 0x1E /* SFC */ +#define IOPW_RES_ADDR_20 0x20 +#define IOPW_Q_BASE 0x22 /* QB */ +#define IOPW_QP 0x24 /* QP */ +#define IOPW_IX 0x26 /* IX */ +#define IOPW_SP 0x28 /* SP */ +#define IOPW_PC 0x2A /* PC */ +#define IOPW_RES_ADDR_2C 0x2C +#define IOPW_RES_ADDR_2E 0x2E +#define IOPW_SCSI_DATA 0x30 /* SD */ +#define IOPW_SCSI_DATA_HSHK 0x32 /* SDH */ +#define IOPW_SCSI_CTRL 0x34 /* SC */ +#define IOPW_HSHK_CFG 0x36 /* HCFG */ +#define IOPW_SXFR_STATUS 0x36 /* SXS */ +#define IOPW_SXFR_CNTL 0x38 /* SXL */ +#define IOPW_SXFR_CNTH 0x3A /* SXH */ +#define IOPW_RES_ADDR_3C 0x3C +#define IOPW_RFIFO_DATA 0x3E /* RFD */ + +/* + * Doubleword I/O register address from base of 'iop_base'. + */ +#define IOPDW_RES_ADDR_0 0x00 +#define IOPDW_RAM_DATA 0x04 +#define IOPDW_RES_ADDR_8 0x08 +#define IOPDW_RES_ADDR_C 0x0C +#define IOPDW_RES_ADDR_10 0x10 +#define IOPDW_RES_ADDR_14 0x14 +#define IOPDW_RES_ADDR_18 0x18 +#define IOPDW_RES_ADDR_1C 0x1C +#define IOPDW_SDMA_ADDR0 0x20 +#define IOPDW_SDMA_ADDR1 0x24 +#define IOPDW_SDMA_COUNT 0x28 +#define IOPDW_SDMA_ERROR 0x2C +#define IOPDW_RDMA_ADDR0 0x30 +#define IOPDW_RDMA_ADDR1 0x34 +#define IOPDW_RDMA_COUNT 0x38 +#define IOPDW_RDMA_ERROR 0x3C + +#define ADW_CHIP_ID_BYTE 0x25 +#define ADW_CHIP_ID_WORD 0x04C1 + +#define ADW_SC_SCSI_BUS_RESET 0x2000 + +#define ADW_INTR_ENABLE_HOST_INTR 0x01 +#define ADW_INTR_ENABLE_SEL_INTR 0x02 +#define ADW_INTR_ENABLE_DPR_INTR 0x04 +#define ADW_INTR_ENABLE_RTA_INTR 0x08 +#define ADW_INTR_ENABLE_RMA_INTR 0x10 +#define ADW_INTR_ENABLE_RST_INTR 0x20 +#define ADW_INTR_ENABLE_DPE_INTR 0x40 +#define ADW_INTR_ENABLE_GLOBAL_INTR 0x80 + +#define ADW_INTR_STATUS_INTRA 0x01 +#define ADW_INTR_STATUS_INTRB 0x02 +#define ADW_INTR_STATUS_INTRC 0x04 + +#define ADW_RISC_CSR_STOP (0x0000) +#define ADW_RISC_TEST_COND (0x2000) +#define ADW_RISC_CSR_RUN (0x4000) +#define ADW_RISC_CSR_SINGLE_STEP (0x8000) + +#define ADW_CTRL_REG_HOST_INTR 0x0100 +#define ADW_CTRL_REG_SEL_INTR 0x0200 +#define ADW_CTRL_REG_DPR_INTR 0x0400 +#define ADW_CTRL_REG_RTA_INTR 0x0800 +#define ADW_CTRL_REG_RMA_INTR 0x1000 +#define ADW_CTRL_REG_RES_BIT14 0x2000 +#define ADW_CTRL_REG_DPE_INTR 0x4000 +#define ADW_CTRL_REG_POWER_DONE 0x8000 +#define ADW_CTRL_REG_ANY_INTR 0xFF00 + +#define ADW_CTRL_REG_CMD_RESET 0x00C6 +#define ADW_CTRL_REG_CMD_WR_IO_REG 0x00C5 +#define ADW_CTRL_REG_CMD_RD_IO_REG 0x00C4 +#define ADW_CTRL_REG_CMD_WR_PCI_CFG_SPACE 0x00C3 +#define ADW_CTRL_REG_CMD_RD_PCI_CFG_SPACE 0x00C2 + +#define ADW_SCSI_CTRL_RSTOUT 0x2000 + +#define ADW_IS_INT_PENDING(iot, ioh) \ + (ADW_READ_WORD_REGISTER((iot), (ioh), IOPW_CTRL_REG) & ADW_CTRL_REG_HOST_INTR) + +/* + * SCSI_CFG0 Register bit definitions + */ +#define ADW_TIMER_MODEAB 0xC000 /* Watchdog, Second, and Select. Timer Ctrl. */ +#define ADW_PARITY_EN 0x2000 /* Enable SCSI Parity Error detection */ +#define ADW_EVEN_PARITY 0x1000 /* Select Even Parity */ +#define ADW_WD_LONG 0x0800 /* Watchdog Interval, 1: 57 min, 0: 13 sec */ +#define ADW_QUEUE_128 0x0400 /* Queue Size, 1: 128 byte, 0: 64 byte */ +#define ADW_PRIM_MODE 0x0100 /* Primitive SCSI mode */ +#define ADW_SCAM_EN 0x0080 /* Enable SCAM selection */ +#define ADW_SEL_TMO_LONG 0x0040 /* Sel/Resel Timeout, 1: 400 ms, 0: 1.6 ms */ +#define ADW_CFRM_ID 0x0020 /* SCAM id sel. confirm., 1: fast, 0: 6.4 ms */ +#define ADW_OUR_ID_EN 0x0010 /* Enable OUR_ID bits */ +#define ADW_OUR_ID 0x000F /* SCSI ID */ + +/* + * SCSI_CFG1 Register bit definitions + */ +#define ADW_BIG_ENDIAN 0x8000 /* Enable Big Endian Mode MIO:15, EEP:15 */ +#define ADW_TERM_POL 0x2000 /* Terminator Polarity Ctrl. MIO:13, EEP:13 */ +#define ADW_SLEW_RATE 0x1000 /* SCSI output buffer slew rate */ +#define ADW_FILTER_SEL 0x0C00 /* Filter Period Selection */ +#define ADW_FLTR_DISABLE 0x0000 /* Input Filtering Disabled */ +#define ADW_FLTR_11_TO_20NS 0x0800 /* Input Filtering 11ns to 20ns */ +#define ADW_FLTR_21_TO_39NS 0x0C00 /* Input Filtering 21ns to 39ns */ +#define ADW_ACTIVE_DBL 0x0200 /* Disable Active Negation */ +#define ADW_DIFF_MODE 0x0100 /* SCSI differential Mode (Read-Only) */ +#define ADW_DIFF_SENSE 0x0080 /* 1: No SE cables, 0: SE cable (Read-Only) */ +#define ADW_TERM_CTL_SEL 0x0040 /* Enable TERM_CTL_H and TERM_CTL_L */ +#define ADW_TERM_CTL 0x0030 /* External SCSI Termination Bits */ +#define ADW_TERM_CTL_H 0x0020 /* Enable External SCSI Upper Termination */ +#define ADW_TERM_CTL_L 0x0010 /* Enable External SCSI Lower Termination */ +#define ADW_CABLE_DETECT 0x000F /* External SCSI Cable Connection Status */ + +#define CABLE_ILLEGAL_A 0x7 + /* x 0 0 0 | on on | Illegal (all 3 connectors are used) */ + +#define CABLE_ILLEGAL_B 0xB + /* 0 x 0 0 | on on | Illegal (all 3 connectors are used) */ + +/* + The following table details the SCSI_CFG1 Termination Polarity, + Termination Control and Cable Detect bits. + + Cable Detect | Termination + Bit 3 2 1 0 | 5 4 | Notes + _____________|________|____________________ + 1 1 1 0 | on on | Internal wide only + 1 1 0 1 | on on | Internal narrow only + 1 0 1 1 | on on | External narrow only + 0 x 1 1 | on on | External wide only + 1 1 0 0 | on off| Internal wide and internal narrow + 1 0 1 0 | on off| Internal wide and external narrow + 0 x 1 0 | off off| Internal wide and external wide + 1 0 0 1 | on off| Internal narrow and external narrow + 0 x 0 1 | on off| Internal narrow and external wide + 1 1 1 1 | on on | No devices are attached + x 0 0 0 | on on | Illegal (all 3 connectors are used) + 0 x 0 0 | on on | Illegal (all 3 connectors are used) + + x means don't-care (either '0' or '1') + + If term_pol (bit 13) is '0' (active-low terminator enable), then: + 'on' is '0' and 'off' is '1'. + + If term_pol bit is '1' (meaning active-hi terminator enable), then: + 'on' is '1' and 'off' is '0'. + */ + +/* + * MEM_CFG Register bit definitions + */ +#define BIOS_EN 0x40 /* BIOS Enable MIO:14,EEP:14 */ +#define FAST_EE_CLK 0x20 /* Diagnostic Bit */ +#define RAM_SZ 0x1C /* Specify size of RAM to RISC */ +#define RAM_SZ_2KB 0x00 /* 2 KB */ +#define RAM_SZ_4KB 0x04 /* 4 KB */ +#define RAM_SZ_8KB 0x08 /* 8 KB */ +#define RAM_SZ_16KB 0x0C /* 16 KB */ +#define RAM_SZ_32KB 0x10 /* 32 KB */ +#define RAM_SZ_64KB 0x14 /* 64 KB */ + +/* + * DMA_CFG0 Register bit definitions + * + * This register is only accessible to the host. + */ +#define BC_THRESH_ENB 0x80 /* PCI DMA Start Conditions */ +#define FIFO_THRESH 0x70 /* PCI DMA FIFO Threshold */ +#define FIFO_THRESH_16B 0x00 /* 16 bytes */ +#define FIFO_THRESH_32B 0x20 /* 32 bytes */ +#define FIFO_THRESH_48B 0x30 /* 48 bytes */ +#define FIFO_THRESH_64B 0x40 /* 64 bytes */ +#define FIFO_THRESH_80B 0x50 /* 80 bytes (default) */ +#define FIFO_THRESH_96B 0x60 /* 96 bytes */ +#define FIFO_THRESH_112B 0x70 /* 112 bytes */ +#define START_CTL 0x0C /* DMA start conditions */ +#define START_CTL_TH 0x00 /* Wait threshold level (default) */ +#define START_CTL_ID 0x04 /* Wait SDMA/SBUS idle */ +#define START_CTL_THID 0x08 /* Wait threshold and SDMA/SBUS idle */ +#define START_CTL_EMFU 0x0C /* Wait SDMA FIFO empty/full */ +#define READ_CMD 0x03 /* Memory Read Method */ +#define READ_CMD_MR 0x00 /* Memory Read */ +#define READ_CMD_MRL 0x02 /* Memory Read Long */ +#define READ_CMD_MRM 0x03 /* Memory Read Multiple (default) */ + + +/* + * Adv Library Status Definitions + */ +#define ADW_TRUE 1 +#define ADW_FALSE 0 +#define ADW_NOERROR 1 +#define ADW_SUCCESS 1 +#define ADW_BUSY 0 +#define ADW_ERROR (-1) + + +/* + * ASC_DVC_VAR 'warn_code' values + */ +#define ASC_WARN_EEPROM_CHKSUM 0x0002 /* EEP check sum error */ +#define ASC_WARN_EEPROM_TERMINATION 0x0004 /* EEP termination bad field */ +#define ASC_WARN_SET_PCI_CONFIG_SPACE 0x0080 /* PCI config space set error */ +#define ASC_WARN_ERROR 0xFFFF /* ADW_ERROR return */ + +#define ADW_MAX_TID 15 /* max. target identifier */ +#define ADW_MAX_LUN 7 /* max. logical unit number */ + + +/* + * AscInitGetConfig() and AscInitAsc1000Driver() Definitions + * + * Error code values are set in ASC_DVC_VAR 'err_code'. + */ +#define ASC_IERR_WRITE_EEPROM 0x0001 /* write EEPROM error */ +#define ASC_IERR_MCODE_CHKSUM 0x0002 /* micro code check sum error */ +#define ASC_IERR_START_STOP_CHIP 0x0008 /* start/stop chip failed */ +#define ASC_IERR_CHIP_VERSION 0x0040 /* wrong chip version */ +#define ASC_IERR_SET_SCSI_ID 0x0080 /* set SCSI ID failed */ +#define ASC_IERR_BAD_SIGNATURE 0x0200 /* signature not found */ +#define ASC_IERR_ILLEGAL_CONNECTION 0x0400 /* Illegal cable connection */ +#define ASC_IERR_SINGLE_END_DEVICE 0x0800 /* Single-end used w/differential */ +#define ASC_IERR_REVERSED_CABLE 0x1000 /* Narrow flat cable reversed */ +#define ASC_IERR_RW_LRAM 0x8000 /* read/write local RAM error */ + +/* + * Fixed locations of microcode operating variables. + */ +#define ASC_MC_CODE_BEGIN_ADDR 0x0028 /* microcode start address */ +#define ASC_MC_CODE_END_ADDR 0x002A /* microcode end address */ +#define ASC_MC_CODE_CHK_SUM 0x002C /* microcode code checksum */ +#define ASC_MC_STACK_BEGIN 0x002E /* microcode stack begin */ +#define ASC_MC_STACK_END 0x0030 /* microcode stack end */ +#define ASC_MC_VERSION_DATE 0x0038 /* microcode version */ +#define ASC_MC_VERSION_NUM 0x003A /* microcode number */ +#define ASCV_VER_SERIAL_W 0x003C /* used in dos_init */ +#define ASC_MC_BIOSMEM 0x0040 /* BIOS RISC Memory Start */ +#define ASC_MC_BIOSLEN 0x0050 /* BIOS RISC Memory Length */ +#define ASC_MC_HALTCODE 0x0094 /* microcode halt code */ +#define ASC_MC_CALLERPC 0x0096 /* microcode halt caller PC */ +#define ASC_MC_ADAPTER_SCSI_ID 0x0098 /* one ID byte + reserved */ +#define ASC_MC_ULTRA_ABLE 0x009C +#define ASC_MC_SDTR_ABLE 0x009E /* Sync. Transfer TID bitmask. */ +#define ASC_MC_TAGQNG_ABLE 0x00A0 +#define ASC_MC_DISC_ENABLE 0x00A2 +#define ASC_MC_IDLE_CMD 0x00A6 +#define ASC_MC_IDLE_PARA_STAT 0x00A8 +#define ASC_MC_DEFAULT_SCSI_CFG0 0x00AC +#define ASC_MC_DEFAULT_SCSI_CFG1 0x00AE +#define ASC_MC_DEFAULT_MEM_CFG 0x00B0 +#define ASC_MC_DEFAULT_SEL_MASK 0x00B2 +#define ASC_MC_RISC_NEXT_READY 0x00B4 +#define ASC_MC_RISC_NEXT_DONE 0x00B5 +#define ASC_MC_SDTR_DONE 0x00B6 +#define ASC_MC_NUMBER_OF_QUEUED_CMD 0x00C0 +#define ASC_MC_NUMBER_OF_MAX_CMD 0x00D0 +#define ASC_MC_DEVICE_HSHK_CFG_TABLE 0x0100 +#define ASC_MC_WDTR_ABLE 0x0120 /* Wide Transfer TID bitmask. */ +#define ASC_MC_CONTROL_FLAG 0x0122 /* Microcode control flag. */ +#define ASC_MC_WDTR_DONE 0x0124 +#define ASC_MC_HOST_NEXT_READY 0x0128 /* Host Next Ready RQL Entry. */ +#define ASC_MC_HOST_NEXT_DONE 0x0129 /* Host Next Done RQL Entry. */ + +/* + * BIOS LRAM variable absolute offsets. + */ +#define BIOS_CODESEG 0x54 +#define BIOS_CODELEN 0x56 +#define BIOS_SIGNATURE 0x58 +#define BIOS_VERSION 0x5A +#define BIOS_SIGNATURE 0x58 + +/* + * Microcode Control Flags + * + * Flags set by the Adv Library in RISC variable 'control_flag' (0x122) + * and handled by the microcode. + */ +#define CONTROL_FLAG_IGNORE_PERR 0x0001 /* Ignore DMA Parity Errors */ + +/* + * ASC_MC_DEVICE_HSHK_CFG_TABLE microcode table or HSHK_CFG register format + */ +#define HSHK_CFG_WIDE_XFR 0x8000 +#define HSHK_CFG_RATE 0x0F00 +#define HSHK_CFG_OFFSET 0x001F + +/* + * LRAM RISC Queue Lists (LRAM addresses 0x1200 - 0x19FF) + * + * Each of the 255 Adv Library/Microcode RISC queue lists or mailboxes + * starting at LRAM address 0x1200 is 8 bytes and has the following + * structure. Only 253 of these are actually used for command queues. + */ + +#define ASC_MC_RISC_Q_LIST_BASE 0x1200 +#define ASC_MC_RISC_Q_LIST_SIZE 0x0008 +#define ASC_MC_RISC_Q_TOTAL_CNT 0x00FF /* Num. queue slots in LRAM. */ +#define ASC_MC_RISC_Q_FIRST 0x0001 +#define ASC_MC_RISC_Q_LAST 0x00FF + +#define ASC_DEF_MAX_HOST_QNG 0xFD /* Max. number of host commands (253) */ +#define ASC_DEF_MIN_HOST_QNG 0x10 /* Min. number of host commands (16) */ +#define ASC_DEF_MAX_DVC_QNG 0x3F /* Max. number commands per device (63) */ +#define ASC_DEF_MIN_DVC_QNG 0x04 /* Min. number commands per device (4) */ + +/* RISC Queue List structure - 8 bytes */ +#define RQL_FWD 0 /* forward pointer (1 byte) */ +#define RQL_BWD 1 /* backward pointer (1 byte) */ +#define RQL_STATE 2 /* state byte - free, ready, done, aborted (1 byte) */ +#define RQL_TID 3 /* request target id (1 byte) */ +#define RQL_PHYADDR 4 /* request physical pointer (4 bytes) */ + +/* RISC Queue List state values */ +#define ASC_MC_QS_FREE 0x00 +#define ASC_MC_QS_READY 0x01 +#define ASC_MC_QS_DONE 0x40 +#define ASC_MC_QS_ABORTED 0x80 + +/* RISC Queue List pointer values */ +#define ASC_MC_NULL_Q 0x00 /* NULL_Q == 0 */ +#define ASC_MC_BIOS_Q 0xFF /* BIOS_Q = 255 */ + +/* ASC_SCSI_REQ_Q 'cntl' field values */ +#define ASC_MC_QC_START_MOTOR 0x02 /* Issue start motor. */ +#define ASC_MC_QC_NO_OVERRUN 0x04 /* Don't report overrun. */ +#define ASC_MC_QC_FIRST_DMA 0x08 /* Internal microcode flag. */ +#define ASC_MC_QC_ABORTED 0x10 /* Request aborted by host. */ +#define ASC_MC_QC_REQ_SENSE 0x20 /* Auto-Request Sense. */ +#define ASC_MC_QC_DOS_REQ 0x80 /* Request issued by DOS. */ + + +/* + * ASC_SCSI_REQ_Q 'a_flag' definitions + * + * The Adv Library should limit use to the lower nibble (4 bits) of + * a_flag. Drivers are free to use the upper nibble (4 bits) of a_flag. + */ +#define ADW_POLL_REQUEST 0x01 /* poll for request completion */ +#define ADW_SCSIQ_DONE 0x02 /* request done */ + +/* + * Adapter temporary configuration structure + * + * This structure can be discarded after initialization. Don't add + * fields here needed after initialization. + * + * Field naming convention: + * + * *_enable indicates the field enables or disables a feature. The + * value of the field is never reset. + */ +typedef struct adw_dvc_cfg { + u_int16_t disc_enable; /* enable disconnection */ + u_int8_t chip_version; /* chip version */ + u_int8_t termination; /* Term. Ctrl. bits 6-5 of SCSI_CFG1 register */ + u_int16_t pci_device_id; /* PCI device code number */ + u_int16_t lib_version; /* Adv Library version number */ + u_int16_t control_flag; /* Microcode Control Flag */ + u_int16_t mcode_date; /* Microcode date */ + u_int16_t mcode_version; /* Microcode version */ + u_int16_t pci_slot_info; /* high byte device/function number */ + /* bits 7-3 device num., bits 2-0 function num. */ + /* low byte bus num. */ + u_int16_t bios_boot_wait; /* BIOS boot time delay */ + u_int16_t serial1; /* EEPROM serial number word 1 */ + u_int16_t serial2; /* EEPROM serial number word 2 */ + u_int16_t serial3; /* EEPROM serial number word 3 */ +} ADW_DVC_CFG; + +/* + * Adapter operation variable structure. + * + * One structure is required per host adapter. + * + * Field naming convention: + * + * *_able indicates both whether a feature should be enabled or disabled + * and whether a device is capable of the feature. At initialization + * this field may be set, but later if a device is found to be incapable + * of the feature, the field is cleared. + */ +typedef struct adw_softc { + + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmamap_control; /* maps the control structures */ + void *sc_ih; + + struct adw_control *sc_control; /* control structures */ + TAILQ_HEAD(, adw_ccb) sc_free_ccb, sc_waiting_ccb; + struct scsipi_link sc_link; /* prototype for devs */ + + LIST_HEAD(, scsipi_xfer) sc_queue; + struct scsipi_xfer *sc_queuelast; + + u_int32_t sc_flags; /* see below sc_flags values */ + + u_int16_t bios_ctrl; /* BIOS control word, EEPROM word 12 */ + ulong isr_callback; /* pointer to function, called in AdvISR() */ + ulong sbreset_callback; /* pointer to function, called in AdvISR() */ + u_int16_t wdtr_able; /* try WDTR for a device */ + u_int16_t sdtr_able; /* try SDTR for a device */ + u_int16_t ultra_able; /* try SDTR Ultra speed for a device */ + u_int16_t tagqng_able; /* try tagged queuing with a device */ + u_int8_t max_dvc_qng; /* maximum number of tagged commands per device */ + u_int16_t start_motor; /* start motor command allowed */ + u_int8_t scsi_reset_wait; /* delay in seconds after scsi bus reset */ + u_int8_t chip_no; /* should be assigned by caller */ + u_int8_t max_host_qng; /* maximum number of Q'ed command allowed */ + u_int8_t cur_host_qng; /* total number of queue command */ + u_int8_t irq_no; /* IRQ number */ + u_int16_t no_scam; /* scam_tolerant of EEPROM */ + u_int16_t idle_cmd_done; /* microcode idle command done set by AdvISR() */ + ulong drv_ptr; /* driver pointer to private structure */ + u_int8_t chip_scsi_id; /* chip SCSI target ID */ + /* + * Note: The following fields will not be used after initialization. The + * driver may discard the buffer after initialization is done. + */ + ADW_DVC_CFG cfg; /* temporary configuration structure */ +} ADW_SOFTC; + +/* sc_flags values */ +#define ADW_WIDE_BOARD 0x04 + + +#define ADW_IS_NARROW_BOARD(sc) (((sc)->sc_flags & ADW_WIDE_BOARD) == 0) +#define ADW_IS_WIDE_BOARD(sc) ((sc)->sc_flags & ADW_WIDE_BOARD) + + +#define NO_OF_SG_PER_BLOCK 15 + +typedef struct adw_sg_block { + u_int8_t reserved1; + u_int8_t reserved2; + u_int8_t first_entry_no; /* starting entry number */ + u_int8_t last_entry_no; /* last entry number */ + struct adw_sg_block *sg_ptr; /* links to the next sg block */ + struct { + u_int32_t sg_addr; /* SG element address */ + u_int32_t sg_count; /* SG element count */ + } sg_list[NO_OF_SG_PER_BLOCK]; +} ADW_SG_BLOCK; + +/* + * ADW_SCSI_REQ_Q - microcode request structure + * + * All fields in this structure up to byte 60 are used by the microcode. + * The microcode makes assumptions about the size and ordering of fields + * in this structure. Do not change the structure definition here without + * coordinating the change with the microcode. + */ +typedef struct adw_scsi_req_q { + u_int8_t cntl; /* Ucode flags and state (ASC_MC_QC_*). */ + u_int8_t sg_entry_cnt; /* SG element count. Zero for no SG. */ + u_int8_t target_id; /* Device target identifier. */ + u_int8_t target_lun; /* Device target logical unit number. */ + ulong data_addr; /* Data buffer physical address. */ + u_int32_t data_cnt; /* Data count. Ucode sets to residual. */ + ulong sense_addr; /* Sense buffer physical address. */ + ulong ccb_ptr; /* Driver request pointer. */ + u_int8_t a_flag; /* Adv Library flag field. */ + u_int8_t sense_len; /* Auto-sense length. Ucode sets to residual. */ + u_int8_t cdb_len; /* SCSI CDB length. */ + u_int8_t tag_code; /* SCSI-2 Tag Queue Code: 00, 20-22. */ + u_int8_t done_status; /* Completion status. */ + u_int8_t scsi_status; /* SCSI status byte. (see below) */ + u_int8_t host_status; /* Ucode host status. */ + u_int8_t ux_sg_ix; /* Ucode working SG variable. */ + u_int8_t cdb[12]; /* SCSI command block. */ + ulong sg_real_addr; /* SG list physical address. */ + struct adw_scsi_req_q *free_scsiq_link; + ulong ux_wk_data_cnt; /* Saved data count at disconnection. */ + struct adw_scsi_req_q *scsiq_ptr; + ADW_SG_BLOCK *sg_list_ptr; /* SG list virtual address. */ + /* + * End of microcode structure - 60 bytes. The rest of the structure + * is used by the Adv Library and ignored by the microcode. + */ + ulong vsense_addr; /* Sense buffer virtual address. */ + ulong vdata_addr; /* Data buffer virtual address. */ + u_int8_t orig_sense_len; /* Original length of sense buffer. */ + u_int8_t pads[3]; /* padding bytes (align to long) */ +} ADW_SCSI_REQ_Q; + +/* + * scsi_status conditions + */ +#define SS_GOOD 0x00 +#define SS_CHK_CONDITION 0x02 +#define SS_CONDITION_MET 0x04 +#define SS_TARGET_BUSY 0x08 +#define SS_INTERMID 0x10 +#define SS_INTERMID_COND_MET 0x14 +#define SS_RSERV_CONFLICT 0x18 +#define SS_CMD_TERMINATED 0x22 +#define SS_QUEUE_FULL 0x28 + +/* + * Microcode idle loop commands + */ +#define IDLE_CMD_COMPLETED 0 +#define IDLE_CMD_STOP_CHIP 0x0001 +#define IDLE_CMD_STOP_CHIP_SEND_INT 0x0002 +#define IDLE_CMD_SEND_INT 0x0004 +#define IDLE_CMD_ABORT 0x0008 +#define IDLE_CMD_DEVICE_RESET 0x0010 +#define IDLE_CMD_SCSI_RESET 0x0020 + +/* + * AdvSendIdleCmd() flag definitions. + */ +#define ADW_NOWAIT 0x01 + +/* + * Wait loop time out values. + */ +#define SCSI_WAIT_10_SEC 10 /* 10 seconds */ +#define SCSI_MS_PER_SEC 1000 /* milliseconds per second */ + + +/* Read byte from a register. */ +#define ADW_READ_BYTE_REGISTER(iot, ioh, reg_off) \ + bus_space_read_1((iot), (ioh), (reg_off)) + +/* Write byte to a register. */ +#define ADW_WRITE_BYTE_REGISTER(iot, ioh, reg_off, byte) \ + bus_space_write_1((iot), (ioh), (reg_off), (byte)) + +/* Read word (2 bytes) from a register. */ +#define ADW_READ_WORD_REGISTER(iot, ioh, reg_off) \ + bus_space_read_2((iot), (ioh), (reg_off)) + +/* Write word (2 bytes) to a register. */ +#define ADW_WRITE_WORD_REGISTER(iot, ioh, reg_off, word) \ + bus_space_write_2((iot), (ioh), (reg_off), (word)) + +/* Read byte from LRAM. */ +#define ADW_READ_BYTE_LRAM(iot, ioh, addr, byte) \ +do { \ + bus_space_write_2((iot), (ioh), IOPW_RAM_ADDR, (addr)); \ + (byte) = bus_space_read_1((iot), (ioh), IOPB_RAM_DATA); \ +} while (0) + +/* Write byte to LRAM. */ +#define ADW_WRITE_BYTE_LRAM(iot, ioh, addr, byte) \ +do { \ + bus_space_write_2((iot), (ioh), IOPW_RAM_ADDR, (addr)); \ + bus_space_write_1((iot), (ioh), IOPB_RAM_DATA, (byte)); \ +} while (0) + +/* Read word (2 bytes) from LRAM. */ +#define ADW_READ_WORD_LRAM(iot, ioh, addr, word) \ +do { \ + bus_space_write_2((iot), (ioh), IOPW_RAM_ADDR, (addr)); \ + (word) = bus_space_read_2((iot), (ioh), IOPW_RAM_DATA); \ +} while (0) + +/* Write word (2 bytes) to LRAM. */ +#define ADW_WRITE_WORD_LRAM(iot, ioh, addr, word) \ +do { \ + bus_space_write_2((iot), (ioh), IOPW_RAM_ADDR, (addr)); \ + bus_space_write_2((iot), (ioh), IOPW_RAM_DATA, (word)); \ +} while (0) + +/* Write double word (4 bytes) to LRAM */ +/* Because of unspecified C language ordering don't use auto-increment. */ +#define ADW_WRITE_DWORD_LRAM(iot, ioh, addr, dword) \ +do { \ + bus_space_write_2((iot), (ioh), IOPW_RAM_ADDR, (addr)); \ + bus_space_write_2((iot), (ioh), IOPW_RAM_DATA, \ + (ushort) ((dword) & 0xFFFF)); \ + bus_space_write_2((iot), (ioh), IOPW_RAM_ADDR, (addr) + 2); \ + bus_space_write_2((iot), (ioh), IOPW_RAM_DATA, \ + (ushort) ((dword >> 16) & 0xFFFF)); \ +} while (0) + +/* Read word (2 bytes) from LRAM assuming that the address is already set. */ +#define ADW_READ_WORD_AUTO_INC_LRAM(iot, ioh) \ + bus_space_read_2((iot), (ioh), IOPW_RAM_DATA) \ + +/* Write word (2 bytes) to LRAM assuming that the address is already set. */ +#define ADW_WRITE_WORD_AUTO_INC_LRAM(iot, ioh, word) \ + bus_space_write_2((iot), (ioh), IOPW_RAM_DATA, (word)) + +/* + * Define macro to check for Condor signature. + * + * Evaluate to ADW_TRUE if a Condor chip is found the specified port + * address 'iop_base'. Otherwise evalue to ADW_FALSE. + */ +#define ADW_FIND_SIGNATURE(iot, ioh) \ + (((ADW_READ_BYTE_REGISTER((iot), (ioh), IOPB_CHIP_ID_1) == \ + ADW_CHIP_ID_BYTE) && \ + (ADW_READ_WORD_REGISTER((iot), (ioh), IOPW_CHIP_ID_0) == \ + ADW_CHIP_ID_WORD)) ? ADW_TRUE : ADW_FALSE) + +/* + * Define macro to Return the version number of the chip at 'iop_base'. + * + * The second parameter 'bus_type' is currently unused. + */ +#define ADW_GET_CHIP_VERSION(iot, ioh, bus_type) \ + ADW_READ_BYTE_REGISTER((iot), (ioh), IOPB_CHIP_TYPE_REV) + +/* + * Abort an SRB in the chip's RISC Memory. The 'srb_ptr' argument must + * match the ASC_SCSI_REQ_Q 'srb_ptr' field. + * + * If the request has not yet been sent to the device it will simply be + * aborted from RISC memory. If the request is disconnected it will be + * aborted on reselection by sending an Abort Message to the target ID. + * + * Return value: + * ADW_TRUE(1) - Queue was successfully aborted. + * ADW_FALSE(0) - Queue was not found on the active queue list. + */ +#define ADW_ABORT_CCB(sc, ccb_ptr) \ + AdvSendIdleCmd((sc), (u_int16_t) IDLE_CMD_ABORT, \ + (ulong) (ccb_ptr), 0) + +/* + * Send a Bus Device Reset Message to the specified target ID. + * + * All outstanding commands will be purged if sending the + * Bus Device Reset Message is successful. + * + * Return Value: + * ADW_TRUE(1) - All requests on the target are purged. + * ADW_FALSE(0) - Couldn't issue Bus Device Reset Message; Requests + * are not purged. + */ +#define ADW_RESET_DEVICE(sc, target_id) \ + AdvSendIdleCmd((sc), (u_int16_t) IDLE_CMD_DEVICE_RESET, \ + (ulong) (target_id), 0) + +/* + * SCSI Wide Type definition. + */ +#define ADW_SCSI_BIT_ID_TYPE ushort + +/* + * AdvInitScsiTarget() 'cntl_flag' options. + */ +#define ADW_SCAN_LUN 0x01 +#define ADW_CAPINFO_NOLUN 0x02 + +/* + * Convert target id to target id bit mask. + */ +#define ADW_TID_TO_TIDMASK(tid) (0x01 << ((tid) & ADW_MAX_TID)) + +/* + * ASC_SCSI_REQ_Q 'done_status' and 'host_status' return values. + */ + +#define QD_NO_STATUS 0x00 /* Request not completed yet. */ +#define QD_NO_ERROR 0x01 +#define QD_ABORTED_BY_HOST 0x02 +#define QD_WITH_ERROR 0x04 + +#define QHSTA_NO_ERROR 0x00 +#define QHSTA_M_SEL_TIMEOUT 0x11 +#define QHSTA_M_DATA_OVER_RUN 0x12 +#define QHSTA_M_UNEXPECTED_BUS_FREE 0x13 +#define QHSTA_M_QUEUE_ABORTED 0x15 +#define QHSTA_M_SXFR_SDMA_ERR 0x16 /* SXFR_STATUS SCSI DMA Error */ +#define QHSTA_M_SXFR_SXFR_PERR 0x17 /* SXFR_STATUS SCSI Bus Parity Error */ +#define QHSTA_M_RDMA_PERR 0x18 /* RISC PCI DMA parity error */ +#define QHSTA_M_SXFR_OFF_UFLW 0x19 /* SXFR_STATUS Offset Underflow */ +#define QHSTA_M_SXFR_OFF_OFLW 0x20 /* SXFR_STATUS Offset Overflow */ +#define QHSTA_M_SXFR_WD_TMO 0x21 /* SXFR_STATUS Watchdog Timeout */ +#define QHSTA_M_SXFR_DESELECTED 0x22 /* SXFR_STATUS Deselected */ +/* Note: QHSTA_M_SXFR_XFR_OFLW is identical to QHSTA_M_DATA_OVER_RUN. */ +#define QHSTA_M_SXFR_XFR_OFLW 0x12 /* SXFR_STATUS Transfer Overflow */ +#define QHSTA_M_SXFR_XFR_PH_ERR 0x24 /* SXFR_STATUS Transfer Phase Error */ +#define QHSTA_M_SXFR_UNKNOWN_ERROR 0x25 /* SXFR_STATUS Unknown Error */ +#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_INVALID_DEVICE 0x45 /* Bad target ID */ + +/* + * SCSI Iquiry structure + */ + +typedef struct +{ + u_int8_t peri_dvc_type:5; + u_int8_t peri_qualifier:3; +} ASC_SCSI_INQ0; + +typedef struct +{ + u_int8_t dvc_type_modifier:7; + u_int8_t rmb:1; +} ASC_SCSI_INQ1; + +typedef struct +{ + u_int8_t ansi_apr_ver:3; + u_int8_t ecma_ver:3; + u_int8_t iso_ver:2; +} ASC_SCSI_INQ2; + +typedef struct +{ + u_int8_t rsp_data_fmt:4; + u_int8_t res:2; + u_int8_t TemIOP:1; + u_int8_t aenc:1; +} ASC_SCSI_INQ3; + +typedef struct +{ + u_int8_t StfRe:1; + u_int8_t CmdQue:1; + u_int8_t Reserved:1; + u_int8_t Linked:1; + u_int8_t Sync:1; + u_int8_t WBus16:1; + u_int8_t WBus32:1; + u_int8_t RelAdr:1; +} ASC_SCSI_INQ7; + +typedef struct +{ + ASC_SCSI_INQ0 byte0; + ASC_SCSI_INQ1 byte1; + ASC_SCSI_INQ2 byte2; + ASC_SCSI_INQ3 byte3; + u_int8_t add_len; + u_int8_t res1; + u_int8_t res2; + ASC_SCSI_INQ7 byte7; + u_int8_t vendor_id[8]; + u_int8_t product_id[16]; + u_int8_t product_rev_level[4]; +} ASC_SCSI_INQUIRY; + + +#define ASC_MAX_SENSE_LEN 32 +#define ASC_MIN_SENSE_LEN 14 + +typedef struct asc_req_sense { + u_int8_t err_code:7; + u_int8_t info_valid:1; + u_int8_t segment_no; + u_int8_t sense_key:4; + u_int8_t reserved_bit:1; + u_int8_t sense_ILI:1; + u_int8_t sense_EOM:1; + u_int8_t file_mark:1; + u_int8_t info1[4]; + u_int8_t add_sense_len; + u_int8_t cmd_sp_info[4]; + u_int8_t asc; + u_int8_t ascq; + u_int8_t fruc; + u_int8_t sks_byte0:7; + u_int8_t sks_valid:1; + u_int8_t sks_bytes[2]; + u_int8_t notused[2]; + u_int8_t ex_sense_code; + u_int8_t info2[4]; +} ASC_REQ_SENSE; + + +/* + * Adv Library functions available to drivers. + */ + +int AdvInitFromEEP __P((ADW_SOFTC *)); +int AdvExeScsiQueue __P((ADW_SOFTC *, ADW_SCSI_REQ_Q *)); +int AdvISR __P((ADW_SOFTC *)); +int AdvSendIdleCmd __P((ADW_SOFTC *, u_int16_t, u_int32_t, int)); +int AdvInitGetConfig __P((ADW_SOFTC *)); +int AdvInitAsc3550Driver __P((ADW_SOFTC *)); +void AdvResetChip __P((bus_space_tag_t, bus_space_handle_t)); +void AdvResetSCSIBus __P((ADW_SOFTC *)); +int AdvResetCCB __P((ADW_SOFTC *)); + +#endif /* _ADVANSYS_WIDE_LIBRARY_H_ */ diff --git a/sys/dev/ic/adwmcode.c b/sys/dev/ic/adwmcode.c new file mode 100644 index 000000000000..100628dcd91c --- /dev/null +++ b/sys/dev/ic/adwmcode.c @@ -0,0 +1,360 @@ +/* $NetBSD: adwmcode.c,v 1.1 1998/09/26 16:10:42 dante Exp $ */ + +/* + * Generic driver definitions and exported functions for the Advanced + * Systems Inc. SCSI controllers + * + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Baldassare Dante Profeta + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +/* + * Ported from: + */ +/* + * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters + * + * Copyright (c) 1995-1998 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 + + +/* + * This is the uCode for the Wide board RISC cpu. + * This code is loaded into Lram during initializzation procedure. + */ +u_int8_t adv_mcode[] = { + 0x9C, 0xF0, 0x80, 0x01, 0x00, 0xF0, 0x44, 0x0A, 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, 0x72, 0x01, 0xD6, 0x11, 0x00, 0x00, 0x70, 0x01, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x10, 0x2D, 0x03, 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, 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, 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, + 0x48, 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, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 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, 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, 0x78, 0x56, 0x34, 0x12, + 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, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0xF7, 0x70, 0x01, 0x0C, 0x1C, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF2, 0xD6, 0x0A, + 0x04, 0xF7, 0x70, 0x01, 0x06, 0xF7, 0x02, 0x00, 0x3E, 0x57, 0x3C, 0x56, 0x0C, 0x1C, 0x00, 0xFC, + 0xA6, 0x00, 0x01, 0x58, 0xAA, 0x13, 0x20, 0xF0, 0xA6, 0x03, 0x06, 0xEC, 0xB9, 0x00, 0x0E, 0x47, + 0x03, 0xE6, 0x10, 0x00, 0xCE, 0x45, 0x02, 0x13, 0x3E, 0x57, 0x06, 0xEA, 0xB9, 0x00, 0x47, 0x4B, + 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x4E, 0x12, 0x03, 0xF6, 0xC0, 0x00, + 0x00, 0xF2, 0x68, 0x0A, 0x41, 0x58, 0x03, 0xF6, 0xD0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x49, 0x44, + 0x59, 0xF0, 0x0A, 0x02, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x44, 0x58, 0x00, 0xF2, + 0xE2, 0x0D, 0x02, 0xCC, 0x4A, 0xE4, 0x01, 0x00, 0x55, 0xF0, 0x08, 0x03, 0x45, 0xF4, 0x02, 0x00, + 0x83, 0x5A, 0x04, 0xCC, 0x01, 0x4A, 0x12, 0x12, 0x00, 0xF2, 0xE2, 0x0D, 0x00, 0xCD, 0x48, 0xE4, + 0x01, 0x00, 0xE9, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0xFA, 0x10, 0x0E, 0x47, 0x03, 0xE6, 0x10, 0x00, + 0xCE, 0x45, 0x02, 0x13, 0x3E, 0x57, 0xCE, 0x47, 0x97, 0x13, 0x04, 0xEC, 0xB4, 0x00, 0x00, 0xF2, + 0xE2, 0x0D, 0x00, 0xCD, 0x48, 0xE4, 0x00, 0x00, 0x12, 0x12, 0x3E, 0x57, 0x06, 0xCC, 0x45, 0xF4, + 0x02, 0x00, 0x83, 0x5A, 0x00, 0xCC, 0x00, 0xEA, 0xB4, 0x00, 0x92, 0x10, 0x00, 0xF0, 0x8C, 0x01, + 0x43, 0xF0, 0x5C, 0x02, 0x44, 0xF0, 0x60, 0x02, 0x45, 0xF0, 0x64, 0x02, 0x46, 0xF0, 0x68, 0x02, + 0x47, 0xF0, 0x6E, 0x02, 0x48, 0xF0, 0x9E, 0x02, 0xB9, 0x54, 0x62, 0x10, 0x00, 0x1C, 0x5A, 0x10, + 0x02, 0x1C, 0x56, 0x10, 0x1E, 0x1C, 0x52, 0x10, 0x00, 0xF2, 0x1E, 0x11, 0x50, 0x10, 0x06, 0xFC, + 0xA8, 0x00, 0x03, 0xF6, 0xBE, 0x00, 0x00, 0xF2, 0x4E, 0x0A, 0x8C, 0x10, 0x01, 0xF6, 0x01, 0x00, + 0x01, 0xFA, 0xA8, 0x00, 0x00, 0xF2, 0x2C, 0x0B, 0x06, 0x10, 0xB9, 0x54, 0x01, 0xFA, 0xA8, 0x00, + 0x03, 0xF6, 0xBE, 0x00, 0x00, 0xF2, 0x58, 0x0A, 0x01, 0xFC, 0xA8, 0x00, 0x20, 0x10, 0x58, 0x1C, + 0x00, 0xF2, 0x1C, 0x0B, 0x5A, 0x1C, 0x01, 0xF6, 0x01, 0x00, 0x38, 0x54, 0x00, 0xFA, 0xA6, 0x00, + 0x01, 0xFA, 0xA8, 0x00, 0x20, 0x1C, 0x00, 0xF0, 0x72, 0x01, 0x01, 0xF6, 0x01, 0x00, 0x38, 0x54, + 0x00, 0xFA, 0xA6, 0x00, 0x01, 0xFA, 0xA8, 0x00, 0x20, 0x1C, 0x00, 0xF0, 0x80, 0x01, 0x03, 0xF6, + 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x0A, 0x13, 0x00, 0xF2, 0x38, 0x10, 0x00, 0xF2, + 0x54, 0x0F, 0x24, 0x10, 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x02, 0xF6, 0xD0, 0x00, + 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x49, 0x44, 0x5B, 0xF0, 0x04, 0x03, 0x00, 0xF2, 0x9C, 0x0F, + 0x00, 0xF0, 0x80, 0x01, 0x00, 0xF2, 0x14, 0x10, 0x0C, 0x1C, 0x02, 0x4B, 0xBF, 0x57, 0x9E, 0x43, + 0x77, 0x57, 0x07, 0x4B, 0x20, 0xF0, 0xA6, 0x03, 0x40, 0x1C, 0x1E, 0xF0, 0x30, 0x03, 0x26, 0xF0, + 0x2C, 0x03, 0xA0, 0xF0, 0x1A, 0x03, 0x11, 0xF0, 0xA6, 0x03, 0x12, 0x10, 0x9F, 0xF0, 0x3E, 0x03, + 0x46, 0x1C, 0x82, 0xE7, 0x05, 0x00, 0x9E, 0xE7, 0x11, 0x00, 0x00, 0xF0, 0x06, 0x0A, 0x0C, 0x1C, + 0x48, 0x1C, 0x46, 0x1C, 0x38, 0x54, 0x00, 0xEC, 0xBA, 0x00, 0x08, 0x44, 0x00, 0xEA, 0xBA, 0x00, + 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x08, 0x44, 0x00, 0x4C, 0x82, 0xE7, 0x02, 0x00, + 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x70, 0x03, 0x00, 0xF2, 0x60, 0x0B, + 0x06, 0xF0, 0x80, 0x03, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A, + 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0x55, 0xF0, 0xAC, 0x04, 0x01, 0xE6, 0x0C, 0x00, 0x00, 0xF2, + 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x01, 0xF0, + 0x7C, 0x02, 0x00, 0xF0, 0x8A, 0x02, 0x46, 0x1C, 0x0C, 0x1C, 0x67, 0x1B, 0xBF, 0x57, 0x77, 0x57, + 0x02, 0x4B, 0x48, 0x1C, 0x32, 0x1C, 0x00, 0xF2, 0x92, 0x0D, 0x30, 0x1C, 0x96, 0xF0, 0xBC, 0x03, + 0xB1, 0xF0, 0xC0, 0x03, 0x1E, 0xF0, 0xFC, 0x09, 0x85, 0xF0, 0x02, 0x0A, 0x00, 0xFC, 0xBE, 0x00, + 0x98, 0x57, 0x14, 0x12, 0x01, 0xE6, 0x0C, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, + 0x01, 0xF0, 0x7C, 0x02, 0x00, 0xF0, 0x8A, 0x02, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, + 0x01, 0x48, 0x55, 0xF0, 0x98, 0x04, 0x03, 0x82, 0x03, 0xFC, 0xA0, 0x00, 0x9B, 0x57, 0x40, 0x12, + 0x69, 0x18, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x42, 0x04, 0x69, 0x08, 0x00, 0xF2, 0x12, 0x11, + 0x85, 0xF0, 0x02, 0x0A, 0x68, 0x08, 0x4C, 0x44, 0x28, 0x12, 0x44, 0x48, 0x03, 0xF6, 0xE0, 0x00, + 0x00, 0xF2, 0x68, 0x0A, 0x45, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xCC, 0x01, 0x48, 0x55, 0xF0, + 0x98, 0x04, 0x4C, 0x44, 0xEF, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, 0x14, 0x10, 0x08, 0x10, + 0x68, 0x18, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0x04, 0x80, 0x18, 0xE4, 0x10, 0x00, 0x28, 0x12, + 0x01, 0xE6, 0x06, 0x00, 0x04, 0x80, 0x18, 0xE4, 0x01, 0x00, 0x04, 0x12, 0x01, 0xE6, 0x0D, 0x00, + 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x04, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00, + 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x69, 0x08, 0x05, 0x80, 0x48, 0xE4, 0x00, 0x00, + 0x0C, 0x12, 0x00, 0xE6, 0x11, 0x00, 0x00, 0xEA, 0xB8, 0x00, 0x00, 0xF2, 0xB6, 0x10, 0x82, 0xE7, + 0x02, 0x00, 0x1C, 0x90, 0x40, 0x5C, 0x00, 0x16, 0x01, 0xE6, 0x06, 0x00, 0x00, 0xF2, 0x4E, 0x0D, + 0x01, 0xF0, 0x80, 0x01, 0x1E, 0xF0, 0x80, 0x01, 0x00, 0xF0, 0xA0, 0x04, 0x42, 0x5B, 0x06, 0xF7, + 0x03, 0x00, 0x46, 0x59, 0xBF, 0x57, 0x77, 0x57, 0x01, 0xE6, 0x80, 0x00, 0x07, 0x80, 0x31, 0x44, + 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x56, 0x13, 0x20, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x4E, 0x12, + 0x00, 0xFC, 0xA2, 0x00, 0x98, 0x57, 0x55, 0xF0, 0x1C, 0x05, 0x31, 0xE4, 0x40, 0x00, 0x00, 0xFC, + 0xA0, 0x00, 0x98, 0x57, 0x36, 0x12, 0x4C, 0x1C, 0x00, 0xF2, 0x12, 0x11, 0x89, 0x48, 0x00, 0xF2, + 0x12, 0x11, 0x86, 0xF0, 0x2E, 0x05, 0x82, 0xE7, 0x06, 0x00, 0x1B, 0x80, 0x48, 0xE4, 0x22, 0x00, + 0x5B, 0xF0, 0x0C, 0x05, 0x48, 0xE4, 0x20, 0x00, 0x59, 0xF0, 0x10, 0x05, 0x00, 0xE6, 0x20, 0x00, + 0x09, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0x2E, 0x05, 0x83, 0x80, 0x04, 0x10, 0x00, 0xF2, + 0xA2, 0x0D, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xEA, 0x26, 0x01, 0x01, 0xEA, 0x27, 0x01, 0x04, 0x80, + 0x18, 0xE4, 0x10, 0x00, 0x36, 0x12, 0xB9, 0x54, 0x00, 0xF2, 0xF6, 0x0E, 0x01, 0xE6, 0x06, 0x00, + 0x04, 0x80, 0x18, 0xE4, 0x01, 0x00, 0x04, 0x12, 0x01, 0xE6, 0x0D, 0x00, 0x00, 0xF2, 0x4E, 0x0D, + 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x04, 0xE6, 0x02, 0x00, + 0x9E, 0xE7, 0x15, 0x00, 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0xFC, 0x20, 0x01, + 0x98, 0x57, 0x34, 0x12, 0x00, 0xFC, 0x24, 0x01, 0x98, 0x57, 0x2C, 0x13, 0xB9, 0x54, 0x00, 0xF2, + 0xF6, 0x0E, 0x86, 0xF0, 0xA8, 0x05, 0x03, 0xF6, 0x01, 0x00, 0x00, 0xF2, 0x8C, 0x0E, 0x85, 0xF0, + 0x9E, 0x05, 0x82, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x82, 0xE7, 0x02, 0x00, 0x00, 0xFC, + 0x24, 0x01, 0xB0, 0x57, 0x00, 0xFA, 0x24, 0x01, 0x00, 0xFC, 0x9E, 0x00, 0x98, 0x57, 0x5A, 0x12, + 0x00, 0xFC, 0xB6, 0x00, 0x98, 0x57, 0x52, 0x13, 0x03, 0xE6, 0x0C, 0x00, 0x00, 0xFC, 0x9C, 0x00, + 0x98, 0x57, 0x04, 0x13, 0x03, 0xE6, 0x19, 0x00, 0x05, 0xE6, 0x08, 0x00, 0x00, 0xF6, 0x00, 0x01, + 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, 0x00, 0x80, 0x04, 0x13, 0x05, 0xE6, + 0x0F, 0x00, 0xB9, 0x54, 0x00, 0xF2, 0xF6, 0x0E, 0x86, 0xF0, 0x0A, 0x06, 0x00, 0xF2, 0xBA, 0x0E, + 0x85, 0xF0, 0x00, 0x06, 0x82, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x82, 0xE7, 0x02, 0x00, + 0x00, 0xFC, 0xB6, 0x00, 0xB0, 0x57, 0x00, 0xFA, 0xB6, 0x00, 0x01, 0xF6, 0x01, 0x00, 0x00, 0xF2, + 0xF6, 0x0E, 0x9C, 0x32, 0x4E, 0x1C, 0x32, 0x1C, 0x00, 0xF2, 0x92, 0x0D, 0x30, 0x1C, 0x82, 0xE7, + 0x04, 0x00, 0xB1, 0xF0, 0x22, 0x06, 0x0A, 0xF0, 0x3E, 0x06, 0x05, 0xF0, 0xD6, 0x06, 0x06, 0xF0, + 0xDC, 0x06, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A, 0x04, 0x80, + 0x18, 0xE4, 0x20, 0x00, 0x30, 0x12, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x21, 0x80, + 0x18, 0xE4, 0xE0, 0x00, 0x09, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, + 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x99, 0xA4, 0x00, 0xF2, 0x12, 0x11, + 0x09, 0xE7, 0x00, 0x00, 0x9A, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x34, 0x12, 0x09, 0xE7, + 0x1B, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x21, 0x80, 0x18, 0xE4, 0xE0, 0x00, 0x09, 0x48, 0x00, 0xF2, + 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, + 0x12, 0x11, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF0, + 0x0C, 0x09, 0xBB, 0x55, 0x9A, 0x81, 0x03, 0xF7, 0x20, 0x00, 0x09, 0x6F, 0x93, 0x45, 0x55, 0xF0, + 0xE2, 0x06, 0xB1, 0xF0, 0xC2, 0x06, 0x0A, 0xF0, 0xBA, 0x06, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, + 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0xF2, 0x60, 0x0B, 0x47, 0x10, 0x09, 0xE7, 0x08, 0x00, + 0x41, 0x10, 0x05, 0x80, 0x48, 0xE4, 0x00, 0x00, 0x1E, 0x12, 0x00, 0xE6, 0x11, 0x00, 0x00, 0xEA, + 0xB8, 0x00, 0x00, 0xF2, 0xB6, 0x10, 0x2C, 0x90, 0xAE, 0x90, 0x08, 0x50, 0x8A, 0x50, 0x38, 0x54, + 0x1F, 0x40, 0x00, 0xF2, 0xB4, 0x0D, 0x08, 0x10, 0x08, 0x90, 0x8A, 0x90, 0x30, 0x50, 0xB2, 0x50, + 0x9C, 0x32, 0x0C, 0x92, 0x8E, 0x92, 0x38, 0x54, 0x04, 0x80, 0x30, 0xE4, 0x08, 0x00, 0x04, 0x40, + 0x0C, 0x1C, 0x00, 0xF6, 0x03, 0x00, 0xB1, 0xF0, 0x26, 0x07, 0x9E, 0xF0, 0x3A, 0x07, 0x01, 0x48, + 0x55, 0xF0, 0xFC, 0x09, 0x0C, 0x1C, 0x10, 0x44, 0xED, 0x10, 0x0B, 0xF0, 0x5E, 0x07, 0x0C, 0xF0, + 0x62, 0x07, 0x05, 0xF0, 0x52, 0x07, 0x06, 0xF0, 0x58, 0x07, 0x09, 0xF0, 0x24, 0x09, 0x00, 0xF0, + 0x02, 0x0A, 0x00, 0xF2, 0x60, 0x0B, 0xCF, 0x10, 0x09, 0xE7, 0x08, 0x00, 0xC9, 0x10, 0x2E, 0x1C, + 0x02, 0x10, 0x2C, 0x1C, 0xAA, 0xF0, 0x64, 0x07, 0xAC, 0xF0, 0x72, 0x07, 0x40, 0x10, 0x34, 0x1C, + 0xF3, 0x10, 0xAD, 0xF0, 0x7C, 0x07, 0xC8, 0x10, 0x36, 0x1C, 0xE9, 0x10, 0x2B, 0xF0, 0x82, 0x08, + 0x6B, 0x18, 0x18, 0xF4, 0x00, 0xFE, 0x20, 0x12, 0x01, 0x58, 0xD2, 0xF0, 0x82, 0x08, 0x76, 0x18, + 0x18, 0xF4, 0x03, 0x00, 0xEC, 0x12, 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0xE2, 0x12, + 0x0B, 0xF0, 0x64, 0x07, 0x0C, 0xF0, 0x64, 0x07, 0x36, 0x1C, 0x34, 0x1C, 0xB7, 0x10, 0x38, 0x54, + 0xB9, 0x54, 0x84, 0x80, 0x19, 0xE4, 0x20, 0x00, 0xB2, 0x13, 0x85, 0x80, 0x81, 0x48, 0x66, 0x12, + 0x04, 0x80, 0x18, 0xE4, 0x08, 0x00, 0x58, 0x13, 0x1F, 0x80, 0x08, 0x44, 0xC8, 0x44, 0x9F, 0x12, + 0x1F, 0x40, 0x34, 0x91, 0xB6, 0x91, 0x44, 0x55, 0xE5, 0x55, 0x02, 0xEC, 0xB8, 0x00, 0x02, 0x49, + 0xBB, 0x55, 0x82, 0x81, 0xC0, 0x55, 0x48, 0xF4, 0x0F, 0x00, 0x5A, 0xF0, 0x1A, 0x08, 0x4A, 0xE4, + 0x17, 0x00, 0xD5, 0xF0, 0xFA, 0x07, 0x02, 0xF6, 0x0F, 0x00, 0x02, 0xF4, 0x02, 0x00, 0x02, 0xEA, + 0xB8, 0x00, 0x04, 0x91, 0x86, 0x91, 0x02, 0x4B, 0x2C, 0x90, 0x08, 0x50, 0x2E, 0x90, 0x0A, 0x50, + 0x2C, 0x51, 0xAE, 0x51, 0x00, 0xF2, 0xB6, 0x10, 0x38, 0x54, 0x00, 0xF2, 0xB4, 0x0D, 0x56, 0x10, + 0x34, 0x91, 0xB6, 0x91, 0x0C, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x08, 0x00, 0x41, 0x12, 0x0C, 0x91, + 0x8E, 0x91, 0x04, 0x80, 0x18, 0xE4, 0xF7, 0x00, 0x04, 0x40, 0x30, 0x90, 0xB2, 0x90, 0x36, 0x10, + 0x02, 0x80, 0x48, 0xE4, 0x10, 0x00, 0x31, 0x12, 0x82, 0xE7, 0x10, 0x00, 0x84, 0x80, 0x19, 0xE4, + 0x20, 0x00, 0x10, 0x13, 0x0C, 0x90, 0x8E, 0x90, 0x5D, 0xF0, 0x78, 0x07, 0x0C, 0x58, 0x8D, 0x58, + 0x00, 0xF0, 0x64, 0x07, 0x38, 0x54, 0xB9, 0x54, 0x19, 0x80, 0xF1, 0x10, 0x3A, 0x55, 0x19, 0x81, + 0xBB, 0x55, 0x10, 0x90, 0x92, 0x90, 0x10, 0x58, 0x91, 0x58, 0x14, 0x59, 0x95, 0x59, 0x00, 0xF0, + 0x64, 0x07, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x06, 0x12, 0x6C, 0x19, 0x19, 0x41, 0x7C, 0x10, + 0x6C, 0x19, 0x0C, 0x51, 0xED, 0x19, 0x8E, 0x51, 0x6B, 0x18, 0x18, 0xF4, 0x00, 0xFF, 0x02, 0x13, + 0x6A, 0x10, 0x01, 0x58, 0xD2, 0xF0, 0xC0, 0x08, 0x76, 0x18, 0x18, 0xF4, 0x03, 0x00, 0x0A, 0x12, + 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0x06, 0x13, 0x9E, 0xE7, 0x16, 0x00, 0x4C, 0x10, + 0xD1, 0xF0, 0xCA, 0x08, 0x9E, 0xE7, 0x17, 0x00, 0x42, 0x10, 0xD0, 0xF0, 0xD4, 0x08, 0x9E, 0xE7, + 0x19, 0x00, 0x38, 0x10, 0xCF, 0xF0, 0xDE, 0x08, 0x9E, 0xE7, 0x20, 0x00, 0x2E, 0x10, 0xCE, 0xF0, + 0xE8, 0x08, 0x9E, 0xE7, 0x21, 0x00, 0x24, 0x10, 0xCD, 0xF0, 0xF2, 0x08, 0x9E, 0xE7, 0x22, 0x00, + 0x1A, 0x10, 0xCC, 0xF0, 0x04, 0x09, 0x84, 0x80, 0x19, 0xE4, 0x04, 0x00, 0x06, 0x12, 0x9E, 0xE7, + 0x12, 0x00, 0x08, 0x10, 0xCB, 0xF0, 0x0C, 0x09, 0x9E, 0xE7, 0x24, 0x00, 0xB1, 0xF0, 0x0C, 0x09, + 0x05, 0xF0, 0x1E, 0x09, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0xE4, 0x10, 0x00, 0xF2, + 0x60, 0x0B, 0xE9, 0x10, 0x9C, 0x32, 0x82, 0xE7, 0x20, 0x00, 0x32, 0x1C, 0xE9, 0x09, 0x00, 0xF2, + 0x12, 0x11, 0x85, 0xF0, 0x02, 0x0A, 0x69, 0x08, 0x01, 0xF0, 0x44, 0x09, 0x1E, 0xF0, 0xFC, 0x09, + 0x00, 0xF0, 0x38, 0x09, 0x30, 0x44, 0x06, 0x12, 0x9E, 0xE7, 0x42, 0x00, 0xB8, 0x10, 0x04, 0xF6, + 0x01, 0x00, 0xB3, 0x45, 0x74, 0x12, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x22, 0x13, 0x4B, 0xE4, + 0x02, 0x00, 0x36, 0x12, 0x4B, 0xE4, 0x28, 0x00, 0xAC, 0x13, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, + 0xC8, 0x11, 0x03, 0xF6, 0xD0, 0x00, 0xFA, 0x14, 0x82, 0xE7, 0x01, 0x00, 0x00, 0xF0, 0x80, 0x01, + 0x9E, 0xE7, 0x44, 0x00, 0x4B, 0xE4, 0x02, 0x00, 0x06, 0x12, 0x03, 0xE6, 0x02, 0x00, 0x76, 0x10, + 0x00, 0xF2, 0xA2, 0x0D, 0x03, 0xE6, 0x02, 0x00, 0x6C, 0x10, 0x00, 0xF2, 0xA2, 0x0D, 0x19, 0x82, + 0x34, 0x46, 0x0A, 0x13, 0x03, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x43, 0x00, 0x68, 0x10, 0x04, 0x80, + 0x30, 0xE4, 0x20, 0x00, 0x04, 0x40, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x82, 0xE7, + 0x01, 0x00, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF0, 0x08, 0x03, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, + 0x06, 0x12, 0x03, 0xE6, 0x02, 0x00, 0x3E, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x3A, 0x12, + 0x04, 0x80, 0x18, 0xE4, 0xFD, 0x00, 0x04, 0x40, 0x1C, 0x1C, 0x9D, 0xF0, 0xEA, 0x09, 0x1C, 0x1C, + 0x9D, 0xF0, 0xF0, 0x09, 0xC1, 0x10, 0x9E, 0xE7, 0x13, 0x00, 0x0A, 0x10, 0x9E, 0xE7, 0x41, 0x00, + 0x04, 0x10, 0x9E, 0xE7, 0x24, 0x00, 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0xD5, 0xF0, 0x8A, 0x02, + 0x04, 0xE6, 0x04, 0x00, 0x06, 0x10, 0x04, 0xE6, 0x04, 0x00, 0x9D, 0x41, 0x1C, 0x42, 0x9F, 0xE7, + 0x00, 0x00, 0x06, 0xF7, 0x02, 0x00, 0x03, 0xF6, 0xE0, 0x00, 0x3C, 0x14, 0x44, 0x58, 0x45, 0x58, + 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xF2, 0x7E, 0x10, 0x00, 0xF2, 0xC6, 0x0F, 0x3C, 0x14, 0x1E, 0x1C, + 0x00, 0xF0, 0x80, 0x01, 0x12, 0x1C, 0x22, 0x1C, 0xD2, 0x14, 0x00, 0xF0, 0x72, 0x01, 0x83, 0x59, + 0x03, 0xDC, 0x73, 0x57, 0x80, 0x5D, 0x00, 0x16, 0x83, 0x59, 0x03, 0xDC, 0x38, 0x54, 0x70, 0x57, + 0x33, 0x54, 0x3B, 0x54, 0x80, 0x5D, 0x00, 0x16, 0x03, 0x57, 0x83, 0x59, 0x38, 0x54, 0x00, 0xCC, + 0x00, 0x16, 0x03, 0x57, 0x83, 0x59, 0x00, 0x4C, 0x00, 0x16, 0x02, 0x80, 0x48, 0xE4, 0x01, 0x00, + 0x0E, 0x12, 0x48, 0xE4, 0x05, 0x00, 0x08, 0x12, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, + 0xC1, 0x5A, 0x3A, 0x55, 0x02, 0xEC, 0xB5, 0x00, 0x45, 0x59, 0x00, 0xF2, 0xF6, 0x0D, 0x83, 0x58, + 0x30, 0xE7, 0x00, 0x00, 0x10, 0x4D, 0x30, 0xE7, 0x40, 0x00, 0x10, 0x4F, 0x38, 0x90, 0xBA, 0x90, + 0x10, 0x5C, 0x80, 0x5C, 0x83, 0x5A, 0x10, 0x4E, 0x04, 0xEA, 0xB5, 0x00, 0x43, 0x5B, 0x03, 0xF4, + 0xE0, 0x00, 0x83, 0x59, 0x04, 0xCC, 0x01, 0x4A, 0x0A, 0x12, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, + 0x00, 0xF2, 0x38, 0x10, 0x00, 0x16, 0x08, 0x1C, 0x00, 0xFC, 0xAC, 0x00, 0x06, 0x58, 0x67, 0x18, + 0x18, 0xF4, 0x8F, 0xE1, 0x01, 0xFC, 0xAE, 0x00, 0x19, 0xF4, 0x70, 0x1E, 0xB0, 0x54, 0x07, 0x58, + 0x00, 0xFC, 0xB0, 0x00, 0x08, 0x58, 0x00, 0xFC, 0xB2, 0x00, 0x09, 0x58, 0x0A, 0x1C, 0x00, 0xE6, + 0x0F, 0x00, 0x00, 0xEA, 0xB9, 0x00, 0x38, 0x54, 0x00, 0xFA, 0x24, 0x01, 0x00, 0xFA, 0xB6, 0x00, + 0x18, 0x1C, 0x14, 0x1C, 0x10, 0x1C, 0x32, 0x1C, 0x12, 0x1C, 0x00, 0x16, 0x3E, 0x57, 0x0C, 0x14, + 0x0E, 0x47, 0x07, 0xE6, 0x10, 0x00, 0xCE, 0x47, 0xF5, 0x13, 0x00, 0x16, 0x00, 0xF2, 0xA2, 0x0D, + 0x02, 0x4B, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x20, 0x12, 0x44, 0x58, + 0x45, 0x58, 0x9E, 0xE7, 0x15, 0x00, 0x9C, 0xE7, 0x04, 0x00, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xF2, + 0x7E, 0x10, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, 0x7A, 0x0A, 0x1E, 0x1C, 0xD5, 0x10, 0x00, 0x16, + 0x69, 0x08, 0x48, 0xE4, 0x04, 0x00, 0x64, 0x12, 0x48, 0xE4, 0x02, 0x00, 0x20, 0x12, 0x48, 0xE4, + 0x03, 0x00, 0x1A, 0x12, 0x48, 0xE4, 0x08, 0x00, 0x14, 0x12, 0x48, 0xE4, 0x01, 0x00, 0xF0, 0x12, + 0x48, 0xE4, 0x07, 0x00, 0x12, 0x12, 0x01, 0xE6, 0x07, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, + 0x12, 0x11, 0x05, 0xF0, 0x60, 0x0B, 0x00, 0x16, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xEA, 0x99, 0x00, + 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0xE7, 0x12, 0x48, 0xE4, 0x06, 0x00, 0xE1, 0x12, 0x01, 0xE6, + 0x06, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x04, 0xE6, 0x02, 0x00, 0x9E, 0xE7, + 0x15, 0x00, 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0x16, 0x02, 0x80, 0x48, 0xE4, + 0x10, 0x00, 0x1C, 0x12, 0x82, 0xE7, 0x08, 0x00, 0x3C, 0x56, 0x03, 0x82, 0x00, 0xF2, 0xE2, 0x0D, + 0x30, 0xE7, 0x08, 0x00, 0x04, 0xF7, 0x70, 0x01, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF0, 0x80, 0x01, + 0x6C, 0x19, 0xED, 0x19, 0x5D, 0xF0, 0xD4, 0x0B, 0x44, 0x55, 0xE5, 0x55, 0x59, 0xF0, 0x52, 0x0C, + 0x04, 0x55, 0xA5, 0x55, 0x1F, 0x80, 0x01, 0xEC, 0xB8, 0x00, 0x82, 0x48, 0x82, 0x80, 0x49, 0x44, + 0x2E, 0x13, 0x01, 0xEC, 0xB8, 0x00, 0x41, 0xE4, 0x02, 0x00, 0x01, 0xEA, 0xB8, 0x00, 0x49, 0xE4, + 0x11, 0x00, 0x59, 0xF0, 0x2E, 0x0C, 0x01, 0xE6, 0x17, 0x00, 0x01, 0xEA, 0xB8, 0x00, 0x02, 0x4B, + 0x88, 0x90, 0xAC, 0x50, 0x8A, 0x90, 0xAE, 0x50, 0x01, 0xEC, 0xB8, 0x00, 0x82, 0x48, 0x82, 0x80, + 0x10, 0x44, 0x02, 0x4B, 0x1F, 0x40, 0xC0, 0x44, 0x00, 0xF2, 0xB4, 0x0D, 0x04, 0x55, 0xA5, 0x55, + 0x9F, 0x10, 0x0C, 0x51, 0x8E, 0x51, 0x30, 0x90, 0xB2, 0x90, 0x00, 0x56, 0xA1, 0x56, 0x30, 0x50, + 0xB2, 0x50, 0x34, 0x90, 0xB6, 0x90, 0x40, 0x56, 0xE1, 0x56, 0x34, 0x50, 0xB6, 0x50, 0x65, 0x10, + 0xB1, 0xF0, 0x70, 0x0C, 0x85, 0xF0, 0xCA, 0x0B, 0xE9, 0x09, 0x4B, 0xE4, 0x03, 0x00, 0x78, 0x12, + 0x4B, 0xE4, 0x02, 0x00, 0x01, 0x13, 0xB1, 0xF0, 0x86, 0x0C, 0x85, 0xF0, 0xCA, 0x0B, 0x69, 0x08, + 0x48, 0xE4, 0x03, 0x00, 0xD5, 0xF0, 0x86, 0x0B, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0xCA, 0x0B, + 0xE8, 0x09, 0x3C, 0x56, 0x00, 0xFC, 0x20, 0x01, 0x98, 0x57, 0x02, 0x13, 0xBB, 0x45, 0x4B, 0xE4, + 0x00, 0x00, 0x08, 0x12, 0x03, 0xE6, 0x01, 0x00, 0x04, 0xF6, 0x00, 0x80, 0xA8, 0x14, 0xD2, 0x14, + 0x30, 0x1C, 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x10, 0x13, 0x00, 0xFC, 0xB6, 0x00, 0x98, 0x57, + 0x02, 0x13, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF0, 0x8E, 0x0B, 0x00, 0xFC, 0x24, 0x01, 0xB0, 0x57, + 0x00, 0xFA, 0x24, 0x01, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0x8E, 0x0B, + 0x00, 0xF2, 0x8C, 0x0E, 0x00, 0xF0, 0x8E, 0x0B, 0xB1, 0xF0, 0xF8, 0x0C, 0x85, 0xF0, 0x86, 0x0B, + 0x69, 0x08, 0x48, 0xE4, 0x01, 0x00, 0xD5, 0xF0, 0x86, 0x0B, 0xFC, 0x14, 0x42, 0x58, 0x6C, 0x14, + 0x80, 0x14, 0x30, 0x1C, 0x4A, 0xF4, 0x02, 0x00, 0x55, 0xF0, 0x86, 0x0B, 0x4A, 0xF4, 0x01, 0x00, + 0x0E, 0x12, 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x06, 0x13, 0x3E, 0x1C, 0x00, 0xF0, 0x8E, 0x0B, + 0x00, 0xFC, 0xB6, 0x00, 0xB0, 0x57, 0x00, 0xFA, 0xB6, 0x00, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF2, + 0x12, 0x11, 0x86, 0xF0, 0x8E, 0x0B, 0x00, 0xF2, 0xBA, 0x0E, 0x00, 0xF0, 0x8E, 0x0B, 0x4C, 0x1C, + 0xB1, 0xF0, 0x50, 0x0D, 0x85, 0xF0, 0x5C, 0x0D, 0x69, 0x08, 0xF3, 0x10, 0x86, 0xF0, 0x64, 0x0D, + 0x4E, 0x1C, 0x89, 0x48, 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, + 0x00, 0xDC, 0x18, 0xF4, 0xFF, 0x7F, 0x30, 0x56, 0x00, 0x5C, 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, + 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, 0x00, 0x80, 0x30, 0x56, 0x00, 0x5C, + 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x0B, 0x58, + 0x00, 0x16, 0x03, 0xF6, 0x24, 0x01, 0x00, 0xF2, 0x58, 0x0A, 0x03, 0xF6, 0xB6, 0x00, 0x00, 0xF2, + 0x58, 0x0A, 0x00, 0x16, 0x02, 0xEC, 0xB8, 0x00, 0x02, 0x49, 0x18, 0xF4, 0xFF, 0x00, 0x00, 0x54, + 0x00, 0x54, 0x00, 0x54, 0x00, 0xF4, 0x08, 0x00, 0xE1, 0x18, 0x80, 0x54, 0x03, 0x58, 0x00, 0xDD, + 0x01, 0xDD, 0x02, 0xDD, 0x03, 0xDC, 0x02, 0x4B, 0x30, 0x50, 0xB2, 0x50, 0x34, 0x51, 0xB6, 0x51, + 0x00, 0x16, 0x45, 0x5A, 0x1D, 0xF4, 0xFF, 0x00, 0x85, 0x56, 0x85, 0x56, 0x85, 0x56, 0x05, 0xF4, + 0x02, 0x12, 0x83, 0x5A, 0x00, 0x16, 0x1D, 0xF4, 0xFF, 0x00, 0x85, 0x56, 0x85, 0x56, 0x85, 0x56, + 0x05, 0xF4, 0x00, 0x12, 0x83, 0x5A, 0x00, 0x16, 0x38, 0x54, 0xBB, 0x55, 0x3C, 0x56, 0xBD, 0x56, + 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x82, 0x0E, 0xE9, 0x09, 0xC1, 0x59, 0x00, 0xF2, 0x12, 0x11, + 0x85, 0xF0, 0x82, 0x0E, 0xE8, 0x0A, 0x83, 0x55, 0x83, 0x55, 0x4B, 0xF4, 0x90, 0x01, 0x5C, 0xF0, + 0x36, 0x0E, 0xBD, 0x56, 0x40, 0x10, 0x4B, 0xF4, 0x30, 0x00, 0x59, 0xF0, 0x48, 0x0E, 0x01, 0xF6, + 0x0C, 0x00, 0x00, 0xF6, 0x01, 0x00, 0x2E, 0x10, 0x02, 0xFC, 0x9C, 0x00, 0x9A, 0x57, 0x14, 0x13, + 0x4B, 0xF4, 0x64, 0x00, 0x59, 0xF0, 0x64, 0x0E, 0x03, 0xF6, 0x64, 0x00, 0x01, 0xF6, 0x19, 0x00, + 0x00, 0xF6, 0x01, 0x00, 0x43, 0xF4, 0x33, 0x00, 0x56, 0xF0, 0x76, 0x0E, 0x04, 0xF4, 0x00, 0x01, + 0x43, 0xF4, 0x19, 0x00, 0xF3, 0x10, 0xB4, 0x56, 0xC3, 0x58, 0x02, 0xFC, 0x9E, 0x00, 0x9A, 0x57, + 0x08, 0x13, 0x3C, 0x56, 0x00, 0xF6, 0x02, 0x00, 0x00, 0x16, 0x00, 0x16, 0x09, 0xE7, 0x01, 0x00, + 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xB8, 0x0E, 0x09, 0xE7, 0x02, 0x00, 0x00, 0xF2, 0x12, 0x11, + 0x86, 0xF0, 0xB8, 0x0E, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xB8, 0x0E, + 0x4E, 0x1C, 0x89, 0x49, 0x00, 0xF2, 0x12, 0x11, 0x00, 0x16, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, + 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, + 0xF2, 0x0E, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x89, 0x49, + 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x4E, 0x1C, 0x89, 0x4A, 0x00, 0xF2, 0x12, 0x11, + 0x00, 0x16, 0x3C, 0x56, 0x00, 0x16, 0x00, 0xEC, 0x26, 0x01, 0x48, 0xE4, 0x01, 0x00, 0x1E, 0x13, + 0x38, 0x44, 0x00, 0xEA, 0x26, 0x01, 0x49, 0xF4, 0x00, 0x00, 0x04, 0x12, 0x4E, 0x1C, 0x02, 0x10, + 0x4C, 0x1C, 0x01, 0xEC, 0x27, 0x01, 0x89, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x02, 0x14, 0x00, 0x16, + 0x85, 0xF0, 0x52, 0x0F, 0x38, 0x54, 0x00, 0xEA, 0x99, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x02, 0x80, + 0x48, 0xE4, 0x06, 0x00, 0x1C, 0x13, 0x00, 0xEC, 0x99, 0x00, 0x48, 0xE4, 0x01, 0x00, 0x0A, 0x12, + 0x04, 0x80, 0x30, 0xE4, 0x01, 0x00, 0x04, 0x40, 0x08, 0x10, 0x04, 0x80, 0x18, 0xE4, 0xFE, 0x00, + 0x04, 0x40, 0x00, 0x16, 0x02, 0xF6, 0xE0, 0x00, 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x81, 0x48, + 0x22, 0x12, 0x00, 0x4E, 0x83, 0x5A, 0x90, 0x4C, 0x20, 0xE7, 0x00, 0x00, 0xC3, 0x58, 0x1B, 0xF4, + 0xFF, 0x00, 0x83, 0x55, 0x83, 0x55, 0x83, 0x55, 0x03, 0xF4, 0x00, 0x12, 0x8B, 0x55, 0x83, 0x59, + 0x00, 0x4E, 0x00, 0x16, 0x00, 0x4E, 0x02, 0xF6, 0xF0, 0x00, 0x02, 0x57, 0x03, 0x59, 0x00, 0x4E, + 0x83, 0x5A, 0x30, 0xE7, 0x00, 0x00, 0x20, 0xE7, 0x00, 0x00, 0x00, 0x16, 0x02, 0xF6, 0xF0, 0x00, + 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x00, 0x4E, 0x83, 0x5A, 0x30, 0xE7, 0x00, 0x00, 0x80, 0x4C, + 0xC3, 0x58, 0x1B, 0xF4, 0xFF, 0x00, 0x83, 0x55, 0x83, 0x55, 0x83, 0x55, 0x03, 0xF4, 0x00, 0x12, + 0x83, 0x59, 0x00, 0x4E, 0x00, 0x16, 0x03, 0xF6, 0xE0, 0x00, 0x03, 0x57, 0x83, 0x59, 0x3A, 0x55, + 0x02, 0xCC, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0xC0, 0x5A, 0x40, 0x5C, 0x38, 0x54, 0x00, 0xCD, + 0x01, 0xCC, 0x4A, 0x46, 0x0A, 0x13, 0x83, 0x59, 0x00, 0x4C, 0x01, 0x48, 0x16, 0x13, 0x0C, 0x10, + 0xC5, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0x4C, 0x01, 0x48, 0x08, 0x13, 0x05, 0xF6, 0xF0, 0x00, + 0x05, 0x57, 0x08, 0x10, 0x45, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x8D, 0x56, 0x83, 0x5A, 0x80, 0x4C, + 0x05, 0x17, 0x00, 0x16, 0x02, 0x4B, 0x06, 0xF7, 0x04, 0x00, 0x62, 0x0B, 0x03, 0x82, 0x00, 0xF2, + 0xE2, 0x0D, 0x02, 0x80, 0x00, 0x4C, 0x45, 0xF4, 0x02, 0x00, 0x52, 0x14, 0x06, 0xF7, 0x02, 0x00, + 0x06, 0x14, 0x00, 0xF2, 0x54, 0x0F, 0x00, 0x16, 0x02, 0x4B, 0x01, 0xF6, 0xFF, 0x00, 0x38, 0x1C, + 0x05, 0xF4, 0x04, 0x00, 0x83, 0x5A, 0x18, 0xDF, 0x19, 0xDF, 0x1D, 0xF7, 0x3C, 0x00, 0xB8, 0xF0, + 0x4E, 0x10, 0x9C, 0x14, 0x01, 0x48, 0x1C, 0x13, 0x0E, 0xF7, 0x3C, 0x00, 0x03, 0xF7, 0x04, 0x00, + 0xAF, 0x19, 0x03, 0x42, 0x45, 0xF4, 0x02, 0x00, 0x83, 0x5A, 0x02, 0xCC, 0x02, 0x41, 0x45, 0xF4, + 0x02, 0x00, 0x00, 0x16, 0x91, 0x44, 0xD5, 0xF0, 0x3E, 0x10, 0x00, 0xF0, 0x9E, 0x02, 0x01, 0xF6, + 0xFF, 0x00, 0x38, 0x1C, 0x05, 0xF4, 0x04, 0x00, 0x83, 0x5A, 0x18, 0xDF, 0x19, 0xDF, 0x0E, 0xF7, + 0x3C, 0x00, 0x03, 0xF7, 0x04, 0x00, 0x0F, 0x79, 0x1C, 0xF7, 0x3C, 0x00, 0xB8, 0xF0, 0x9C, 0x10, + 0x4E, 0x14, 0x01, 0x48, 0x06, 0x13, 0x45, 0xF4, 0x04, 0x00, 0x00, 0x16, 0x91, 0x44, 0xD5, 0xF0, + 0x82, 0x10, 0x00, 0xF0, 0x9E, 0x02, 0x02, 0xF6, 0xFF, 0x00, 0x38, 0x1C, 0x2C, 0xBC, 0xAE, 0xBC, + 0xE2, 0x08, 0x00, 0xEC, 0xB8, 0x00, 0x02, 0x48, 0x1D, 0xF7, 0x80, 0x00, 0xB8, 0xF0, 0xCC, 0x10, + 0x1E, 0x14, 0x01, 0x48, 0x0E, 0x13, 0x0E, 0xF7, 0x80, 0x00, 0x38, 0x54, 0x03, 0x58, 0xAF, 0x19, + 0x82, 0x48, 0x00, 0x16, 0x82, 0x48, 0x12, 0x45, 0xD5, 0xF0, 0xBA, 0x10, 0x00, 0xF0, 0x9E, 0x02, + 0x39, 0xF0, 0xF8, 0x10, 0x38, 0x44, 0x00, 0x16, 0x7E, 0x18, 0x18, 0xF4, 0x03, 0x00, 0x04, 0x13, + 0x61, 0x18, 0x00, 0x16, 0x38, 0x1C, 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0xF1, 0x12, + 0xE3, 0x10, 0x30, 0x44, 0x30, 0x44, 0x30, 0x44, 0xB1, 0xF0, 0x18, 0x11, 0x00, 0x16, 0x3E, 0x57, + 0x03, 0xF6, 0xE0, 0x00, 0x03, 0x57, 0x83, 0x59, 0x04, 0xCC, 0x01, 0x4A, 0x6A, 0x12, 0x45, 0x5A, + 0x00, 0xF2, 0xF6, 0x0D, 0x02, 0x4B, 0x70, 0x14, 0x34, 0x13, 0x02, 0x80, 0x48, 0xE4, 0x08, 0x00, + 0x18, 0x12, 0x9C, 0xE7, 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, + 0x7A, 0x0A, 0x1E, 0x1C, 0x01, 0xF6, 0x01, 0x00, 0x00, 0x16, 0x30, 0xE4, 0x10, 0x00, 0x04, 0x40, + 0x00, 0xF2, 0xE2, 0x0D, 0x20, 0xE7, 0x01, 0x00, 0x01, 0xF6, 0x01, 0x00, 0x00, 0x16, 0x04, 0xDC, + 0x01, 0x4A, 0x24, 0x12, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0x43, 0x5B, 0x06, 0xEC, 0x98, 0x00, + 0x00, 0xF2, 0x38, 0x10, 0xC6, 0x59, 0x20, 0x14, 0x0A, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, + 0x14, 0x10, 0xA7, 0x10, 0x83, 0x5A, 0xD7, 0x10, 0x0E, 0x47, 0x07, 0xE6, 0x10, 0x00, 0xCE, 0x47, + 0x5A, 0xF0, 0x20, 0x11, 0xB9, 0x54, 0x00, 0x16, 0x14, 0x90, 0x96, 0x90, 0x02, 0xFC, 0xA8, 0x00, + 0x03, 0xFC, 0xAA, 0x00, 0x48, 0x55, 0x02, 0x13, 0xC9, 0x55, 0x00, 0x16, 0x00, 0xEC, 0xBA, 0x00, + 0x10, 0x44, 0x00, 0xEA, 0xBA, 0x00, 0x00, 0x16, 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A, + 0x10, 0x44, 0x00, 0x4C, 0x00, 0x16 +}; + +u_int16_t adv_mcode_size = sizeof(adv_mcode); /* 0x11D6 */ + +/* + * This checksum is used to compare with that one that will be calculated + * at run time. + * This is performed to ensure the uCode is correctly loaded into the Lram. + */ +u_int32_t adv_mcode_chksum = 0x03494981UL; diff --git a/sys/dev/ic/adwmcode.h b/sys/dev/ic/adwmcode.h new file mode 100644 index 000000000000..a16a7354c2a5 --- /dev/null +++ b/sys/dev/ic/adwmcode.h @@ -0,0 +1,48 @@ +/* $NetBSD: adwmcode.h,v 1.1 1998/09/26 16:10:42 dante Exp $ */ + +/* + * Generic driver definitions and exported functions for the Advanced + * Systems Inc. SCSI controllers + * + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Baldassare Dante Profeta + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef ADV_MCODE_H +#define ADV_MCODE_H + +extern u_int8_t adv_mcode[]; +extern u_int16_t adv_mcode_size; +extern u_int32_t adv_mcode_chksum; + +#endif /* ADV_MCODE_H */ diff --git a/sys/dev/pci/adw_pci.c b/sys/dev/pci/adw_pci.c new file mode 100644 index 000000000000..c8a7179cb36c --- /dev/null +++ b/sys/dev/pci/adw_pci.c @@ -0,0 +1,218 @@ +/* $NetBSD: adw_pci.c,v 1.1 1998/09/26 16:09:32 dante Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. + * + * Author: Baldassare Dante Profeta + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ +/* + * Device probe and attach routines for the following + * Advanced Systems Inc. SCSI controllers: + * + * Single Channel Products: + * ABP940UW - Bus-Master PCI Ultra-Wide (240 CDB) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +/******************************************************************************/ + +#define PCI_BASEADR_IO 0x10 + +/******************************************************************************/ + +int adw_pci_match __P((struct device *, struct cfdata *, void *)); +void adw_pci_attach __P((struct device *, struct device *, void *)); + +struct cfattach adw_pci_ca = +{ + sizeof(ADW_SOFTC), adw_pci_match, adw_pci_attach +}; + +/******************************************************************************/ +/* + * Check the slots looking for a board we recognise + * If we find one, note it's address (slot) and call + * the actual probe routine to check it out. + */ +int +adw_pci_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ADVSYS) + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_ADVSYS_WIDE: + return (1); + } + + return 0; +} + + +void +adw_pci_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pci_attach_args *pa = aux; + ADW_SOFTC *sc = (void *) self; + bus_space_tag_t iot; + bus_space_handle_t ioh; + pci_intr_handle_t ih; + pci_chipset_tag_t pc = pa->pa_pc; + u_int32_t command; + const char *intrstr; + + + sc->sc_flags = 0x0; + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ADVSYS) + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_ADVSYS_WIDE: + sc->sc_flags |= ADW_WIDE_BOARD; + printf(": AdvanSys ABP-9xxUW SCSI adapter\n"); + break; + + default: + printf(": unknown model!\n"); + return; + } + + + /* + * Make sure IO/MEM/MASTER are enabled + */ + command = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + if ((command & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE)) != + (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE)) { + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + command | (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE)); + } + + /* + * Latency timer settings. + */ + { + u_int32_t bhlcr; + + bhlcr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); + + if ((PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ADVSYS_WIDE) && + (PCI_LATTIMER(bhlcr) < 0x20)) { + bhlcr &= 0xFFFF00FFul; + bhlcr |= 0x00002000ul; + pci_conf_write(pa->pa_pc, pa->pa_tag, + PCI_BHLC_REG, bhlcr); + } + } + + + if((PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ADVSYS_WIDE) && + (command & PCI_COMMAND_PARITY_ENABLE) == 0) { + sc->cfg.control_flag |= CONTROL_FLAG_IGNORE_PERR; + } + + + + /* + * Map Device Registers for I/O + */ + if (pci_mapreg_map(pa, PCI_BASEADR_IO, PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL)) { + printf("%s: unable to map device registers\n", + sc->sc_dev.dv_xname); + return; + } + sc->sc_iot = iot; + sc->sc_ioh = ioh; + sc->sc_dmat = pa->pa_dmat; + + /* + * Initialize the board + */ + if (adw_init(sc)) + panic("adw_pci_attach: adw_init failed"); + + /* + * Map Interrupt line + */ + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + + /* + * Establish Interrupt handler + */ + sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, adw_intr, sc); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt", sc->sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr); + + /* + * Attach all the sub-devices we can find + */ + adw_attach(sc); +} +/******************************************************************************/